Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save UnkindPartition/5ccaeee166b45be13a8e375f980f405a to your computer and use it in GitHub Desktop.

Select an option

Save UnkindPartition/5ccaeee166b45be13a8e375f980f405a to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"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": [
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment