-
-
Save UnkindPartition/5ccaeee166b45be13a8e375f980f405a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Informal introduction to MCMC sampling" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"MCMC (Markov Chain Monte Carlo) is a class of methods which allow to sample from arbitrary probability distributions of which the analytical expression is only known up to a constant (of which only the \"kernel\" is known). So as an example, suppose you want to sample from a probability density $p(x) \\propto \\mathrm{e}^{-\\frac{1}{2\\sigma^2} x^2}$ and you don't know the normalization constant (which of course is $\\sqrt{2 \\pi \\sigma^2})$. Then MCMC techniques will save you." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Markov chains" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"So what is a Markov chain? Really informally and without all the technical details, it's a random sequence of states in which the probability of being in a certain state depends only on the previous state in the chain and not on the previous history. The whole thing is memory-less. Under certain conditions, a Markov chain has a unique invariant distribution to which it will converge after a certain number of states. From that number on, states in the Markov chain will be distributed according to the invariant distribution. Now MCMC algorithms work by constructing a Markov chain with the probability distribution you want to sample from as the stationary distribution." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"The key quantity characterizing a Markov chain is the transition operator $T(x_{i+1}|x_i)$ which gives you the probability of jumping to state $x_{i+1}$ in your (for didactic reasons here discrete) state space given that the current state is $x_i$. A sufficient (but not neccessary), convenient condition for a Markov chain to have a distribution $p(x)$ as its invariant distribution is called **detailed balance**: $p(x)T(y|x) = p(y)T(x|y)$. So most MCMC algorithms just differ in the construction of $T$, but it almost always fulfills the detailed balance condition." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now just for fun, let's quickly whip up a Markov chain:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 59, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import numpy as np\n", | |
"import matplotlib.pyplot as plt\n", | |
"%matplotlib notebook" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's define our discrete state space, in our case, weather states:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 60, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"state_space = (\"sunny\", \"cloudy\", \"rainy\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"And now our transition matrix, with more or less sensible values. Columns and rows correspond to sunny, cloudy, and rainy weather:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 61, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"transition_matrix = np.array(((0.6, 0.3, 0.1),\n", | |
" (0.4, 0.5, 0.1),\n", | |
" (0.1, 0.8, 0.1)))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"The rows indicate the states the chain might currently be in and the columns the states the chains might transition to. So if it's sunny, there's a 60% chance it stays sunny, a 30% chance it gets cloudy and only a 10% chance of it raining immediately in the next step. This also means that each row has to sum up to 1." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's simulate our Markov Chain for 20000 steps:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 62, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"n_steps = 20000\n", | |
"states = [0]\n", | |
"for i in range(n_steps):\n", | |
" states.append(np.random.choice((0,1,2), p=transition_matrix[states[-1]]))\n", | |
"states = np.array(states)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's monitor the convergence of our Markov chain to its stationary distribution by calculating the empirical probability for each of the states as a function of chain length:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 83, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuydB3hVxfbFV3oBQg8dKVKULgJixS5W7AUVewW7PNSnwlOeBQtSFJ8+RVGUJmDXPyCo+ASp0ov0JggkdFL/35rLiQlpNzl3cu/JXcOXj+TemTkzv73P2evsOSUiOzs7GyoiIAIiIAIiIAIiIAJhQyBCAjBsbK2JioAIiIAIiIAIiIAhIAEoRxABERABERABERCBMCMgARhmBtd0RUAEREAEREAEREACUD4gAiIgAiIgAiIgAmFGQAIwzAyu6YqACIiACIiACIiABKB8QAREQAREQAREQATCjIAEYJgZXNMVAREQAREQAREQAQlA+YAIiIAIiIAIiIAIhBkBCcAwM7imKwIiIAIiIAIiIAISgPIBERABERABERABEQgzAhKAYWZwTVcEREAEREAEREAEJADlAyIgAiIgAiIgAiIQZgQkAMPM4JquCIiACIiACIiACEgAygdEQAREQAREQAREIMwISACGmcE1XREQAREQAREQARGQAJQPiIAIiIAIiIAIiECYEZAADDODa7oiIAIiIAIiIAIiIAEoHxABERABERABERCBMCMgARhmBtd0RUAEREAEREAEREACUD4gAiIgAiIgAiIgAmFGQAIwzAyu6YqACIiACIiACIiABKB8QAREQAREQAREQATCjIAEYJgZXNMVAREQAREQAREQAQlA+YAIiIAIiIAIiIAIhBkBCcAwM7imKwIiIAIiIAIiIAISgPIBERABERABERABEQgzAhKAYWZwTVcEREAEREAEREAEJADlAyIgAiIgAiIgAiIQZgQkAMPM4JquCIiACIiACIiACEgAygdEQAREQAREQAREIMwISACGmcE1XREQAREQAREQARGQAJQPiIAIiIAIiIAIiECYEZAADDODa7oiIAIiIAIiIAIiIAEoHxABERABERABERCBMCMgARhmBtd0RUAEREAEREAEREACUD4gAiIgAiIgAiIgAmFGQAIwzAyu6YqACIiACIiACIiABKB8QAREQAREQAREQATCjIAEYJgZXNMVAREQAREQAREQAQlA+YAIiIAIiIAIiIAIhBkBCcAwM7imKwIiIAIiIAIiIAISgPIBERABERABERABEQgzAhKAYWZwTVcEREAEREAEREAEJADlAyIgAiIgAiIgAiIQZgQkAMPM4JquCIiACIiACIiACEgAygdEQAREQAREQAREIMwISACGmcE1XREQAREQAREQARGQAJQPiIAIiIAIiIAIiECYEZAADDODa7oiIAIiIAIiIAIiIAEoHxABERABERABERCBMCMgARhmBtd0RUAEREAEREAEREACUD4gAiIgAiIgAiIgAmFGQAIwzAyu6YqACIiACIiACIiABKB8QAREQAREQAREQATCjIAEYJgZXNMVAREQAREQAREQAQlA+YAIiIAIiIAIiIAIhBkBCUAXBs/KysKWLVtQqVIlREREuOhJTUVABERABERABMqKQHZ2Nvbu3Yu6desiMjKyrDYbUtuRAHRhjk2bNqFBgwYuelBTERABERABERCBYBHYuHEj6tevH6zNB3W7EoAu8KempqJKlSqgAyUlJbnoSU1FQAREQAREQATKisCePXtMAiclJQWVK1cuq82G1HYkAF2Ygw5Ex6EQlAB0AVJNRUAEREAERKAMCSh+AxKALhxODuQCnpqKgAiIgAiIQJAIKH5LALpyPTmQK3xqLAIiIAIiIAJBIaD4LQHoyvHkQK7wqbEIiIAIiIAIBIWA4rcEoCvHkwO5wqfGIiACIhAWBPjIkYyMDGRmZobFfENhklFRUYiOji70EW2K3xKArvxUDuQKnxqLgAiIQLknkJaWhq1bt+LAgQPlfq6hNsHExETUqVMHsbGx+Yam+C0B6Mpf5UCu8KmxCIiACJRrAnxZwKpVq8BsVM2aNY0Q0UsD7JucGVcK7x07dpisa7NmzfI97FnxWwLQlSfKgVzhU2MREAERKNcEDh06hLVr1+KYY44Bs1EqZUuAWdf169ejcePGiI+Pz7NxxW8JQFfeKAdyhU+NRUAERKBcE3AEYEECpFxPPEQmVxR/xW8JQFduKgdyhU+NRUAERKBcE5AADK55JQCL5l9uHgT9448/YtCgQZg7d6654HbixIno0aNHkbOfPn06HnnkESxZssS8Euaf//wnbrnlFr89VgLQb1SqKAIiIAJhR0ACMLgmlwAMEwH4zTffYObMmejYsSOuuOKKYgUgr8to3bo17rnnHtxxxx2YOnUqHnroIXz11Vc4//zz/fJaCUC/MKmSCIiACIQlAQnA4JpdAjBMBGDuafIuq+IygP/4xz+M2Fu8eHFO0+uuu868GPrbb7/1y2slAP3C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d3j1JgIiIAIiIAIkYCt+e4luuVgCJnA+BoY3fbz99ttGCPIxMGPHjsXy5ctRq1Yt84gYXifI5/yxcDmXj3v5z3/+k7MEfO+994LCkH35U2w5kASgP/RVRwREQAREQARKR8BW/C7daILTqtwIQOLjI2CcB0G3b98evMmDmUGWbt26oVGjRhg5cqT5mzd9DBw4EKNGjcLmzZtRs2ZNXHLJJeazKlWq+GUNWw7kCMALWtXGiJu0BOyXMVRJBERABERABPwkYCt++7n5kKhWrgRgWRO15UAf/boe/5y0GBKAZW1RbU8EREAERCAcCNiK315iJwHowlq2HEgC0IVR1FQEREAEREAEiiFgK357CbwEoAtr2XIgRwCe36oW3r7pRBcjVFMREAEREAEREIGjCdiK314iLQHowlq2HOjjWevx1MTFkAB0YRw1FQEREAEREIFCCNiK314CLgHowlq2HEgC0IVR1FQEREAEREAEtARcrA9IABaLqPAKtgXgecfXwn9u1hKwCxOpqQiIgAiIgAjkI2ArfnsJtQSgC2vZcqDRszbgyYmLIAHowjhqKgIiIAIiIAJaAi7UByQAXeweEoAu4KmpCIiACIiACASJgK34HaTplGqzEoClwuZrZMuBnAzgucfXwjtaAnZhITUVAREQAREQgfwEbMVvL7GWAHRhLVsO9MnsDXjis0WQAHRhHDUVAREQAREQgUII2IrfXgIuAejCWrYcSALQhVHUVAREQAREQASKIWArfnsJvASgC2vZciAJQBdGUVMREAEREAERkAAs1gckAItFVHgF2wLwnONq4d1eegyMCxOpqQiIgAiIgAjkI2ArfnsJtQSgC2vZcqBPZ29Av88WQQLQhXHUVAREQAREQAQKIWArfnsJuASgC2vZciAJQBdGUVMREAEREAER0BJwsT4gAVgsorJfAv5bACbj3V6dXIxQTUVABERABERABI4mYCuB4yXSEoAurGXLgcb8tgH/mMAlYAlAF+ZRUxEQAREQAREokICt+O0l3BKALqxly4EkAF0YRU1FQAREQAREQEvAxfqABGCxiMp+CdgRgGe3TMZ/b9ESsAsTqakIiIAIiIAI5CNgK4HjJdQSgC6sZcuBxv62EX0n/A4JQBfGUVMREAEREAERKISArfjtJeASgC6sZcuBJABdGEVNRUAEREAEREBLwMX6gARgsYjKfgnYEYBntUzGe1oCdmEhNRUBERABERCB/ARsJXC8xFoC0IW1bDnQ2Dkb0Xf875AAdGEcNRUBERABERABLQEX6gMSgC52DwlAF/DUVAREQAREQASCRMBW/A7SdEq1WQnAUmHzNbLlQE4G8MwWNfH+rZ1djFBNRUAEREAEREAEjiZgK357ia6DBGgAACAASURBVLQEoAtr2XKgcXM24vHxv0MC0IVx1FQEREAEREAECiFgK357CbgEoAtr2XIgCUAXRlFTERABERABESiGgK347SXwEoAurGXLgSQAXRhFTUVABERABERAArBYH5AALBZR4RVsC8BuLWpipK4BdGEhNRUBERABERCB/ARsxW8vsZYAdGEtWw40fu4mPDZuISQAXRhHTUVABERABESgEAK24reXgEsAurCWLQeSAHRhFDUVAREQAREQAS0BF+sDEoDFIir7JWBHAJ7RvCY+uE2PgXFhIjUVAREQAREQgXwEbCVwvIRaAtCFtWw50IS5m/DouIWQAHRhHDUVAREQAREQAS0BF+oDEoAudg8JQBfw1FQEREAEREAEgkTAVvwO0nRKtVkJwFJh8zWy5UBOBvD05jXxoZaAXVhITUVABERABEQgPwFb8dtLrCUAXVjLlgN9Nm8THhm7EBKALoyjpiIgAiIgAiJQCAFb8dtLwCUAXVjLlgNJALowipqKgAiIgAiIQDEEbMVvL4GXAHRhLVsO5AjA05rVwKjbu7gYoZqKgAiIgAiIgAgcTcBW/PYSaQlAF9ay5UAT52/Cw2MWQgLQhXHUVAREQAREQAS0BFyoD0gAutg9JABdwFNTERABERABEQgSAVvxO0jTKdVmJQBLhc3XyJYDKQPowihqKgIiIAIiIALFELAVv70EXgLQhbVsOdCk+Zvx0JgFWgJ2YRs1FQEREAEREIHCCNiK314iLgHowlq2HEgC0IVR1FQEREAEREAElAEs1gckAItFVHgF2wLw1GNr4KM7dBewCxOpqQiIgAiIgAjkI2ArfnsJtQSgC2vZcqDJCzbjwU8XQALQhXHUVAREQAREQAQKIWArfnsJuASgC2vZciAJQBdGUVMREAEREAER0BJwsT4gAVgsorJfApYAdGEUNRUBERABERABCcBifUACsFhEwROApxxbHR/fcZKLEaqpCIiACIiACIjA0QRsreB5ibQEoAtr2XIgJwMoAejCOGoqAiIgAiIgAoUQsBW/vQRcAtCFtWw5kASgC6OoqQiIgAiIgAhoCbhYH5AALBZR8JaAT25aHaPv1BKwCxOpqQiIgAiIgAjkI2ArgeMl1BKALqxly4E+X7gFD3wyHxKALoyjpiIgAiIgAiKgJeBCfUAC0MXuIQHoAp6aioAIiIAIiECQCNiK30GaTqk2KwFYKmy+RrYcyMkAdm1SHZ/cpSVgFyZSUxEQAREQARHIR8BW/PYSaglAF9ay5UBfLNyCPp/MhwSgC+OoqQiIgAiIgAgUQsBW/PYScAlAF9ay5UASgC6MoqYiIAIiIAIiUAwBW/HbS+AlAF1Yy5YDOQLwpCbV8OldXV2MUE1FQAREQAREQASOJmArfnuJdLkSgMOHD8egQYOwbds2tGvXDkOHDkXnzp0LtUdKSgqeeuopfPbZZ9i1axeOOeYYDB48GBdeeKFfNrTlQF/+vgW9R8+HBKBfZlAlERABERABESgRAVvxu0SDCHLlciMAx4wZg5tvvhkjRoxAly5djJAbN24cVqxYgeTk5HyY09LScMopp5jvnnzySdSrVw/r169HlSpVjHj0p9hyIAlAf+irjgiIgAiIgAiUjoCt+F260QSnVbkRgBR9nTp1wrBhwwzJrKwsNGjQAH369EG/fv3y0aVQZLZw+fLliImJKRV9Ww7kCMAujathzN1aAi6VcdRIBERABERABAohYCt+ewl4uRCAzOYlJiZi/Pjx6NGjRw7/Xr16gcu8kydPzmcTLvNWq1bNtOP3NWvWxA033IB//OMfiIqK8suGthzoq9+34v7R8yAB6JcZVEkEREAEREAESkTAVvwu0SCCXLlcCMAtW7aYJdxffvkFXbv+nTHr27cvZsyYgVmzZuXD3LJlS6xbtw49e/bEfffdh9WrV5v/H3jgATz77LMFmuXw4cPgj1PoQMwypqamIikpKWCmlAAMGEp1JAIiIAIiIAL5CEgAAmErAJs3b45Dhw5h7dq1ORm/1157zSwLb926tcDdpX///hgwYEC+7yQAdXQRAREQAREQAe8QkAAsJwKwNEvAZ5xxhrn2b8qUKTke+80335g7gJnli42NzefJZZ0B7Ny4GsbqGkDvHFE0UhEQAREQAU8QkAAsJwKQ3sabQPjIFz76hYU3gTRs2BC9e/cu8CYQ3vk7evRorFmzBpGRkabNG2+8gZdeeglcUvan2HKgrxdtxX0fz4MEoD9WUB0REAEREAERKBkBW/G7ZKMIbu1ysQRMhHwMDG/6ePvtt40Q5GNgxo4da+7yrVWrlnlEDK8TfOGFFwzxjRs3olWrVqYN7xRetWoVbrvtNnMNIJ8N6E+x5UCOAIyJisDy57ojKjLCn+GojgiIgAiIgAiIgB8EbMVvPzYdMlWCIgCHDBniNwAKMn8LHwHjPAi6ffv24HaYGWTp1q0bGjVqhJEjR+Z097///Q8PP/wwFixYYMTh7bffHhJ3ATsCkAO9/8ymePz8lv4iUD0REAEREAEREIFiCEgABmkJuHHjxnlMs2PHDhw4cMA8hJmFj27h41n4kGYu0YZqseVA3yzains/nmemHRsViZUDu4cqAo1LBERABERABDxHwFb89hKIoGQAcwPidXhvvvkm/vvf/6JFixbmK769484778Tdd99tHtMSqsWWA0kAhqrFNS4REAEREIHyQMBW/PYSm6ALwKZNm5oHOHfo0CEPt7lz5+Kqq64yj2kJ1WLLgSQAQ9XiGpcIiIAIiEB5IGArfnuJTdAFIJd6+bBmvsYtd5k9e7a5bo9Lw6FabDnQt4u34p6PjiwBR0di5fNaAg5VH9C4REAEREAEvEfAVvz2EomgC8BLLrkEmzdvxrvvvosTTjjBsGP276677jI3Znz++echy9OWA0kAhqzJNTAREAEREIFyQMBW/PYSmqALQN4AwkexfPvtt+bBzCwZGRk4//zzzR27vBEkVIstB5IADFWLa1wiIAIiIALlgYCt+O0lNkEXgA6slStXYtmyZYiIiADf08tXtYV6seVA3y7ehns+mmumHxcdiRVaAg51V9D4REAEREAEPETAVvz2EILQehdwdna2YUcR6IViy4FyC8D4mEjzMGgVERABERABERCBwBCwFb8DM7qy6SUkMoAffviheYAz38bBwuzf448/jptuuqlsKJRyK7YcSAKwlAZRMxEQAREQARHwg4Ct+O3HpkOmStAF4GuvvYann37avLP3lFNOMWB+/vlnDB8+HM8//7x5U0eoFlsO9N2Sbbh7lG8JWBnAULW+xiUCIiACIuBVArbit5d4BF0A8q0gAwYMMO/qzV0++OAD9O/fPyyfA5hbACbERGHZcxd4yac0VhEQAREQAREIaQISgEF6FVxur4iPj8fixYtx7LHH5nEWLge3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SjUVAREQAREQARHITcBa/PYQZglAF8ay5kDLvgTG9PSNrGZL4P4SvMfXxXzUVAREQAREQATCgYC1+O0heBKALoxlzYGWfQGMudE3suTjgfv+52KUaioCIiACIiACIqAMYF4fkAB0sU+UjQBsBdz3i4tRqqkIiIAIiIAIiIAEoARgwPaCMhGAtVoD984M2JjVkQiIgAiIgAiEOwFr8dtDYJUBdGEsaw609HNg7E2+kdVqA9z7s4tRqqkIiIAIiIAIiIAygMoABmwvKBMBWK0p8MC8gI1ZHYmACIiACIhAuBOwFr89BFYZQBfGsuZAuTOAHF//VBejVFMREAEREAEREAFlAJUBDNheYE8ATgbG3vz3OCUAA2YzdSQCIiACIiAC1uK3h9AqA+jCWNYcaKkEoAuzqKkIiIAIiIAIFEnAWvz2EHcJQBfGsuZASyYB43odGVkE0D/FxSjVVAREQAREQAREQEvAWgIO2F5QJgIwrjLwxIaAjVkdiYAIiIAIiEC4E7AWvz0EVhlAF8ay5kC5M4BxScATG12MUk1FQAREQAREQASUAVQGMGB7gT0BOBEYd4tvnBKAAbOXOhIBERABERABErAWvz2EVxlAF8ay5kBLJABdmEVNRUAEREAERKBIAtbit4e4SwC6MJY1B8otAGMrAU9ucjFKNRUBERABERABEchNwFr89hBmCUAXxrLmQIs/A8bf6htZbEXgyc0uRqmmIiACIiACIiACEoB5fUAC0MU+USYCMKYC8NQWF6NUUxEQAREQAREQAQlACcCA7QX2BOAEYPxtvnFKAAbMXupIBERABERABEjAWvz2EF5lAF0Yy5oDLZYAdGEWNRUBERABERCBIglYi98e4i4B6MJY1hwojwBMBJ7a6mKUaioCIiACIiACIpCbgLX47SHMEoAujGXNgRaNBybc7htZdALwz20uRqmmIiACIiACIiACEoB5fUAC0MU+IQHoAp6aioAIiIAIiECQCFiL30GaT2k2KwFYGmpH2lhzIGUAXVhFTUVABERABESgaALW4reHwEsAujCWNQfKIwDjgX/+6WKUaioCIiACIiACIpCbgLX47SHMEoAujGXNgXILwKg44OntLkappiIgAiIgAiIgAhKAeX1AAtDFPmFNAP4+DvjsDt/IomKBp3e4GKWaioAIiIAIiIAISABKAAZsL5AADBhKdSQC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vn4MOE4ZQBfmUVMREAEREAERKJCAtfjtId4SgC6MZc2BHAF4/GXANR+6GKGaioAIiIAIiIAIaAk4vw9IALrYL2wKwAk/PotptRrjleunICE6wcUo1VQEREAEREAERCA3AWvx20OYJQBdGMuaA81+B22WDTEje7Tjo7il9S0uRqmmIiACIiACIiACEoB5fUAC0MU+URYC8O62d6N3h94uRqmmIiACIiACIiACEoASgAHbC6wJwFn/QZvlvnca39fuPtzb/t6AjVkdiYAIiIAIiEC4E7AWvz0EVhlAF8ay5kC5BOD97e/HPe3ucTFKNRUBERABERABEVAGUBnAgO0FZSEAH+jwAO5se2fAxqyOREAEREAERCDcCViL3x4CqwygC2NZc6BZb6PN8mFmZA+e8CDuaHOHi1GqqQiIgAiIgAiIgDKAygAGbC+wJQCzfx2BtiuGm3E+0vER3Nr61oCNWR2JgAiIgAiIQLgTsBW/vcRVGUAX1rLlQGm/DEfHVSPMyB478TH0atXLxSjVVAREQAREQAREQBlAZQADthfYEoD7Zg5B19XvmHH27dQXNx1/U8DGrI5EQAREQAREINwJ2IrfXuKqDKALa9lyoF0/v44z/njPjIx3APNOYBUREAEREAEREIHAELAVvwMzurLpRQLQBWdbDrTtp0E4d83f7wBe1GuRi1GqqQiIgAiIgAiIQG4CtuK3lyhLALqwli0H2vjji7hw7cc5I5MAdGEkNRUBERABERCBowjYit9eAi0B6MJathxozYx/47J1n0gAurCNmoqACIiACIhAYQRsxW8vEZcAdGEtWw70x/Tn0WP9GAlAF7ZRUxEQAREQARGQACzcByQAXewftgTgqunP4Yr1Y83I2tZsi48v/Hs52MVw1VQEREAEREAERACArfjtJbgSgC6sZcuBVk7/F65cP86MrH3N9hh14SgXo1RTERABERABERCB3ARsxW8vUZYAdGEtWw60Yvq/cNURAXhcteMw9hJfNlBFBERABERABETAPQFb8dv9yMquBwlAF6xtOdDyHwbg6g3jzciaVG6CyT0mFzjK/en7cfnky5GRlYGpV09FRESEi9moqQiIgAiIgAiEBwFb8dtL9CQAXVjLlgMtm9Yf12ycYEZWr2I9fHvltwWO8vM/PsdTPz9lvuvXuR96HtfTxWzCt2l2djbm/DkHv279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\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"<matplotlib.legend.Legend at 0x7fe6966cdcc0>" | |
] | |
}, | |
"execution_count": 83, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"fig, ax = plt.subplots()\n", | |
"width = 1000\n", | |
"for i, label in enumerate(state_space):\n", | |
" offsets = range(1, n_steps, 5)\n", | |
" ax.plot(offsets, [np.sum(states[:offset] == i) / float(offset) \n", | |
" for offset in offsets], label=state_space[i])\n", | |
"ax.set_xlabel(\"number of steps\")\n", | |
"ax.set_ylabel(\"likelihood\")\n", | |
"ax.legend()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"We can also find an analytical solution for the stationary distribution. A distribution $\\pi$ of the state space of a Markov Chain is the stationary one if it is a (row) eigenvector of the transition matrix: $\\pi T=\\pi$. So let's just calculate the eigenvectors and eigenvalues of $T$: " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 85, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"[0.4625 0.4375 0.1 ]\n" | |
] | |
} | |
], | |
"source": [ | |
"evals, evecs = np.linalg.eig(transition_matrix.T)\n", | |
"evec = np.squeeze(evecs.T[np.all(evecs.T > 0, 1)])\n", | |
"evec /= evec.sum()\n", | |
"print(evec)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Implementing our first MCMC algorithm" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"So all that is fun, but back to sampling arbitrary probability distributions. If we could design our transition kernel in such a way that the next state is already drawn from $\\pi$, we would be done, as our Markov Chain would... well... immediately sample from $\\pi$. Unfortunately, to do this, we need to be able to sample from $\\pi$, which we can't. Otherwise you wouldn't be reading this, right? \n", | |
"What is instead done in MCMC algorithms is that the transition kernel $T(x_{i+1}|x+i)$ is split up into two parts: a proposal distribution $q(x_{i+1}^*|x_i)$, from which we can sample possible next states, and an acceptance probability $p_\\mathrm{acc}(x_{i+1}=x_{i+1}^*|x_{i+1}^*, x_i)$ which determines if a proposal $x_{i+1}^*$ is accepted as the next state in the chain or not. This acceptance probability is designed in a way to correct for the error we make by choosing proposal states from a distribution $q \\neq \\pi$. We thus have \n", | |
"$$\n", | |
"T(x_{i+1}|x_i)=q(x_{i+1}^* | x_i) \\times p_\\mathrm{acc}(x_{i+1}=x_{i+1}^*|x_{i+1}^*, x_i) \\ \\mbox .\n", | |
"$$\n", | |
"I don't really know whether this is best notation for this, but the idea is: first get a $x_{i+1}^*$, then accept / reject it with probability $p_\\mathrm{acc}(x_{i+1}=x_{i+1}^*|x_{i+1}^*, x_i)$.\n", | |
"For this transition kernel to obey detailed balance, we need to choose $p_\\mathrm{acc}$ correctly. One possibility is the Metropolis acceptance criterion: \n", | |
"$$\n", | |
"p_\\mathrm{acc}(x_{i+1}=x_{i+1}^*|x_{i+1}^*,x_i) = \\mathrm{min} \\left\\{1, \\frac{\\pi(x_{i+1}^*) \\times q(x_i|x_{i+1}^*)}{\\pi(x_i) \\times q(x_{i+1}^*|x_i)} \\right\\} \\ \\mbox .\n", | |
"$$\n", | |
"Most commonly, symmetric proposal distributions ($q(x_i|x_{i+1}^*)=q(x_{i+1}^*|x_i)$) are used, in which case the Metropolis-Hastings algorithm reduces to the original Metropolis algorithm developed in 1953 at Los Alamos and for which\n", | |
"$$\n", | |
"p_\\mathrm{acc}(x_{i+1}=x_{i+1}^*|x_{i+1}^*,x_i) = \\mathrm{min} \\left\\{1, \\frac{\\pi(x_{i+1}^*)}{\\pi(x_i)} \\right\\} \\ \\mbox .\n", | |
"$$" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Alright, now that we now how MCMC in principle works, let's go ahead and implement the most basic MCMC algoritm: Metropolis-Hastings.\n", | |
"First, we set the log-probability of the distribution we want to sample from - without normalization constants, as we pretend we don't know them. Let's work for now with a standard normal distribution:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 86, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"log_prob = lambda x: -0.5 * np.sum(x ** 2)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Next, we need a symmetric proposal distribution. We can, for example, just take the current state $x$ and pick a proposal from $\\mathcal{U}(x-\\frac{\\Delta}{2}, x+\\frac{\\Delta}{2})$, that is, we set some step size $\\Delta$ and move left or right a maximum of $\\frac{\\Delta}{2}$ from our current state:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 87, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"proposal = lambda x, stepsize: np.random.uniform(low=x - 0.5 * stepsize, high=x + 0.5 * stepsize, size=x.shape)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Finally, we calculate our acceptance probability:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 88, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"p_acc = lambda x_new, x_old, log_prob: min(1, np.exp(log_prob(x_new) - log_prob(x_old)))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now we piece all this together into our first implementation of the Metropolis-Hastings algoritm:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 89, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def sample_MH(x_old, log_prob, stepsize):\n", | |
" x_new = proposal(x_old, stepsize)\n", | |
" # here we determine whether we accept the new state or not:\n", | |
" # we draw a random number uniformly from [0,1] and compare\n", | |
" # it with the acceptance probability\n", | |
" accept = np.random.random() < p_acc(x_new, x_old, log_prob)\n", | |
" if accept:\n", | |
" return accept, x_new\n", | |
" else:\n", | |
" return accept, x_old" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Apart from the next state in the Markov chain, $x_\\mathrm{new}$ or $x_\\mathrm{old}$, we also return whether the MCMC move was accepted or not. That will allow us to keep track of the acceptance rate.\n", | |
"Sweet. Now let's write a function that iteratively calls `sample_MH` and thus builds up a Markov chain:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 90, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def build_MH_chain(init, stepsize, n_total, log_prob):\n", | |
"\n", | |
" n_accepted = 0\n", | |
" chain = [init]\n", | |
"\n", | |
" for _ in range(n_total):\n", | |
" accept, state = sample_MH(chain[-1], log_prob, stepsize)\n", | |
" chain.append(state)\n", | |
" n_accepted += accept\n", | |
" \n", | |
" acceptance_rate = n_accepted / float(n_total)\n", | |
" \n", | |
" return chain, acceptance_rate" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now you're probably excited to see this baby in action. Here we go, taking some informed guesses at the `stepsize` and `n_total` parameters:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 91, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Acceptance rate: 0.763\n", | |
"Last ten states of chain: -0.30299, 0.55305, 0.56697, 0.56697, 0.23015, -0.13149, -1.21230, -1.28122, -1.18260, -1.18260\n" | |
] | |
} | |
], | |
"source": [ | |
"chain, acceptance_rate = build_MH_chain(np.array([2.0]), 2.5, 10000, log_prob)\n", | |
"print(\"Acceptance rate: {:.3f}\".format(acceptance_rate))\n", | |
"print(\"Last ten states of chain: \" + \"\".join([\"{:.5f}, \".format(x) \n", | |
" for x in [x[0] for x in chain[-10:]]])[:-2])" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Alright. So did this work? We achieved an acceptance rate of around 75% and we have a chain of states. Let's check whether the states in the chain are actually normally distributed:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 92, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"def plot_samples(chain, log_prob):\n", | |
" from scipy.integrate import quad\n", | |
" fig, ax = plt.subplots()\n", | |
" ax.hist(chain[100:], bins=50, density=True, label=\"MCMC samples\")\n", | |
" xspace = np.linspace(-5, 5, 1000)\n", | |
" # we numerically calculate the normalization constant of or PDF\n", | |
" Z = quad(lambda x: np.exp(log_prob(x)), -1000, 1000)[0]\n", | |
" ax.plot(xspace, np.array(list(map(lambda x: np.exp(log_prob(x)), xspace))) / Z,\n", | |
" label=\"true distribution\")\n", | |
" ax.legend(frameon=False)\n", | |
" ax.set_yticks(())\n", | |
" for spine in ('top', 'left', 'right'):\n", | |
" ax.spines[spine].set_visible(False)\n", | |
" plt.show()\n", | |
"\n", | |
"plot_samples([x[0] for x in chain], log_prob)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Yay. Now isn't that neat!" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now, what's up with the parameters `stepsize` and `n_total`? Easy: the step size determines how big the random steps are our Markov chain takes. If the step size is too large, we will jump around a lot in the tails of the distribution, where probability is low. Kinda boring there. The MH sampler will reject most of these moves, meaning that the acceptance rate decreases and convergence is much slower. See for your self:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 93, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Acceptance rate: 0.167\n" | |
] | |
}, | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuzdCbyUY//H8e/MnHMmUdl3OooIJVtkqyRRWRIpslQKla20SIX2VZYWShIS2RIVKknxpNCCiKLsuxaqmTkz839dd875d05nmTmz3TP3Z16v5/V/nqf7vq7r974u/77PdW+ucDgcFj8EEEAAAQQQQAABxwi4CICOmWsKRQABBBBAAAEELAECIAsBAQQQQAABBBBwmAAB0GETTrkIIIAAAggggAABkDWAAAIIIIAAAgg4TIAA6LAJp1wEEEAAAQQQQIAAyBpAAAEEEEAAAQQcJkAAdNiEUy4CCCCAAAIIIEAAZA0ggAACCCCAAAIOEyAAOmzCKRcBBBBAAAEEECAAsgYQQAABBBBAAAGHCRAAHTbhlIsAAggggAACCBAAWQMIIIAAAggggIDDBAiADptwykUAAQQQQAABBAiArAEEEEAAAQQQQMBhAgRAh0045SKAAAIIIIAAAgRA1gACCCCAAAIIIOAwAQKgwyacchFAAAEEEEAAAQIgawABBBBAAAEEEHCYAAHQYRNOuQgggAACCCCAAAGQNYAAAggggAACCDhMgADosAmnXAQQQAABBBBAgADIGkAAAQQQQAABBBwmQAB02IRTLgIIIIAAAgggQABkDSCAAAIIIIAAAg4TIAA6bMIpFwEEEEAAAQQQIACyBhBAAAEEEEAAAYcJEAAdNuGUiwACCCCAAAIIEABZAwgggAACCCCAgMMECIAOm3DKRQABBBBAAAEECICsAQQQQAABBBBAwGECBECHTTjlIoAAAggggAACBEDWAAIIIIAAAggg4DABAqDDJpxyEUAAAQQQQAABAiBrAAEEEEAAAQQQcJgAAdBhE065CCCAAAIIIIAAAZA1gAACCCCAAAIIOEyAAOiwCadcBBBAAAEEEECAAMgaQAABBBBAAAEEHCZAAHTYhFMuAggggAACCCBAAGQNIIAAAggggAACDhMgADpswikXAQQQQAABBBAgALIGEEAAAQQQQAABhwkQAB024ZSLAAIIIIAAAggQAFkDCCCAAAIIIICAwwQIgA6bcMpFAAEEEEAAAQQIgKwBBBBAAAEEEEDAYQIEQIdNOOUigAACCCCAAAIEQNYAAggggAACCCDgMAECoMMmnHIRQAABBBBAAAECIGsAAQQQQAABBBBwmAAB0GETTrkIIIAAAggggAABkDWAAAIIIIAAAgg4TIAA6LAJp1wEEEAAAQQQQIAAyBpAAAEEEEAAAQQcJkAAdNiEUy4CCCCAAAIIIEAAZA0ggAACCCCAAAIOEyAAOmzCKRcBBBBAAAEEECAAsgYQQAABBBBAAAGHCRAAHTbhlIsAAggggAACCBAAWQMIIIAAAggggIDDBAiADptwykUAAQQQQAABBAiArAEEEEAAAQQQQMBhAgRAh0045SKAAAIIIIAAAgRA1gACCCCAAAIIIOAwAQKgwyacchFAAAEEEEAAAQIgawABBBBAAAEEEHCYAAHQYRNOuQgggAACCCCAAAGQNYAAAggggAACCDhMgADosAmnXAQQQAABBBBAgADIGkAAAQQQQMDGArm5ubrzzjutf5mfy+XSq6++qssvvzyuo27QoIHq1Kmjhx56yGq3aL/x7KxoX/Fsm7YiEyAARubEUQgggAACGS5g11BSNIj98ssv2meffeT1esuckWjC4l9//aXs7GxVqlQpbgHw3XffVcOGDfX3339r7733Lhhv0b7KLIQD4i5AAIw7KQ0igAACCKSjQCQBMBwOKxgMKisrK2klxrITF0kA9Pv9ysnJ2a2eWPrNb6ykAJg0PDoqUYAAyOJAAAEEEHC8wI033qipU6cWcvj222+1YcMGawdrzpw56tu3rz799FO9/fbbeuqpp7Rp0ybNnDmz4BxziXblypUyocf8QqGQhg8frokTJ8rs2tWoUUP9+vXTlVdeWaL3b7/9pg4dOmj+/Pk6+OCDNWjQIN17770lXgI24a1bt256+eWXrV22gw46SLfccovuuece6xLuxo0bC/qqWrWqVc/9999vjbtr164aPHiwdYwZa3GXgM1Y1qxZo1mzZlk7eH369FGXLl2sNk1bRx11lFasWGFdOjY/Y2J2JxcuXGj1b/58198NN9xg2RXty4z9jjvu0Ouvvy6fz6f69evrkUce0THHHGOdbs4xvi+88IL1f7///nudc845mjJlig455BDHr9/yABAAy6PGOQgggAACkQuEw1JgW+THx/PI7IrmprkyW9y8ebMuvvhinXjiiRowYIB1/AEHHKDFixdbAbB27doaNWqUqlWrZgUcE7rKCoAmXD377LPWPXUmyLz33ntWOHvrrbesgFPcr2nTpvrpp5/02GOPWZdj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JKhMWzabaPuiRUc7FyaAvfPOO9qyZYt+/vln7bHHHlaTO3bs0CGHHKLKlSurYcOGBQFww4YNOvvss7X33ntrwIABqlWrlgKBgN566y1NnDhRX375ZUEADIVCqlmzpvVn+b+lS5eqWbNm8vl8Gjt2bEEAfOONN9SyZUs1adJEt99+u6pXr67ffvtNL774or7//nu98MILtpzfkgZFALTndBEA7TkvjAoBBBIkkOgQEm3oSFCZtmw20fbxCICbNm3S+vXr1bt3b1177bVWk88995yGDx+uo446ygp7+TuATZs21erVq7V27Vrtueeehbo37Zhjzc/sALZp00ZjxozR119/be3mmV+nTp1UoUIFPf3003rooYesALht2zZVrVpV55xzjl599dXd5nHXdov+odmhNH2YkFilShWde+65eumll6zD3nzzTQ0aNEifffaZPB6P6tWrp4cfftgKl+Znwqypz4TLRx99VB999JFOPPFETZs2zdqBvPXWW61Aa9o04z3ggAOs88yYzZhOPvlkK8SaMHvNNdfokUceUU5OjnVM0QBojrn33nutHVFzrunH+JrjzM/slnbt2lVLliyR3++3/EaOHCnjzS9+AgTA+FnSEgIIpIFAokMIAbDkRZBo+3gFwPr161uXdOfPn281ecEFF6h58+Z69913CwLgX3/9pf3339+6/HvPPfeUuvJNgDGXhBcuXKjTTz9dffv2tYKe2VVctGiRFXzyA6AJfVdccYU++OADK6RF+jOB7cwzz9Qzzzyjs846S2Z8ixcvtnYQze/ll1+Wy+VS7dq19c8//6h///5W6Fu5cqXcbndBADzuuOOssRx55JFq3769taNZqVIlKzxWrFhRrVq1sjwmTJhQEABN22Yns1+/flY77dq1U8eOHS2b4gKg+TNz2XvYsGE69NBDraBrTD799FMdc8wxlrUJfqNHj7aCtTnW7L6ed955kXJwXAQCBMAIkDgEAQQyRyDRIYQAmP4BcNKkSdYundnZMz8Tisyu2k033VQQAJctW6YzzjhDr7zyilq0aBFRADQ7bN27d7d2AU1QM0Hrk08+sdrMD4AjRoxQr169rAC3zz77RPwPnhmHCV4//PCDFdjK+v3xxx/WLp4JXWYHLn8H8IknnlCHDh2s059//nlr53LBggU6//zzrf/OhDazA5p/edvsAL7++uuWjwmI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\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"chain, acceptance_rate = build_MH_chain(np.array([2.0]), 20, 10000, log_prob)\n", | |
"print(\"Acceptance rate: {:.3f}\".format(acceptance_rate))\n", | |
"plot_samples([x[0] for x in chain], log_prob)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Not so cool, huh? Now you could think the best thing to do is do choose a tiny step size. Turns out that this is not too smart, either, because then the Markov chain will explore the probability distribution only very slowly and thus again won't converge as rapidly as with a well-adjusted step size:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 94, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Acceptance rate: 0.989\n" | |
] | |
}, | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuzdB5gT1RrG8TfJFqTaEBBF7Ap2RQEromLvXax4LTTFimJvYEGQar2IDRVUFBvYEBF7R8R6UbECIkV0W3KfM8visuzuZJKZZMo/z8Mj92bmzPl+34F9mclMYqlUKiVeCCCAAAIIIIAAApERiBEAI9NrCkUAAQQQQAABBCwBAiALAQEEEEAAAQQQiJgAATBiDadcBBBAAAEEEECAAMgaQAABBBBAAAEEIiZAAIxYwykXAQQQQAABBBAgALIGEEAAAQQQQACBiAkQACPWcMpFAAEEEEAAAQQIgKwBBBBAAAEEEEAgYgIEwIg1nHIRQAABBBBAAAECIGsAAQQQQAABBBCImAABMGINp1wEEEAAAQQQQIAAyBpAAAEEEEAAAQQiJkAAjFjDKRcBBBBAAAEEECAAsgYQQAABBBBAAIGICRAAI9ZwykUAAQQQQAABBAiArAEEEEAAAQQQQCBiAgTAiDWcchFAAAEEEEAAAQIgawABBBBAAAEEEIiYAAEwYg2nXAQQQAABBBBAgADIGkAAAQQQQAABBCImQACMWMMpFwEEEEAAAQQQIACyBhBAAAEEEEAAgYgJEAAj1nDKRQABBBBAAAEECICsAQQQQAABBBBAIGICBMCINZxyEUAAAQQQQAABAiBrAAEEEEAAAQQQiJgAATBiDadcBBBAAAEEEECAAMgaQAABBBBAAAEEIiZAAIxYwykXAQQQQAABBBAgALIGEEAAAQQQQACBiAkQACPWcMpFAAEEEEAAAQQIgKwBBBBAAAEEEEAgYgIEwIg1nHIRQAABBBBAAAECIGsAAQQQQAABBBCImAABMGINp1wEEEAAAQQQQIAAyBpAAAEEEEAAAQQiJkAAjFjDKRcBBBBAAAEEECAAsgYQQAABBBBAAIGICRAAI9ZwykUAAQQQQAABBAiArAEEEEAAAQQQQCBiAgTAiDWcchFAAAEEEEAAAQIgawABBBBAAAEEEIiYAAEwYg2nXAQQQAABBBBAgADIGkAAAQQQQAABBCImQACMWMMpFwEEEEAAAQQQIACyBhBAAAEEEEAAgYgJEAAj1nDKRQABBBBAAAEECICsAQQQQAABBBBAIGICBMCINZxyEUAAAQQQQAABAiBrAAEEEEAAAQQQiJgAATBiDadcBBBAAAEEEECAAMgaQAABBBBAAAEEIiZAAIxYwykXAQQQQAABBBAgALIGEEAAAQQQQACBiAkQACPWcMpFAAEEEEAAAQQIgKwBBBBAAAEEEEAgYgIEwIg1nHIRQAABBBBAAAECIGsAAQQQQAABBBCImAABMGINp1wEEEAAAQQQQIAAyBpAAAEEEEAAAQQiJkAAjFjDKRcBBBBAAAEEECAAsgYQQAABBBBAAIGICRAAI9ZwykUAAQQQQAABBAiArAEEEEAAAQQQQCBiAgTAiDWcchFAAAEEEEAAAQIgawABBBBAAAEEEIiYAAEwYg2nXAQQQAABBBBAgADIGkAAAQQQQAABBCImQACMWMMpFwEEEEAAAQQQIACyBhBAAAEEEPCxQNu2bXX++edbv8wrFovpqaee0uGHH+7qrPfaay9tt912Gjp0qDVuzeO6ebCax3JzbMZKT4AAmJ4TWyGAAAIIhFzAr6GkZhD79ddftcYaa6i4uNi2I07C4h9//KHCwkI1adLEtQA4depUdenSRQsXLtTqq6++Yr41j2VbCBu4LkAAdJ2UARFAAAEEgiiQTgBMpVKqqKhQQUFBzkrM5kxcOgGwtLRURUVFq9STzXGrBqsrAOYMjwPVKUAAZHEggAACCERe4LTTTtPYsWNXcvjf//6nOXPmWGewnn/+eV1xxRX67LPPNGXKFN1///36888/NXHixBX7mEu0H3/8sUzoMa9kMqmbb75Zd999t8xZu80220xXXnmljj766Dq9f//9d/Xo0UMvv/yyWrZsqRtuuEEDBgyo8xKwCW8XXHCBnnjiCessW4sWLXTOOefosssusy7hfv/99yuOtcEGG1j1XHPNNda8e/furRtvvNHaxsy1tkvAZi6zZs3SM888Y53Bu/zyy9WrVy9rTDPWhhtuqI8++si6dGxexsScnXzttdes45v3q79OPfVUy67msczczzvvPE2aNEklJSXac889NWzYMG266abW7mYf4/vYY49Z//3xxx+12267acyYMWrVqlXk128mAATATNTYBwEEEEAgfYFUSipblv72bm5Z2NB8aM52xEWLFumAAw7QVlttpeuuu87avnnz5nrjjTesALjNNtvotttu00YbbWQFHBO67AKgCVcPPfSQ9Zk6E2SmTZtmhbPJkydbAae214EHHqiff/5Zd955p3U5tm/fvlbA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0WqrrWYh//PPP2rVqpWaNm2qLl26rAiAc+bM0a677qrVV19d1113nbbeemuVlZVp8uTJuvvuuzV79uwVATCZTGrLLbe03qt6vf322zrooINUUlKiESNGrAiAzz77rI466ih169ZNffv21cYbb6zff/9d48eP148//qjHHnssUM0nAPqzXQRAf/aFWSGAAAEwdGsgCAHwzz//1Lfffqv+/fvrpJNOsnrwyCOP6Oabb9aGG25ohb2qM4AHHnigPv30U3355Zdq1KjRSv0y45htq84AnnDCCRoyZIi+/vpr62yeeZ111llq0KCBHnjgAQ0dOtQKgMuWLdMGG2yg3XbbTU899dQqa6D6uDXfNGcozTFMSGzWrJl23313TZgwwdrsxRdf1A033KCZM2cqkUioU6dOuuOOO6xwaV4mzJr6TLgcPny43n//fW211VZ6+OGHrTOQ5557rhVozZhmvs2bN7f2M3M2c9p+++2tEGvC7Iknnqhhw4apqKjI2qZmADTbDBgwwDojavY1xzG+ZjvzMmdLe/furenTp6u0tNQ6g3rrrbfKePNyT4AA6J4lIyGAQA4FOAOYQ2yXDhWUALjnnntal3Rffvllq/J99tlHBx98sKZOnboiAP7xxx9ae+21rcu/l112Wb1CJsCYS8KvvfaaOnTooCuuuMIKeuas4uuvv24Fn6oAaELfkUceqRkzZlghLd2XCWwdO3bUgw8+qM6dO8vM74033rDOIJrXE088oVgspm222UZLly7VVVddZYW+jz/+WPF4fEUA3GKLLay5tGnTRmeccYZ1RrNJkyZWeGzYsKGOPfZYy2P06NErAqAZ25zJvPLKK61xTj/9dP3nP/+xbGoLgOY9c9l70KBBWnfdda2ga0w+++wzbbrpppa1CX6DBw+2grXZ1px93WOPPdLlYLs0BAiAaSCxCQII+E+AAOi/ntjNKCgB8J577rHO0pkze+ZlQpE5q3bmmWeuCIDvvvuudtllFz355JM64ogj0gqA5gzbhRdeaJ0FNEHNBK0PP/zQGrMqAN5yyy269NJLrQC3xhpr2JGueN/MwwSvuXPnWoHN7jV//nzrLJ4JXeYMXNUZwHvvvVc9evSwdn/00Udlzly+8sor2nvvva3/z4Q2cwa06vK2OQM4adIky8cERPMyn580n1U0Zw5NuKx+BvCHH37QRhttJPNfE/6qXiZU7rzzztbnHk1INZfAr776arsyeD8LAQJgFnjsigAC+RMgAObPPtMjByUATpw40QogJoikUinrsqm5lHr44YevCIDvvPOOdcbNSQA0lzXXW2896zKrCTdHH320damzegA0l0LN5WenAXDJkiXW5xHNZxf3339/65cJplWhzIROc9bPzNuEP/OZxL/++ss602kurVYFQBNszVlK8zJnLE3wM58/rLrka25WMSHWzM+8TAA0Yc58drLq9cknn1g3wpgxzeXs6gHQHM+c4at5ydxcFjZnPo2NCaHmkrMJhCYYVvUi03XHfrULEABZGQggEEgBAmDw2hakAGiCigln5jVy5EgrJFUPgJlcAjaXgc2Zsffee88KYj///LN1lq96AMz0ErCZZ3l5uXWZesqUKdYlX3P2zRzLjG/OYpowdskll1hn3kwANGf+zPFMXVUB0Nx9bMKbeZmxzE0vCxcuXPF5RnP2z9RhPruXSQA0Ac98tvLzzz+3PotY/dW4cWO1bNnS+r/MGUXTA1OLuSnGXA7u06dP8Ba9j2dMAPRxc5gaAgjULUAADN7qCFIArKiosD4HZz43Z25KMGGlegA0+gcccIB1CTWdm0BMaDK/vvjiC7Vr107HHXecdYnVvKoHQHNWznxmMJObQKqvCDOOGdcELvOZRvN5RfNIGXMTh3mZGyzM790IgOYSsLn0XHXX9F133aWLLrqo1kvAX331lTbffPOV5mK3ks1nLE0YNDfc8HJPgADoniUjIYBADgUIgDnEdulQQQqApmTzOBjzMjcgmFfNAPjdd99Zl13XXHNN6zEw5pKxOQv30ksvWTdJmLBnXlU3gZgAaF4LFiywLs1WBabqAdC8//TTT+uYY46xLuOamzg22WQT67Lt448/bl1urQqO1dtizpKZ+ZgbJcxZxeeff946g2lCk3n8jHk+oQms5tKzGcNcZjZnB90IgOZs4yGHHGLdyGHOJJqbR8znEQcOHGhNseZdwN27d9ebb75pndUzdw/PmzfP+pyh8TM3kxgnM1fzvENz9rFnz57W2cugPf7GpT82ng1DAPSMloERQMBLAQKgl7rejB20AFhToWYANO+bz9yZu11NADO/N5+V23HHHdWvX78VjzWpGQBrjlszAJr3zV29JkCZO3lNEDU3pZjP45lLyCYQ1nyZM3omgJnAZ55baO6mNY9aMXft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dgYwiznlc1cTiNdff339+OOPkf0DlQt/nHOhXPkPPNaz99Y4e29sjoBz7c6cAczN+uMoIReoOiMa5X9R5aLFOOdC+d8z/Kxnb71Zz976Vo2Oc26cg3QUzgAGqVs+nyt/weSmQTjjnBuB3ByF9YxzbgQ4Sk0BAiBrwjUB/iJ3jbLegXDGOTcCuTkK6xnn3AhwFAIga8AzgZKSEg0cOFCXXXaZiouLPTtO1AfGOTcrAGeccyOQm6OwnnPjHKSjcAYwSN1irggggAACCCCAgAsCBEAXEBkCAQQQQAABBBAIkgABMEjdYq4IIIAAAggggIALAgRAFxAZAgEEEEAAAQQQCJIAATBI3WKuCCCAAAIIIICACwIEQBcQGaJuAXPn2S677KJPPvlEH330kbbbbju4XBKYM2eOrr/+er366qv69ddfte6666p79+4aMGCAioqKXDpKNIcZOXKkbr31Vst122231fDhw7XzzjtHE8Ojqs0TA5588knNnj1bq622mjp37qybb75Zm2++uUdHZFgjMGjQIOtJDeedd56GDh0KSoQFCIARbn4uSjd/yXz99dd64YUXCIAug7/44ot67LHHdMIJJ2iTTTbRzJkz9Z///Ecnn3yybrvtNpePFp3hjOkpp5yiO++80/rHi/khOX78eH355ZdaZ511ogPhcaX777+/jj/+eHXo0EHl5eW6/PLLrTU8a9YsNWrUyOOjR3P49957T8cee6z1VZ1dunQhAEZzGayomgAY8QXgZfkm9F1wwQV64okn1L59ewKgl9jLxzZnrUaPHq3vvvsuB0cL5yFM6DOhZMSIEVaByWTS+k7gPn36qH///uEs2gdVzZs3zwrYr7/+uvbYYw8fzChcU1i6dKl22GEHjRo1SjfccIN1NYYzgOHqsdNqCIBOxdg+LYHffvtNO+64oyZOnKi1115bG264IQEwLbnsNrriiitkzgy+//772Q0U0b1LS0vVsGFDTZgwQYcffvgKhVNPPVV//vmnnn766YjKeF/2N998o0033VSfffaZttpqK+8PGLEjmDW85pprasiQIdprr70IgBHrf23lEgBZBK4LpFIpHXjggdp1111lAon5rBoB0HXmVQY0P0BN6DaXf82lYF7OBX7++We1bt1aM2bMUKdOnVYMcMkll1hnpt555x3ng7KHrYA5y3rooYdaIXv69Om227OBM4FHH31UN954o8wl4AYNGhAAnfGFdmsCYGhb635h5vKX+ZB2fa8vvvhCU6ZM0eOPP279wEwkEgRAh61I13mLLbZYMfJPP/2kPffc0/qL/d5773V4RDavEiAA5mctnHvuudbnhE34W2+99fIziZAe9ccff9ROO+2kl156Sdtss41VJWcAQ9psh2URAB2CRXlz8xmdBQsW1Euw0UYbWR8ynjRpkmKx2IptKyoqrDB40kknaezYsVFmtK09XeeqO31NaDF/oXfs2FH333+/4vG47THYoHYBLgHnfmX07t3burQ+bdo060oBL3cFzMdwjjjiCOvv36qX+fvY/P1s/q4wT2qo/p67R2c0PwsQAP3cnYDO7YcfftDixYtXzN4ElG7dulmfqzIfsOdf+O411pz5M3fzmUu/Dz30EH+Ru0Br1qh55It59It5mcuTbdq0kQkq3ATiAvDyIcxHRcyNNU899ZSmTp1qff6Pl/sCS5Ys0ffff7/SwKeffrrMFYRLL72Uz1u6Tx6YEQmAgWlVcCfKZwC96Z0Jf+bM3wYbbGCdVa3+r/iWLVt6c9AIjGoeA2M+MH/XXXdZQdDcKWk+0mCeV9eiRYsICOSmxJ49e+qRRx6xzv5Vf/Zfs2bNrOcC8vJOgEvA3tkGaWQCYJC6FdC5EgC9aZy53Gv+JV/by5xd4ZW5gHkETNWDoM3jMoYNG2adveblnkD1j4hUH3XMmDE67bTT3DsQI60iQABkURgBAiDrAAEEEEAAAQQQiJgAATBiDadcBBBAAAEEEECAAMgaQAABBBBAAAEEIiZAAIxYwykXAQQQQAABBBAgALIGEEAAAQQQQACBiAkQACPWcMpFAAEEEEAAAQQIgKwBBBBAAAEEEEAgYgIEwIg1nHIRQAABBBBAAAECIGsAAQQQQAABBBCImAABMGINp1wEEEAAAQQQQIAAyBpAAAEEEEAAAQQiJkAAjFjDKRcBBBBAAAEEECAAsgYQQAABBBBAAIGICRAAI9ZwykUAAQQQQAABBAiArAEEEEAAAQQQQCBiAgTAiDWcchFAAAEEEEAAAQIgawABBBBAAAEEEIiYAAEwYg2nXAQQQAABBBBAgADIGkAAAQQQQAABBCImQACMWMMpFwEEEEAAAQQQIACyBhBAAAEEEEAAgYgJEAAj1nDKRQABBBBAAAEECICsAQQQQAABBBBAIGICBMCINZxyEUAAAQQQQAABAiBrAAEEEEAAAQQQiJgAATBiDadcBBBAAAEEEECAAMgaQAABBBBAAAEEIiZAAIxYwykXAQQQQAABBBAgALIGEEAAAQQQQACBiAkQACPWcMpFAAEEEEAAgf+3W8cEAAAACML6tyYIi+D0kIADaAMECBAgQIAAgZmAAzgrXFwCBAgQIECAQNpkl5sz7eXPAAAAAElFTkSuQmCC\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"chain, acceptance_rate = build_MH_chain(np.array([2.0]), 0.1, 10000, log_prob)\n", | |
"print(\"Acceptance rate: {:.3f}\".format(acceptance_rate))\n", | |
"plot_samples([x[0] for x in chain], log_prob)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"No matter how you choose the step size parameter, the Markov chain will eventually converge to its equilibrium distribution. But it may take a looooong time. Let's keep the tiny step size and increase the number of samples:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 95, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Acceptance rate: 0.990\n" | |
] | |
}, | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuzdB5RT1b7H8W86gmDFXiiK2BBUrKhgQRSxoFKsKB0UpIMFFelFekcsV8CGHRURsaEigiA2UBTBdsUCqEgymeStfeYOjzJDEiYzc8ova9317nNO9tn/z3+73u/tU+JLJpNJ9JGABCQgAQlIQAIS8IyATwHQM71WoRKQgAQkIAEJSMASUADUQpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCUhAAh4TUAD0WMNVrgQkIAEJSEACElAA1BqQgAQkIAEJSEACHhNQAPRYw1WuBCQgAQlIQAISUADUGpCABCQgAQlIQAIeE1AA9FjDVa4EJCABCUhAAhJQANQakIAEJCABCdhYoFKlStxxxx3Wf8zH5/Px3HPPceWVV2Z11nXr1qVmzZqMGjXKGnfH82bzZDueK5tja6z0BBQA03PSURKQgAQk4HIBu4aSHYPYL7/8wj777EMkEknZkUzC4h9//EEoFKJ8+fJZC4BvvfUW9erV488//2TvvffeOt8dz5WyEB2QdQEFwKyTakAJSEACEnCiQDoBMJlMkpubSzAYLLESi7ITl04AjMVihMPhneopynnzByssAJYYnk5UqIACoBaHBCQgAQl4XqBFixY8+uij2zl89913rFmzxtrBeuWVV7j77rtZsWIFr7/+Oo888ggbNmzg+eef3/odc4l22bJlmNBjPolEgiFDhjBlyhTMrl21atW45557uOaaawr1/vXXX2nZsiVvvPEGBx10EP379+euu+4q9BKwCW9du3Zl9uzZ1i7bgQceSLt27ejTp491Cff777/feq4jjzzSque+++6z5n3bbbcxYMAA6xgz14IuAZu5fPHFF7z44ovWDt6dd95Jx44drTHNWJUrV+aTTz6xLh2bjzExu5MLFiywzm/+vu3n5ptvtux2PJeZe+fOnXnppZeIRqOcd955jBkzhqOPPtr6uvmO8X3yySet/7lu3Trq1KnDww8/zMEHH+z59bs7AAqAu6Om70hAAhKQQPoCySTkbE7/+GweGSprbppLOeLGjRu55JJLOOGEE+jX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\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"chain, acceptance_rate = build_MH_chain(np.array([2.0]), 0.1, 500000, log_prob)\n", | |
"print(\"Acceptance rate: {:.3f}\".format(acceptance_rate))\n", | |
"plot_samples([x[0] for x in chain], log_prob)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Sloooowly getting there..." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Slightly more interesting distributions" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Okay, now let's step things up a bit. Let's consider a mixture of two Gaussians, which we will choose in such a way that the resulting density is bimodal, and see how we can handle that:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 96, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"sigma0 = 0.5\n", | |
"sigma1 = 0.2\n", | |
"mu0 = -1.5\n", | |
"mu1 = 2.0\n", | |
"# now working in log space for numerical stability\n", | |
"gaussian_log_prob = lambda x, mu, sigma: -0.5 * np.sum(x - mu) ** 2 / sigma ** 2 - np.log(np.sqrt(2 * np.pi * sigma ** 2))\n", | |
"mixture_log_prob = lambda x: np.logaddexp(np.log(0.3) + gaussian_log_prob(x, mu0, sigma0),\n", | |
" np.log(0.7) + gaussian_log_prob(x, mu1, sigma1))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"First, we'll try Metropolis-Hastings again:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 97, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Acceptance rate: 0.554\n" | |
] | |
}, | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuzdCbhV8/7H8c+eD5GxzESDCJVkuqRkppBQaC5DChXNk+ZUpJImUpnSIEMqSWUmZEwZknnIkPns+f/81rH7n45TnWHvtYf1Xs/jca/W+g2v7+/e+7m/NWxXPB6PiwMBBBBAAAEEEEDAMQIuAqBjas1EEUAAAQQQQAABS4AAyEJAAAEEEEAAAQQcJkAAdFjBmS4CCCCAAAIIIEAAZA0ggAACCCCAAAIOEyAAOqzgTBcBBBBAAAEEECAAsgYQQAABBBBAAAGHCRAAHVZwposAAggggAACCBAAWQMIIIAAAggggIDDBAiADis400UAAQQQQAABBAiArAEEEEAAAQQQQMBhAgRAhxWc6SKAAAIIIIAAAgRA1gACCCCAAAIIIOAwAQKgwwrOdBFAAAEEEEAAAQIgawABBBBAAAEEEHCYAAHQYQVnuggggAACCCCAAAGQNYAAAggggAACCDhMgADosIIzXQQQQAABBBBAgADIGkAAAQQQQAABBBwmQAB0WMGZLgIIIIAAAgggQABkDSCAAAIIIIAAAg4TIAA6rOBMFwEEEEAAAQQQIACyBhBAAAEEEEAAAYcJEAAdVnCmiwACCCCAAAIIEABZAwgggAACCCCAgMMECIAOKzjTRQABBBBAAAEECICsAQQQQAABBBBAwGECBECHFZzpIoAAAggggAACBEDWAAIIIIAAAggg4DABAqDDCs50EUAAAQQQQAABAiBrAAEEEEAAAQQQcJgAAdBhBWe6CCCAAAIIIIAAAZA1gAACCCCAAAIIOEyAAOiwgjNdBBBAAAEEEECAAMgaQAABBBBAAAEEHCZAAHRYwZkuAggggAACCCBAAGQNIIAAAggggAACDhMgADqs4EwXAQQQQAABBBAgALIGEEAAAQQQQAABhwkQAB1WcKaLAAIIIIAAAggQAFkDCCCAAAIIIICAwwQIgA4rONNFAAEEEEAAAQQIgKwBBBBAAAEEEEDAYQIEQIcVnOkigAACCCCAAAIEQNYAAggggAACCCDgMAECoMMKznQRQAABBBBAAAECIGsAAQQQQAABBBBwmAAB0GEFZ7oIIIAAAggggAABkDWAAAIIIIAAAgg4TIAA6LCCM10EEEAAAQQQQIAAyBpAAAEEEEAAAQQcJkAAdFjBmS4CCCCAAAIIIEAAZA0ggAACCCCAAAIOEyAAOqzgTBcBBBBAAAEEECAAsgYQQAABBBBAAAGHCRAAHVZwposAAggggAACCBAAWQMIIIAAAggggIDDBAiADis400UAAQQQQAABBAiArAEEEEAAAQQQQMBhAgRAhxWc6SKAAAIIIIAAAgRA1gACCCCAAAIIIOAwAQKgwwrOdBFAAAEEEEAAAQIgawABBBBAAAEEEHCYAAHQYQVnuggggAACCCCAAAGQNYAAAggggAACCDhMgADosIIzXQQQQAABBBBAgADIGkAAAQQQQAABBBwmQAB0WMGZLgIIIIAAAgggQABkDSCAAAIIIIAAAg4TIAA6rOBMFwEEEEAAAQQQIACyBrJCoEqVKrrlllusv8zhcrn0+OOP65JLLknq+Bs2bKg6depo/PjxVrtF+01mZ0X7SmbbtIUAAggggMCOBAiArI9tBDI1lBQNYt9//7322msvBQKBnVawNGHxl19+kc/n0+677560ALhq1So1atRIv/76q/bcc8+t4y3a104nwgkIIIAAAggkSYAAmCTIXGmmJAEwHo8rGo3K6/XaNu3y7MSVJACGQiH5/f7/zKc8/SYa214AtA2PjhBAAAEEECgiQABkSWwVaNu2rWbNmrWNyOeff65NmzZZO1jPPPOM+vfvr/fff1/PPvusHnjgAW3ZskWLFi3aeo25RfvOO+/IhB5zxGIxjR49WtOmTZPZtatRo4YGDBig5s2bb1f+xx9/VIcOHfTcc89p//3317Bhw9SvX7/t3gI24a179+5asGCBtcu233776frrr1efPn2sW7hffPHF1r4OO+wwaz6DBw+2xt2lSxcNHz7cOseMtbhbwGYs69at05NPPmnt4PXt21c33nij1aZp6/DDD9fatWutW8fmMCZmd3LlypVW/+bPCx9t2rSx7Ir2ZcZ+880366mnnlIwGNQZZ5yhCRMmqHr16tbl5hrjO3fuXOvvX331lU477TTNnDlTBxxwACsZAQQQQACBEgsQAEtMlYQT43Ep/HcSGiplE75dzUNzO73ot99+0/nnn69jjjlGQ4YMsc6vVKmSXnzxRSsAHnfccRo7dqyOOOIIK+CY0LWzAGjC1YMPPmg9U2eCzAsvvGCFs2XLllkBp7jjggsu0LfffqspU6ZYt2NvuukmK2CNGDGi2GcAzZhM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/7TnnntqyJAhOvbYYxUOh7Vs2TJNmzZN69ev3xoAY7GYjjrqKOvPEsdrr72mCy+8UMFgUJMmTdoaAJ9++mlddtllOvfcc3XTTTepatWq+vHHHzVv3jx99dVXmjt3bhoqV/YuCYBlt+PK3BQgAOZmXZlV9ggQANNQq0wPgFu2bNFnn32m3r176+qrr7aEHn74YY0ePVqHH364FfYSO4AXXHCB3nvvPW3YsEEVKlTYRtO0Y85N7AC2bNlSd911lz755BNrN88c1157rfLy8jR79myNHz/eCoB///23DjvsMJ122ml6/PHH/1Ohwu0W/UOzQ2n6MCFxjz320Omnn6758+dbpy1dulTDhg3TBx98II/Ho1NOOUV33323FS7NYcKsmZ8JlxMnTtSbb76pY445Rg899JC1A3nDDTdYgda0acZbqVIl6zozZjOmunXrWiHWhNmrrrpKEyZMkN/vt84pGgDNOf369bN2RM21ph/ja84zh9kt7dKli1566SWFQiFrB3XMmDEy3hwI5IIAATAXqsgcslmAAJiG6mVDADzjjDOsW7rPPfecJXTWWWfpoosu0qpVq7YGwF9++UX77ruvdfu3T58+O5Q0AcbcEl65cqXq16+v/v37W0HP7CquXr3aCj6JAGhCX7NmzfTKK69YIa2khwlsJ598subMmaNTTz1VZnwvvviitYNojgULFsjlcum4447Tn3/+qYEDB1qh75133pHb7d4aAGvWrGmN5dBDD1X79u2tHc3dd9/dCo+77rqrrrjiCsvj3nvv3RoATdtmJ3PAgAFWO+3atVOnTp0sm+ICoPkzc9t71KhROvDAA62ga0zef/99Va9e3bI2wW/cuHFWsDbnmt3XBg0alJSD8xDIaAECYEaXh8E5QIAAmIYiZ0MAnD59urVLZ3b2zGFCkdlV69ix49YA+MYbb+ikk07SwoULdemll5YoAJodth49eli7gCaomaD19ttvW20mAuAdd9yhXr16WQFur732KnGFzDhM8Pr666+twLaz46effrJ28UzoMjtwiR3AGTNmqEOHDtbljz76qMzO5YoVK3TmmWda/8yENrMDmri9bXYAn3rqKcvHBERzmOcnzbOKZufQhMvCO4BffvmljjjiCJm/m/CXOEyoPPHEE63nHk1INbfABw0atLNp8OcIZKUAATAry8agc0iAAJiGYmZDAFy0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\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"chain, acceptance_rate = build_MH_chain(np.array([2.0]), 1.0, 10000, mixture_log_prob)\n", | |
"print(\"Acceptance rate: {:.3f}\".format(acceptance_rate))\n", | |
"plot_samples([x[0] for x in chain], mixture_log_prob)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Not so great. We're oversampling one of the modes and undersampling the other. That's because it takes the Markov Chain a long time to cross the region of low probability between the modes. Most of the proposals ending up in there will get rejected.\n", | |
"We now remember some elementary probability theory. Our Gaussian mixture is a density: $$p(x) = \\frac{3}{10}\\mathcal{N}(\\mu_1, \\sigma_1) + \\frac{7}{10}\\mathcal{N}(\\mu_2, \\sigma2) = \\sum_{k=\\{1,2\\}} p(x|k) p(k) = \\sum_{k=\\{1,2\\}} p(x,k)$$\n", | |
"That means that if we can sample from the joint distribution $p(x,k)$, we can later just sum over (marginalize out) the component $k$ to obtain $p(x)$. But how do we sample from $p(x,k)$? $x$ is continuous and $k$ is discrete, so it's not immediately clear how to design a Metropolis-Hastings algorithm to sample from that. Luckily, there's Gibbs sampling (technically a special case of the Metropolis-Hastings algorithm), which allows us to reconstruct the joint distribution $p(x,k)$ from the conditional distributions $p(x|k)$ and $p(k|x)=p(k)$. It works in two steps: \n", | |
"$$ \\begin{align}\n", | |
" x_{i+1} &\\sim p(x|k_i) \\\\\n", | |
" k_{i+1} &\\sim p(k|x_{i+1})\n", | |
" \\end{align}\n", | |
"$$\n", | |
"with, in our case,\n", | |
"$$ p(x|k) = \n", | |
" \\begin{cases}\n", | |
" \\mathcal{N}(\\mu_1, \\sigma_1) :& k = 1\\\\\n", | |
" \\mathcal{N}(\\mu_2, \\sigma_2) :& k = 2\n", | |
" \\end{cases}\n", | |
"$$\n", | |
"and\n", | |
"$$ p(k|x) = p(k) = \n", | |
" \\begin{cases}\n", | |
" \\frac{3}{10} :& k = 1\\\\\n", | |
" \\frac{7}{10} :& k = 2\n", | |
" \\end{cases} \\ \\mbox .\n", | |
"$$\n", | |
"That way, we'll end up with a new state $(x_{i+1}, k_{i+1})$. So in words, a new state is drawn alternatively drawing from the conditional distributions of each variable.\n", | |
"Let's implement this:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 117, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def build_gibbs_chain(init, n_total, mu0, sigma0, mu1, sigma1):\n", | |
" init_x, init_k = init\n", | |
" chain = [init]\n", | |
" for _ in range(n_total):\n", | |
" x_new = None\n", | |
" k_new = None\n", | |
" \n", | |
" # draw new x conditioned on k\n", | |
" if chain[-1][1] == 0:\n", | |
" x_new = np.random.normal(mu0, sigma0)\n", | |
" if chain[-1][1] == 1:\n", | |
" x_new = np.random.normal(mu1, sigma1)\n", | |
" \n", | |
" # draw new k\n", | |
" k_new = np.random.choice((0,1), p=(0.3, 0.7))\n", | |
" \n", | |
" chain.append((x_new, k_new))\n", | |
" \n", | |
" return chain\n", | |
"\n", | |
"chain = build_gibbs_chain((np.array([2.0]), 0), 10000, mu0, sigma0, mu1, sigma1)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's see whether this worked and plot all the `x`ses, that is, the marginal distribution $p(x)$ we were looking for:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 118, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"plot_samples([x[0] for x in chain], mixture_log_prob)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Pretty kewl! I personally think it is fascinating that knowledge of the conditional distributions only is fully sufficient to reconstruct the *joint* distribution. In our case was especially simple, as we were able to directly sample from the conditional distributions. In general, we might use MCMC sampling to sample from one or more of the conditional distributions." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Exploiting the geometry of the distribution for better proposal states" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now we're getting to the really cool stuff. Aren't the proposal states we pick in Metropolis-Hastings kind of dumb? After all, we're just performing a random walk. We can do better! Say we're trying to sample from a joint distribution of $d$ random variables. Then we can think of a list of $d$ values representing realizations of these random variables as the position vector of a lazy particle moving in a $d$-dimensional space under the influence of an energy potential given by the negative log-probability. If the lazy particle moves around in that space, it is less likely to hang out in regions with high energy (equivalent to low probability), while it's more likely to be found in regions with low energy (equivalent to high probability). \n", | |
"If you now give this particle a kick so that it moves in some direction, it will slow down when it moves towards a region of high energy (kinetic energy decreases and potential energy increases) or accelerate if it's going towards a region of low energy (increase in kinetic energy is balanced by a decrease in potential energy). Its total energy is conserved and thus the probability of it having a certain total energy is constant everywhere in that space." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now this means that if we're able to predict the location of the particle after some time $t$, having given it a kick drawn from a multivariate normal distribution with mean zero (which is the distribution of velocities lazy particles in these kind of energy landscapes happen to have), we will get a nice, possibly distant state drawn from our distribution of interest $\\pi$! Now unfortunately this prediction is hard as it involves solving the equations of motion of the particle. But we can solve them approximately by discretizing time. We will inevitably make a numerical error when doing this, but with a Metropolis acceptance step we can correct for it, just as we corrected for the error in having a proposal distribution not perfectly adapted to $\\pi$." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Here's a little function approximatively solving the equations of motions of the particle. It's called the leap frog integrator and it depends on an initial state $(x,p)$, the gradient of the energy landscape, a discretization timestep and the number of steps for which to integrate the equation of motions. Basically, we calculate the position and momentum of the particle at time `timestep x trajectory_length`:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 100, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def leapfrog(x, p, gradient, timestep, trajectory_length):\n", | |
" \n", | |
" p -= 0.5 * timestep * gradient(x)\n", | |
" for _ in range(trajectory_length - 1):\n", | |
" x += timestep * p\n", | |
" p -= timestep * gradient(x)\n", | |
" x += timestep * p\n", | |
" p -= 0.5 * timestep * gradient(x)\n", | |
" \n", | |
" return x, p" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"We also need the total energy of the particle, which is the sum of the potential (\"gravitation\") energy and the kinetic energy $\\frac{|\\vec{p}|^2}{2}$: " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 101, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"total_E = lambda x, p, E: E(x) + 0.5 * np.sum(p ** 2)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"For the Metropolis step, we need to again calculate an acceptance probability:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 102, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"log_p_acc = lambda x_new, p_new, x_old, p_old, E: min(0, -(total_E(x_new, p_new, E) - total_E(x_old, p_old, E)))\n", | |
"p_acc = lambda x_new, p_new, x_old, p_old, E: min(1, np.exp(-(total_E(x_new, p_new, E) - total_E(x_old, p_old, E))))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's stitch all this together in a function yielding a single HMC sample:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 103, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def sample_HMC(x_old, log_prob, log_prob_gradient, timestep, trajectory_length):\n", | |
" # switch to physics mode!\n", | |
" energy = lambda x: -log_prob(x)\n", | |
" gradient = lambda x: -log_prob_gradient(x)\n", | |
" \n", | |
" p_old = np.random.normal(size=x_old.shape)\n", | |
" x_new, p_new = leapfrog(x_old.copy(), p_old.copy(), gradient, timestep, trajectory_length)\n", | |
" # print(x_new, p_new)\n", | |
" accept = np.log(np.random.random()) < log_p_acc(x_new, p_new, x_old, p_old, energy)\n", | |
" # accept = np.random.random() < p_acc(x_new, p_new, x_old, p_old, energy)\n", | |
" \n", | |
" if accept:\n", | |
" return accept, x_new\n", | |
" else:\n", | |
" return accept, x_old" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"And, you guessed it, analogous to both other MCMC algorithms we discussed before, here's a function to build up a Markov chain from HMC samples:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 104, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def build_HMC_chain(init, timestep, trajectory_length, n_total, log_prob, gradient):\n", | |
"\n", | |
" n_accepted = 0\n", | |
" chain = [init]\n", | |
"\n", | |
" for _ in range(n_total):\n", | |
" accept, state = sample_HMC(chain[-1].copy(), log_prob, gradient, timestep, trajectory_length)\n", | |
" chain.append(state)\n", | |
" n_accepted += accept\n", | |
" \n", | |
" acceptance_rate = n_accepted / float(n_total)\n", | |
" \n", | |
" return chain, acceptance_rate" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's write down the log-probability of a two-dimensional Gaussian and its gradient:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 105, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"log_prob = lambda x: -0.5 * np.sum(x ** 2)\n", | |
"log_prob_gradient = lambda x: -x" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now we're ready to go, that is, to test the HMC sampler:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 106, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Acceptance rate: 0.601\n" | |
] | |
} | |
], | |
"source": [ | |
"chain, acceptance_rate = build_HMC_chain(np.array([5.0, 1.0]), 1.54, 10, 10000, log_prob, log_prob_gradient)\n", | |
"print(\"Acceptance rate: {:.3f}\".format(acceptance_rate))\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"To display the result, we plot a two-dimensional histogram of the sampled states:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 107, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4Xuy9B3gj13nv/QcLAFaAJACCBHtftuX21UparSxpJVu2ZNmW4092LPkmdiyX6Nr5blye+IsUO4/jOL5WbuIS27m23GXZKla1VpJ3Je1qeyG5y9577wWs33MGBAFwZpdDDrkgif88j5444DkzZ37zDvDbU96jm5+fnwcPEiABEiABEiABEiCBgCGgowAGzLPmjZIACZAACZAACZCARIACyEAgARIgARIgARIggQAjQAEMsAfO2yUBEiABEiABEiABCiBjgARIgARIgARIgAQCjAAFMMAeOG+XBEiABEiABEiABCiAjAESIAESIAESIAESCDACFMAAe+C8XRIgARIgARIgARKgADIGSIAESIAESIAESCDACFAAA+yB83ZJgARIgARIgARIgALIGCABEiABEiABEiCBACNAAQywB87bJQESIAESIAESIAEKIGOABEiABEiABEiABAKMAAUwwB44b5cESIAESIAESIAEKICMARIgARIgARIgARIIMAIUwAB74LxdEiABEiABEiABEqAAMgZIgARIgARIgARIIMAIUAAD7IHzdkmABEiABEiABEiAAsgYIAESIAESIAESIIEAI0ABDLAHztslARIgARIgARIgAQogY4AESIAESIAESIAEAowABTDAHjhvlwRIgARIgARIgAQogIwBEiABEiABEiABEggwAhTAAHvgvF0SIAESIAESIAESoAAyBkiABEiABEiABEggwAhQAAPsgfN2SYAESIAESIAESIACyBggARIgARIgARIggQAjQAEMsAfO2yUBEiABEiABEiABCiBjgARIgARIgARIgAQCjAAFMMAeOG+XBEiABEiABEiABCiAjAESIAESIAESIAESCDACFMAAe+C8XRIgARIgARIgARKgADIGSIAESIAESIAESCDACFAAA+yB83ZJgARIgARIgARIgALIGCABEiABEiABEiCBACNAAQywB87bJQESIAESIAESIAEKIGOABEhgyxH42c9+hk984hM4c+YMdu/eLbu/Q4cOobe3F+Xl5dLf0tLS0NTUhNtuuw2vvfaarPyPf/xjfOpTn5I+VzrnxYsX8W//9m84duwYuru7ERERgZ07d+KjH/0oPv7xjyM4OHjLMeYNkQAJbG4CFMDN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mgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgJoCjwKoCR8rkwAJkAAJkIBfCFAAKYCaAo8CqAkfK5MACZAACZCAXwhQACmAmgKPAqgJHyuTAAmQAAmQgF8IUAApgIuB9y//8i/4yle+gkceeQSPP/64qoCkAKrCxEIkQAIkQAIksKEIUAApgFJAnjlzBh/+8IcRHR2NW2+9lQK4oV5TNoYESIAESIAE1pYABZACiNHRUezc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r8hJKbJ2iB4nMYlTjIn+yU9+InuZdtNGAQzS3RYtfiIx53333YfZ2Vlv/jCRwoTb5glQADfPTnT3ilYCvU20znA7PwGRAsaTCFrM6Bez+0XLIDfjBPyHcfjXEgl3//qv/9r4jlhSlwAFcPOB8fvf/17+Xov8f2JYgpgQshsnxlEANx9DrEkCJEACJEACJEACYUmAAhiWt40nTQIkQAIkQAIkQAKbJ0AB3Dw71iQBEiABEiABEiCBsCRAAQzL28aTJgESIAESIAESIIHNE6AAbp4da5IACZAACZAACZBAWBKgAIblbeNJkwAJkAAJkAAJkMDmCVAAN8+ONUmABEiABEiABEggLAlQAMPytvGkSYAESIAESIAESGDzBCiAm2fHmiRAAiRAAiRAAiQQlgQogGF523jSJEACJEACJEACJLB5AhTAzbNjTRIgARIgARIgARIISwIUwLC8bTxpEiABEiABEiABEtg8AQrg5tmxJgmQAAmQAAmQAAmEJQEKYFjeNp40CZAACZAACZAACWyeAAVw8+xYkwRIgARIgARIgATCkgAFMCxvG0+aBEiABEiABEiABDZPgAK4eXasSQIkQAIkQAIkQAJhSYACGJa3jSdNAiRAAiRAAiRAApsnQAHcPDvWJAESIAESIAESIIGwJEABDMvbxpMmARIgARIgARIggc0ToABunh1rkgAJkAAJkAAJkEBYEqAAhuVt40mTAAmQAAmQAAmQwOYJUAA3z441SYAESIAESIAESCAsCVAAw/K28aRJgARIgARIgARIYPMEKICbZ8eaJEACJEACJEACJBCWBCiAYXnbeNIkQAIkQAIkQAIksHkCFMDNs2NNEiABEiABEiABEghLAhTAsLxtPGkSIAESIAESIAES2DwBCuDm2bEmCZAACZAACZAACYQlAQpgWN42njQJkAAJkAAJkAAJbJ4ABXDz7FiTBEiABEiABEiABMKSAAUwLG8bT5oESIAESIAESIAENk/g/wcSSCcQRN/H7wAAAABJRU5ErkJggg==\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"def plot_samples_2D(chain, path_length, title):\n", | |
" fig, ax = plt.subplots()\n", | |
" chain = np.array(chain)\n", | |
" bins = np.linspace(-6, 6, 100)\n", | |
" ax.hist2d(*chain[200:].T, bins=(bins, bins))\n", | |
" ax.plot(*chain[:path_length].T, marker='o', c='w', lw=0.2, markersize=0.75, alpha=0.75)\n", | |
" ax.set_title(title)\n", | |
" plt.show()\n", | |
" \n", | |
"plot_samples_2D(chain, 100, \"HMC\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"How does Metropolis-Hastings do on the same distribution? Take a look:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 108, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Acceptance rate: 0.737\n" | |
] | |
} | |
], | |
"source": [ | |
"# gotta wrap this in classes...\n", | |
"p_acc = lambda x_new, x_old, log_prob: min(1, np.exp(log_prob(x_new) - log_prob(x_old)))\n", | |
"chain, acceptance_rate = build_MH_chain(np.array([5.0, 1.0]), 1.74, 10000, log_prob)\n", | |
"print(\"Acceptance rate: {:.3f}\".format(acceptance_rate))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 109, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4XuydB3hc1Zm/fxpJo95778W9GzC4UEwnQAKkQYAkm06yyeaftrsJ7KYnu8mSsumkktASyJoewAYXbIxtucgqlqzee6+j//Pd8WhmdK/tK5+xRtL8zvP4SRjdc++57/3u6NUp3/GbmpqaAgsJkAAJkAAJkAAJkIDPEPCjAPrMs+aNkgAJkAAJkAAJkIBGgALIQCABEiABEiABEiABHyNAAfSxB87bJQESIAESIAESIAEKIGOABEiABEiABEiABHyMAAXQxx44b5cESIAESIAESIAEKICMARIgARIgARIgARLwMQIUQB974LxdEiABEiABEiABEqAAMgZIgARIgARIgARIwMcIUAB97IHzdkmABEiABEiABEiAAsgYIAESIAESIAESIAEfI0AB9LEHztslARIgARIgARIgAQogY4AESIAESIAESIAEfIwABdDHHjhvlwRIgARIgARIgAQogIwBEiABEiABEiABEvAxAhRAH3vgvF0SIAESIAESIAESoAAyBkiABEiABEiABEjAxwhQAH3sgfN2SYAESIAESIAESIACyBggARIgARIgARIgAR8jQAH0sQfO2yUBEiABEiABEiABCiBjgARIgARIgARIgAR8jAAF0MceOG+XBEiABEiABEiABCiAjAESIAESIAESIAES8DECFEAfe+C8XRIgARIgARIgARKgADIGSIAESIAESIAESMDHCFAAfeyB83ZJgARIgARIgARIgALIGCABEiABEiABEiABHyNAAfSxB87bJQESIAESIAESIAEKIGOABEiABEiABEiABHyMAAXQxx44b5cESIAESIAESIAEKICMARIgARIgARIgARLwMQIUQB974LxdEiABEiABEiABEqAAMgZIgARIgARIgARIwMcIUAB97IHzdkmABEiABEiABEiAAsgYIAESIAESIAESIAEfI0AB9LEHztslAV8lcN9992Hnzp2oqamZRuDn54evfe1rePDBBxctluzsbGzbtg2//e1vF+098sZIgARmT4ACOHtmrEECC4KA/MK///77tba+8cYbuOKKK9zaPTU1hczMTDQ0NOCmm27Cjh07ZnVfP/3pTxEaGgoRq4VQLrYAimR1dHTg+PHjOhwinTk5Ofje976Hz3/+8x7H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EiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8QoACSAFUCjwKoBI+ViYBEiABEiABrxCgAFIAlQKPAqiEj5VJgARIgARIwCsEKIAUQKXAowAq4WNlEiABEiABEvAKAQogBVAp8CiASvhYmQRIgARIgAS8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h1mO/JSRAAmQwIwiQAGcUa97Wj0sBXCaCeCDDz6Ixx57bMwgzcnJwY4dO/Dmm29i165dsLe31yyADz/8MB555BHF+SmA0+p7gQ9DAiSgkQAFUCMoHjbpCFAAp5kA1tfXo7GxccxAi4uLw5YtW/Dhhx9i1qxZw8cODg5KGbzxxhvx8ssvq56DI4CT7jPMGyIBErAhAQqgDeHz0roIUACnmQBqjYaysjKI6Vtjq6qqwsaNG/H222/LlDARERGaTsU0MJow8SASIAESIAESmFQEKIAzVABHR6HWNYCj+1EAJ9XnmTdDAiRAAiRAApoIUAApgDJQKICaPi88iARIgARIgASmBQEKIAVQVyBzBFAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABScparxAAADxNJREFUAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52JgESIAESIAGbEKAAUgB1BR4FUBc+diYBEiABEiABmxCgAFIAdQUeBVAXPnYmARIgARIgAZsQoABSAHUFHgVQFz52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dIQgJCTE8hNP854iDYxYCySWdAgRFLssxbIE8VkODg6e5k9vvccTC+tfe+01OfpnmvvP29tb5gVk00eAU8CW8RNTvSInpcjaIWacxCZOsSb6ueeek7NMM6lRAK30tsWIn0jM+corr6C7u3s4f5hIYcJmOQEKoOXsxHSvGCVQa2J0hu3sBEQKGGMiaLGjX+zuFyODbNoJmC7jMO0lEu7edttt2k/EI1UJUAAtD4yPPvpI/r0W+f/EsgSxIWQmboyjAFoeQ+xJAiRAAiRAAiRAAlOSAAVwSr423jQJkAAJkAAJkAAJWE6AAmg5O/YkARIgARIgARIggSlJgAI4JV8bb5oESIAESIAESIAELCdAAbScHXuSAAmQAAmQAAmQwJQkQAGckq+NN00CJEACJEACJEAClhOgAFrOjj1JgARIgARIgARIYEoSoABOydfGmyYBEiABEiABEiABywlQAC1nx54kQAIkQAIkQAIkMCUJUACn5GvjTZMACZAACZAACZCA5QQogJazY08SIAESIAESIAESmJIEKIBT8rXxpkmABEiABEiABEjAcgIUQMvZsScJkAAJkAAJkAAJTEkCFMAp+dp40yRAAiRAAiRAAiRgOQEKoOXs2JMESIAESIAESIAEpiQBCuCUfG28aRIgARIgARIgARKwnAAF0HJ27EkCJEACJEACJEACU5IABXBKvjbeNAmQAAmQAAmQAAlYToACaDk79iQBEiABEiABEiCBKUmAAjglXxtvmgRIgARIgARIgAQsJ0ABtJwde5IACZAACZAACZDAlCRAAZySr403TQIkQAIkQAIkQAKWE6AAWs6OPUmABEiABEiABEhgShKgAE7J18abJgESIAESIAESIAHLCVAALWfHniRAAiRAAiRAAiQwJQlQAKfka+NNkwAJkAAJkAAJkIDlBCiAlrNjTxIgARIgARIgARKYkgQogFPytfGmSYAESIAESIAESMByAhRAy9mxJwmQAAmQAAmQAAlMSQIUwCn52njTJEACJEACJEACJGA5AQqg5ezYkwRIgARIgARIgASmJAEK4JR8bbxpEiABEiABEiABErCcwP8PyhCjEHbitHAAAAAASUVORK5CYII=\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"plot_samples_2D(chain, 200, \"Metropolis-Hastings\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"What you might or might not see is that Metropolis-Hastings takes a longer time to explore the relevant region of high probability, while HMC finds it much faster. " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Fighting general multimodality" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"The last algorithm we discuss is a way to fight multimodality. As we saw in the Gaussian mixture example, a sampler can have a hard time traversing regions of low probability and thus sampling a multimodal distribution correctly. A cool way to overcome this problem is to not sample only from the distribution of interest, but also somehow \"flatter\" versions of it, in which low-probability barriers can be crossed more easily, and occasionally exchange states betweens the Markov chains at different temperatures in a manner which maintains all equilibrium distributions. This is the idea of Replica Exchange, also called Parallel Tempering or Metropolis Coupled Markov Chain Monte Carlo." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's first talk about flattening distributions. The most simple way, which is inspired by statistical physics, is to consider a family of distributions $p_\\beta(x) = p(x)^\\beta$ with $p_1(x)=p(x)$ being the distribution we're actually interested in and $p_\\beta(x)$ with $1>\\beta > 0$ being the same distribution at higher \"inverse temperature\" $\\beta$. Let's write a function which will be able to plot histograms and true distributions for different inverse temperatures and see how the family of distributions could look like in the case of the Gaussian mixture example:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 110, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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+eefyyWXXOKxMvlDQrWl/bZhiQG0oOekgAoKCiQ8PNyi9IdCjzSAHkn0BIkUFxebHsaEhARzk9EbzDXXXCN/+tOf5PHHH/d29qQPAQhAwC8InHnmmTJkyBB59NFHTXm+++47ueKKK8yIjCeODz/80NxbX3nlFWMwdVTn448/lnXr1knjxo2Pm8W0adNkwYIF0rt3b7nsssswgJ6oDD9MAwNoUSm+NIA6LNulSxfTQ/bOO+9I165dTe+Z3ijuuece+fLLLyU/P1/69Okjzz77rHnS02Ps2LFmaFd72/71r39JWlqaXHjhheaJMyYmxpxzsiFgTfeRRx6R9957T1JTUyUpKUnuv/9+ueGGG0TN3E033SSzZs0yT5gtWrSQ2267Te66664T3ly0DCkpKdKkSRNznt6g/v73v8vevXs9ZmwtqpZQCEAAAl4lkJ2dLbGxsbJw4UJjzvTQe7n2uum93ROHptu3b1958cUXTXIlJSXm/n3HHXfIfffd51YWQUFBGEC3SNW8kzCAFnXmawOoNwY1cmq89OjQoYMMHTpU6tatawyaGropU6aIzilZv369xMfHGwOoc0r0RvD000+LllnjdQjg3XffdcsAXnXVVbJo0SJ5/vnnjbHcsmWL7Nu3T/T7wsJCYywvuugiadCggbmZqSF844035MorrzwmXS3r//73P1mxYkXFv2uabdq0kWXLlknPnj0taoVQCEAAAv5PYP78+TJ48GBRI6jGTB+w9eFZh1uPnAOoIyMnGx1ZvXq1eQB3HTpKFBkZKZ988slhw7c65KwdB9pp4M6BAXSHUs08BwNoUW++NoCanxok16E3EL1RaK9cRERExfdt27aVMWPGGCOmBlAN2rZt26RZs2bmnOnTp5u4nTt3mmHYE/UAqpFUo6lDEzpU4c5x++23m95AvfEc69ByaXm+/fbbin/OycmRevXqyTfffCPnn3++O9lwDgQgAIEaS0B75fSBXUc/BgwYYK5Dh1t1iDY4OPiw69J52fpzoqNVq1aHzaHWERa95+tDef/+/StCtW2YO3euLF682C12GEC3MNXIkzCAFtXmawPYrl07M3TrOnRy75133ml6ACsfubm5ZijhiSeeMAbwrbfeks2bN1eckpmZaYYe5syZI4MGDTqhAfzoo49k1KhRomnqwpBjHVqO119/XXRhh56nT546x2/JkiUY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\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"def plot_RE_samples(chain, log_prob, temperatures):\n", | |
" from scipy.integrate import quad\n", | |
"\n", | |
" fig, axes = plt.subplots(len(temperatures), 1, sharex=True, sharey=True)\n", | |
" xspace = np.linspace(-3, 5, 1000)\n", | |
" for i, (temp, ax) in enumerate(zip(temperatures, axes)):\n", | |
" pdf = lambda x: np.exp(temp * mixture_log_prob(x))\n", | |
" Z = quad(pdf, -1000, 1000)[0]\n", | |
" if len(chain) > 0:\n", | |
" ax.hist(chain[:,i], bins=50, density=True, label=\"MCMC samples\")\n", | |
" ax.plot(xspace, np.array(list(map(pdf, xspace))) / Z)\n", | |
" ax.text(0.8, 0.3, r'$\\beta={}$'.format(temp), transform=ax.transAxes)\n", | |
" ax.text(0.05, 0.3, 'replica {}'.format(i), transform=ax.transAxes)\n", | |
" ax.set_yticks(())\n", | |
" plt.show()\n", | |
"\n", | |
"# temperatures = [0.1, 0.4, 0.7, 0.9, 1.0] \n", | |
"temperatures = [0.1, 0.7, 1.0] \n", | |
"plot_RE_samples([], mixture_log_prob, temperatures)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"I hope you agree that the distribution on top looks somewhat easier to sample.\n", | |
"Now how are these exchanges exactly performed? Well, an exchange is an exchange: if, after $k$ steps of sampling, the Markov chain $i$ is in state state $x^i_k$ and the Markov chain $j$ is in state $x^j_k$, then an exchange of states between the two chains leads to the next state of chain $i$ being in state $x^i_{k+1}=x^j_k$, while chain $j$ will assume $x^j_{k+1}=x^i_k$ as its next state. Of course you just can't switch states like that, because the exchanged states will not be drawn from the right distribution. So what do we do if we have a proposal state which is not from the correct distribution? We use a Metropolis criterion to conditionally accept / reject it, thus making sure that detailed balance is maintained. So the probability to accept the exchange is the probability to accept the new proposal state in chain $i$ times the probability to accept the proposal state in chain $j$. Waaay too much words, so here's the math: $$p^\\mathrm{RE}_\\mathrm{acc} (x^i_{k+1}=x^j_k, x^j_{k+1}=x^i_k | x^i_k, x^j_k) = \\mathrm{min}\\left\\{1, \\frac{p_{\\beta_i}(x^j_k)}{p_{\\beta_i}(x^i_k)} \\times \\frac{p_{\\beta_j}(x^i_k)}{p_{\\beta_j}(x^j_k)}\\right\\}$$" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"I love the smell of indices in the morning! We also shouldn't always swap between the same replicas, but alternate, so that all replicas are connected to each other. At the same time, we only want to swap replicas adjacent in the temperature ladder, because acceptance rate will decrease if the distributions are too different. Okay, let's implement this:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 111, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def build_RE_chain(init, stepsizes, n_total, temperatures, swap_interval, log_prob):\n", | |
" \n", | |
" from itertools import cycle\n", | |
" \n", | |
" # a bunch of arrays in which we will store how many\n", | |
" # Metropolis-Hastings / swap moves were accepted\n", | |
" # and how many there were performed in total\n", | |
" accepted_MH_moves = np.zeros(len(temperatures))\n", | |
" total_MH_moves = np.zeros(len(temperatures))\n", | |
" accepted_swap_moves = np.zeros(len(temperatures) - 1)\n", | |
" total_swap_moves = np.zeros(len(temperatures) - 1)\n", | |
" \n", | |
" cycler = cycle((0,1))\n", | |
" chain = [init]\n", | |
" for k in range(n_total):\n", | |
" new_multistate = []\n", | |
" if k > 0 and k % swap_interval == 0:\n", | |
" # perform RE swap\n", | |
" if next(cycler) == 0:\n", | |
" # swap (0,1), (2,3), ...\n", | |
" partners = [(j-1, j) for j in range(1, len(temperatures), 2)]\n", | |
" else:\n", | |
" # swap (1,2), (3,4), ... \n", | |
" partners = [(j-1, j) for j in range(2, len(temperatures), 2)]\n", | |
" for (i,j) in partners:\n", | |
" bi, bj = temperatures[i], temperatures[j]\n", | |
" lpi, lpj = log_prob(chain[-1][i]), log_prob(chain[-1][j])\n", | |
" log_p_acc = min(0, bi * lpj - bi * lpi + bj * lpi - bj * lpj)\n", | |
" if np.log(np.random.uniform()) < log_p_acc:\n", | |
" new_multistate += [chain[-1][j], chain[-1][i]]\n", | |
" accepted_swap_moves[i] += 1\n", | |
" else:\n", | |
" new_multistate += [chain[-1][i], chain[-1][j]]\n", | |
" total_swap_moves[i] += 1\n", | |
" # handle border cases:\n", | |
" if partners[0][0] != 0:\n", | |
" # the first replica didn't participate in a swap, so have it draw\n", | |
" # a Metropolis-Hastings sample\n", | |
" accepted, state = sample_MH(chain[-1][0], lambda x: temperatures[0] * log_prob(x), \n", | |
" stepsizes[0])\n", | |
" new_multistate = [state] + new_multistate\n", | |
" accepted_MH_moves += accepted\n", | |
" total_MH_moves[0] += 1\n", | |
" if partners[-1][1] != len(temperatures) - 1:\n", | |
" # the last replica didn't participate in a swap, so have it draw\n", | |
" # a Metropolis-Hastings sample\n", | |
" accepted, state = sample_MH(chain[-1][-1], lambda x: temperatures[-1] * log_prob(x),\n", | |
" stepsizes[-1])\n", | |
" new_multistate = new_multistate + [state]\n", | |
" accepted_MH_moves[0] += accepted\n", | |
" total_MH_moves[-1] += 1 \n", | |
" else:\n", | |
" # perform sampling in single chains\n", | |
" for j, temp in enumerate(temperatures):\n", | |
" accepted, state = sample_MH(chain[-1][j], lambda x: temp * log_prob(x), stepsizes[j])\n", | |
" accepted_MH_moves[j] += accepted\n", | |
" total_MH_moves[j] += 1\n", | |
" new_multistate.append(state)\n", | |
" chain.append(new_multistate)\n", | |
" \n", | |
" # calculate acceptance rates\n", | |
" MH_acceptance_rates = accepted_MH_moves / total_MH_moves\n", | |
" swap_acceptance_rates = accepted_swap_moves / total_swap_moves\n", | |
" \n", | |
" return MH_acceptance_rates, swap_acceptance_rates, np.array(chain)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Phew, now that was quite someting. Before we can run this beast, we have to set stepsizes for all the single Metropolis-Hastings samplers:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 113, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"stepsizes = [3.0, 2.0, 1.5]" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's first run the 3 Metropolis-Hastings samplers independently by setting the `swap_interval` argument of the above function to something bigger than `n_total`, meaning that no swap will be attempted:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 114, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"MH acceptance rates: 0: 0.786 1: 0.691 2: 0.709 \n", | |
"Swap acceptance rates: 0<->1: nan, 1<->2: nan\n" | |
] | |
}, | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"/home/simeon/.local/lib/python3.7/site-packages/ipykernel_launcher.py:63: RuntimeWarning: invalid value encountered in true_divide\n" | |
] | |
} | |
], | |
"source": [ | |
"MH_acc_rates, swap_acc_rates, chain = build_RE_chain(np.random.uniform(low=-3, high=3, \n", | |
" size=len(temperatures)),\n", | |
" stepsizes, 10000, temperatures, 500000000, \n", | |
" mixture_log_prob)\n", | |
"print(\"MH acceptance rates: \" + \"\".join([\"{}: {:.3f} \".format(i, x) for i, x in enumerate(MH_acc_rates)]))\n", | |
"print(\"Swap acceptance rates: \" + \"\".join([\"{}<->{}: {:.3f}, \".format(i, i+1, x)\n", | |
" for i, x in enumerate(swap_acc_rates)])[:-2])" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"We now use the `plot_RE_samples` function to visualize what we just did - running 5 uncoupled Metropolis-Hastings samplers:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 115, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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4cN11FFH+XZMmZAoKPXbhSUG0IFeOgW0evVq1axZ0+Ho14VubAB9SbqFJGVlZd4TxmbNmnkXGbvAnHLKKTrrrLN0yy23JLt58kMAAhDICAI9evTQQQcdpBtvvNE7nvfff1/9+vXzRmT82F544QXv2vrQQw95BtNGdV566SX9+OOPatKkSZVNvP322/rss8+022676ZhjjsEA+tEZGZgDA+jQKak0gDYs26lTJ+8J2TPPPKPOnTt7T8/sQnHFFVdoxIgRWrVqlXbffXfddddd3p2ebTfccIM3tGtP22666SYtXLhQffv29e44i4qKvH2qGwK2vP/85z/13HPPad68eWrVqpWuvvpqnXHGGTIzd/bZZ2vkyJHeHWbr1q11/vnn6+KLL97ixcWOYc6cOWratKm3n12g/vGPf2j+/Pm+GVuHriUUAhCAQFIJlJSUqF69evr88889c2abXcvtqZtd2/3YLO8ee+yh+++/30sXiUS86/eFF16oq666KqYmQqEQBjAmUtm3EwbQoc9SbQDtwmBGzoyXbTvssIN69+6t2rVrewbNDN3DDz8sm1MyZcoUNWjQwDOANqfELgR33HGH7Jgt3oYAnn322ZgM4HHHHacxY8bonnvu8Yzl9OnTtWDBAtnv16xZ4xnLww8/XA0bNvQuZmYIn3zySfXv33+zdO1YX3vtNU2YMKHy/y1n+/btNX78eO26664OvUIoBCAAgcwn8Omnn6pXr14yI2jGzG6w7ebZhls3ngNoIyPVjY58//333g14xWajRHXq1NHLL7+8wfCtDTnbgwN7aBDLhgGMhVJ27oMBdOi3VBtAa88MUsVmFxC7UNhTuVq1alX+frvtttOVV17pGTEzgGbQZs6cqRYtWnj7vPPOO17cL7/84g3DbukJoBlJM5o2NGFDFbFsF1xwgfc00C48m9vsuOx43n333cr/Xr58uerWrau33npLhxxySCzNsA8EIACBrCVgT+Xsht1GP3r27Omdhw232hBtOBze4LxsXrZ9bWlr27btBnOobYTFrvl2U969e/fKUKsNo0aN0pdffhkTOwxgTJiycicMoEO3pdoAdujQwRu6rdhscu9FF13kPQFcf1uxYoU3lHDrrbd6BvDpp5/WtGnTKndZsmSJN/Tw8ccfa7/99tuiAXzxxRd14oknynLaiyGb2+w4nnjiCdmLHbaf3Xna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i92lsz5M/Jgf6inN/U7q95TU8ejE8xCZtQSo3xhAJ/EiICd8BEOgWgIbG8BqA6rYAQOYKLkMi3v7H9KXD0l7nScdMijxg3vjUul/T0jdL5AOvjnxPERmLQHqNwbQSbxBEdBpp52mxYsX69VXX/V47L///uratavuvvtuJz4EQ8CVAAbQlWDA4h89QPplnHTs41LnvyR+cl8/K404X2rdXfrbO4nn8SHyqaee0m233aYZM2aoTZs2Gjx4sA47jDfafUC7xRRBqd8unELRqM2qZUuEQFAEtLEBXLRokWrUqKGCgoJEsMQUc9FFF+mzzz7TxIkTtdNOO2nChAkxxbFTbhHAAOZWf2/xbNeslAa2lCJrpIu/keq3TRzO/CnSA3tI+VuVzyXMq5F4LofIV155RaeeeqoeffRR7bXXXrr33ntlvysuLnbIumHoAw88oNtvv11z585Vly5ddN9992nPPffcYv62bdtq5syZm+xz/vnny/IFYQtK/XbpCwygA710Cmj16tWqWbOmw9GvC93YAPqStJokZgB32GEHffnll/r2228xgKmAnoVt+GUA1z91hoOzUAh2yMVjpcd7S3UbS1f8JIVCiZ9IJCLd1lZauUQ6e5TUvGviuRwie/TooYMOOkg33nijl+X9999Xv379vBEZP7YXXnhBp5xyih566CHPYNqozksvvaQff/xRTZo0qbKJ+fPnq6ysrPL/7Ua9d+/e+uijj7wRoiBs6azfmcIPA+jQE6kUkP3RderUSfn5+XrmmWfUuXNn74/RLhRXXHGFRowYoVWrVmn33XfXXXfd5d3p2XbDDTd4Q7vnnXeebrrpJi1cuFB9+/b17jiLioq8faobAra8//znP/Xcc89p3rx5atWqla6++mqdccYZ3kXi7LPP1siRI707zNatW8vuEi+++OKYyFYcH08AY8IV+J2SYfg2hoYBzFIZjfmP9O7V0vaHSCcOdT+JIUdLU0dKhw6W9jzLPV+cGUpKSlSvXj19/vnnnjmzza7l48aN867tfmyWd4899tD999/vpYtEIt71+8ILL9RVV10VcxOXXHKJ3njjDf30008KuRjvmFtM/o6prN/JP5vEWsAAJsbNi0qlgMwA2oXBjJwZL9vsCZrdldWuXdszaGboHn74YdmckilTpqhBgwaeAbQ5JXYhuOOOO7xjtngbAnj22WdjMoDHHXecxowZo3vuucczltOnT9eCBQtkv1+zZo1nLA8//HA1bNjQu5iZIXzyySfVv3//auliAKtFFPgdUmH61oeIAcxSSb10ujRpmHTAddK+A9xP4qNbpFG3Sl1OkI5+yD1fnBk+/fRT9erVS2YEzZjZDbbdPA8fPnyTOYC33HKL7GtL2/fff+/dgFdsNkpUp04dvfzyyzrqqKMqf29DzvbgwB4axLJZnubNm+uyyy7TNddcE0tIVuyTyvqdqUAwgA49k0oBmQG09saPH195xHYBscnC9lSuVq1162Ftt912uvLKKz0jZgbLDJrN52jRony9q3feeceL++WXX9SsWbMtPgE0I2lG04YmbKgilu2CCy7wngbahae6DQNYHaHg/3+qDeDGRDGEWaKxuztLi2dJp4yQ2vswDDnlPem5flLD7aQLx6Ucgj2Vsxt2G57t2bOn1/4xxxzjDdGGw+ENjsfmZdvXljabt2cjRBXbnDlzvGu+3ZR379698vdWG0aNGuVNv4lle/HFF3XiiSdq1qxZnhEMypbK+p2pzDCADj2TSgGZAezQoYM3dFux2WRcm0tnTwDX31asWOENJdx6662eAXz66ac1bdq0yl2WLFniDT18/PHH2m+//bZoACv++C2nvRiyuc2O44knnvAuELaf3THaW8Rjx46tli4GsFpEgd8BAxj4LnY/wWXzpMEdJIWkq2ZJWxW651y+SLqtXXmeK6dLdRq454wjw5lnnumNoNhLGZMnT/Zeirvuuus0YMAA77rtuvllAA8++GBvvvnrr7/uekgZFZ/K+p1RJ77ewWAAHXomlQLa3NIsZvDs4mFGbuPNDF6jRo2cDaD90R999NGesducARw6dKhOP/10b3jZ7jLtzWF748zuLmOZ14cBdBBgQEIxgAHpyGSexuS3pKEnSI13kv7+hX8t3dtNWjRV+usrUofYRjj8atzma5988skbzJc+99xzvdGat99+e4Nm0jUEbMfSvn17DRs2TEceeaRfp54ReVJZvzPihDdzEBhAh55JpYA2ZwBtWPaQQw7Rzz//LHv8v7mtYgh4/cf37777rg499NCYhoBtbSq7ALz33nubHQK2ycQ29+TDD9etym9DxTZHEAPoIK4cCsUA5lBnJ3qqH/5b+uQOadeTpCN9XIZk2NnSty9I+10l9bo60aOLO660tNS7WbZ5eH369KmM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\" width=\"640\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"plot_RE_samples(chain[100:], mixture_log_prob, temperatures)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"We find, as expected, that the replicas with lower inverse temperature are sampled much more exhaustively, while the Metropolis-Hastings sampler struggles for $\\beta=1$ and perhaps already $\\beta = 0.9$.\n", | |
"Now we couple the chains by replacing every 5th Metropolis-Hastings step by an exchange step:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 116, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"MH acceptance rates: 0: 0.827 1: 0.609 2: 0.546 \n", | |
"Swap acceptance rates: 0<->1: 0.430, 1<->2: 0.812\n" | |
] | |
}, | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support. ' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" if (mpl.ratio != 1) {\n", | |
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", | |
" }\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" fig.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var backingStore = this.context.backingStorePixelRatio ||\n", | |
"\tthis.context.webkitBackingStorePixelRatio ||\n", | |
"\tthis.context.mozBackingStorePixelRatio ||\n", | |
"\tthis.context.msBackingStorePixelRatio ||\n", | |
"\tthis.context.oBackingStorePixelRatio ||\n", | |
"\tthis.context.backingStorePixelRatio || 1;\n", | |
"\n", | |
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width * mpl.ratio);\n", | |
" canvas.attr('height', height * mpl.ratio);\n", | |
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option);\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'] / mpl.ratio;\n", | |
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", | |
" var x1 = msg['x1'] / mpl.ratio;\n", | |
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x * mpl.ratio;\n", | |
" var y = canvas_pos.y * mpl.ratio;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" var width = fig.canvas.width/mpl.ratio\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var width = this.canvas.width/mpl.ratio\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>');\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" event.shiftKey = false;\n", | |
" // Send a \"J\" for go to next cell\n", | |
" event.which = 74;\n", | |
" event.keyCode = 74;\n", | |
" manager.command_mode();\n", | |
" manager.handle_keydown(event);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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BBAoEQKdOnXSSSedpHvuuccZz7Rp03Teeec5V2QisUycONE5tj777LNOgWmu6rz99ttaunSpatasmW8XH3/8sebOnaujjjpK55xzDgVgJHZGCWyDAtBip8SyADSXZVu2bOmcIXv99dfVqlUr5+yZOVDcdtttev/995WZmam2bdvq0UcfdT7pmeXuu+92Lu2as23Dhw/Xxo0b1aNHD+cTZ6VKlZx1CrsEbNq988479eabb2rdunWqX7++Bg8erL59+8oUc1dddZW++OIL5xNmgwYNdN1116l///4FHlzMGNasWaNatWo565kD1O23367169dHrLC12LWEIoAAAlEV2LZtmypXrqyvvvrKKc7MYo7l5qybObZHYjHttmvXTk8++aTTXCAQcI7fN954owYNGhRWFx6PhwIwLKnStxIFoMU+i3UBaA4MppAzhZdZmjdvrm7duqls2bJOgWYKuueee07mnpJly5apatWqTgFo7ikxB4JHHnlEZswm3lwCeOONN8IqAC+44ALNmzdPjz/+uFNY/vnnn9qwYYPM77Ozs53C8owzzlC1atWcg5kpCMeOHavzzz9/v7pmrB988IF++OGHvL83bTZu3Fjff/+9jjzySIu9QigCCCBQ8gXmzJmjrl27yhSCpjAzH7DNh2dzuXXfewDNlZHCro788ssvzgfw3MVcJSpXrpwmTZq01+Vbc8nZnDgwJw3CWSgAw1EqnetQAFrst1gXgKY/UyDlLuYAYg4U5qxccnJy3u+bNGmigQMHOoWYKQBNgbZixQrVrVvXWeeTTz5x4lavXu1chi3oDKApJE2haS5NmEsV4Sw33HCDczbQHHj2t5hxmfF8+umneX+9Y8cOlS9fXh999JFOO+20cLphHQQQQKDUCpizcuYDu7n6ceyxxzrbYS63mku0Xq93r+0y92Wbn4KW1NTUve6hNldYzDHffCjv2LFjXqiZG2bNmqVvvvkmLDsKwLCYSuVKFIAWuy3WBWDTpk2dS7e5i7m596abbnLOAO657Ny507mU8MADDzgF4Kuvvqrly5fnrZKRkeFcepg5c6a6dOlSYAH41ltvqVevXjJtmgdD9reYcbz88ssyD3aY9cwnT3OP3/z58ykALfKLUAQQOHAF+vXr51xBMQ9lLFmyxLnnbujQoRowYIBz3LZdKAALFozl/G27L6MVTwFoIRvLBNrf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b4QnGAwGbRqI59gDJYH2LQA3bdqkxMREVawYvaffbrrpJs2dO1c//fSTDj30UP3www/xnEpsOwIIREpg9wMgqlhHqesfLnKrTgFolhg8CPLOO++od+/eeuGFF9ShQweNGTNG5nfp6elFHnd+AU899ZQeeughrV27VkcccYSeeOIJtW/fvsD2U1NTtWLFiv+sc91118m0dyAsB8r8bbMvKAAt9NxMoKysLCUlJVmM/t/QfQvAiDRaSCOmAGzevLm++eYbLV68mAIwFuj0gUA8COx+AETNT1fqokuKvMV5BeCEi0PfCBLFB0E6deqkk046Sffcc48zzmnTpum8885zrshEYpk4caIuu+wyPfvss06Baa7qvP3221q6dKlq1qyZbxfr16+X3+/P+3vzQb1bt26aMWOGc4XoQFjcnL9Lih8FoMWeiGUCmX90LVu2VEJCgl5//XW1atXK+cdoDhS33Xab3n//fWVmZqpt27Z69NFHnU96Zrn77rudS7vXXnuthg8fro0bN6pHjx7OJ85KlUKXRgq7BGzavfPOO/Xmm29q3bp1ql+/vgYPHqy+ffs6B4mrrrpKX3zxhfMJs0GDBjKfEvv37x+WbO |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment