diff --git a/AnalyzeExp.ipynb b/AnalyzeExp.ipynb new file mode 100644 index 0000000..bb61db8 --- /dev/null +++ b/AnalyzeExp.ipynb @@ -0,0 +1,78 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "outputs": [], + "source": [ + "from load import pkl_load" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "Can't get attribute 'simple_cell_model' on ", + "output_type": "error", + "traceback": [ + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)", + "Input \u001B[0;32mIn [2]\u001B[0m, in \u001B[0;36m\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0m model_center_fp \u001B[38;5;241m=\u001B[39m \u001B[43mpkl_load\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43msimple_cell_center_fp\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\n", + "File \u001B[0;32m~/dev/amgen/load.py:6\u001B[0m, in \u001B[0;36mpkl_load\u001B[0;34m(filename)\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mpkl_load\u001B[39m(filename):\n\u001B[1;32m 5\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m \u001B[38;5;28mopen\u001B[39m(filename, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mrb\u001B[39m\u001B[38;5;124m'\u001B[39m) \u001B[38;5;28;01mas\u001B[39;00m f:\n\u001B[0;32m----> 6\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mpickle\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mload\u001B[49m\u001B[43m(\u001B[49m\u001B[43mf\u001B[49m\u001B[43m)\u001B[49m\n", + "\u001B[0;31mAttributeError\u001B[0m: Can't get attribute 'simple_cell_model' on " + ] + } + ], + "source": [ + "model_center_fp = pkl_load('simple_cell_center_fp')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/Decoded_Orientation.pdf b/Decoded_Orientation.pdf deleted file mode 100644 index 838097b..0000000 Binary files a/Decoded_Orientation.pdf and /dev/null differ diff --git a/NoisyCircle.ipynb b/NoisyCircle.ipynb deleted file mode 100644 index e5e12c0..0000000 --- a/NoisyCircle.ipynb +++ /dev/null @@ -1,358 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# Topological analysis of a noisy circle\n", - "_Test application for the topological tools_" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "**Imports**" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 13, - "outputs": [], - "source": [ - "import numpy as np\n", - "from matplotlib import pyplot as plt\n", - "from model import persistence, decoding\n", - "import pandas as pd\n", - "%matplotlib inline" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "**Parameters**" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 2, - "outputs": [], - "source": [ - "N = 20\n", - "R = 1\n", - "SIGMA = 0.1" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "markdown", - "source": [ - "### Generating the circle" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%% md\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 3, - "outputs": [ - { - "data": { - "text/plain": "(20, 2)" - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "phase = np.linspace(0, 2 * np.pi, N)\n", - "points = np.array([np.cos(phase), np.sin(phase)]).swapaxes(0, 1) * R\n", - "points.shape" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 4, - "outputs": [], - "source": [ - "noise = np.random.normal(scale=SIGMA, size=points.shape)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 5, - "outputs": [ - { - "data": { - "text/plain": "" - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "text/plain": "
", - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "points_no_noise = points\n", - "points += noise" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 9, - "outputs": [ - { - "data": { - "text/plain": "" - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "text/plain": "
", - "image/png": "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\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.scatter(points[:, 0], points[:, 1])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 8, - "outputs": [ - { - "data": { - "text/plain": "
", - "image/png": "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\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "persistence.persistence(points)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 14, - "outputs": [], - "source": [ - "dataset = pd.DataFrame(points, columns=['x', 'y'])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 7, - "outputs": [ - { - "data": { - "text/plain": "
", - "image/png": "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\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "persistence.persistence(points_no_noise)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 15, - "outputs": [ - { - "data": { - "text/plain": "
", - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Decoding... done\n" - ] - }, - { - "data": { - "text/plain": "(20, 1)" - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "param = decoding.cohomological_parameterization(dataset)\n", - "param.shape" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 19, - "outputs": [ - { - "data": { - "text/plain": "Text(0, 0.5, 'Cohomological parameter')" - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "text/plain": "
", - "image/png": "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\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(np.roll(np.arctan2(points[:, 0], points[:, 1]), 5), np.roll(param, 5))\n", - "plt.xlabel('Phase')\n", - "plt.ylabel('Cohomological parameter')" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/TestInteractivity.ipynb b/TestInteractivity.ipynb deleted file mode 100644 index 7d2c09e..0000000 --- a/TestInteractivity.ipynb +++ /dev/null @@ -1,311 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "outputs": [ - { - "data": { - "text/plain": "", - "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.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(\n '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\nmpl.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 = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(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 (fig.ratio !== 1) {\n fig.send_message('set_device_pixel_ratio', {\n device_pixel_ratio: fig.ratio,\n });\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\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\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\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.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,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n fig.rubberband_canvas.style.cursor = msg['cursor'];\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.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.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\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 img.type = 'image/png';\n }\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\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\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(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\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(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.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 }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\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 * https://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\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\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.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\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar 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.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\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 var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.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 = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\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;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\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.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.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 / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.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 () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('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 } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.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.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "", - "text/html": "
" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "interactive(children=(FloatSlider(value=1.0, description='w', max=3.0, min=-1.0), Output()), _dom_classes=('wi…", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "fa96235a53da4c98bb4bbef6541cd4ca" - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "%matplotlib notebook\n", - "from ipywidgets import *\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "x = np.linspace(0, 2 * np.pi)\n", - "fig = plt.figure()\n", - "ax = fig.add_subplot(1, 1, 1)\n", - "line, = ax.plot(x, np.sin(x))\n", - "\n", - "def update(w = 1.0):\n", - " line.set_ydata(np.sin(w * x))\n", - " # fig.plot(x, np.sin(w * x))\n", - " fig.canvas.draw_idle()\n", - "\n", - "interact(update);" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 3, - "outputs": [], - "source": [ - "import seaborn as sns\n", - "import matplotlib.pyplot as plt\n", - "mpg = sns.load_dataset('mpg')\n", - "def f(x):\n", - " img = sns.countplot(mpg[mpg['mpg']" - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "interact(f, x=20)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 4, - "outputs": [ - { - "data": { - "text/plain": "", - "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.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(\n '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\nmpl.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 = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(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 (fig.ratio !== 1) {\n fig.send_message('set_device_pixel_ratio', {\n device_pixel_ratio: fig.ratio,\n });\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\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\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\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.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,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n fig.rubberband_canvas.style.cursor = msg['cursor'];\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.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.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\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 img.type = 'image/png';\n }\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\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\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(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\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(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.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 }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\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 * https://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\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\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.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\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar 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.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\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 var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.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 = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\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;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\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.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.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 / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.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 () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('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 } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.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.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "", - "text/html": "
" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax = plt.subplots()\n", - "img = ax.imshow([[0, 1], [2, 3]])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 5, - "outputs": [], - "source": [ - "img?" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 8, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'list' object has no attribute 'items'", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)", - "Input \u001B[0;32mIn [8]\u001B[0m, in \u001B[0;36m\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[43mimg\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mupdate\u001B[49m\u001B[43m(\u001B[49m\u001B[43m[\u001B[49m\u001B[43m[\u001B[49m\u001B[38;5;241;43m0\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m1\u001B[39;49m\u001B[43m]\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m[\u001B[49m\u001B[38;5;241;43m2\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m5\u001B[39;49m\u001B[43m]\u001B[49m\u001B[43m]\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m/usr/lib/python3.10/site-packages/matplotlib/artist.py:1056\u001B[0m, in \u001B[0;36mArtist.update\u001B[0;34m(self, props)\u001B[0m\n\u001B[1;32m 1054\u001B[0m ret \u001B[38;5;241m=\u001B[39m []\n\u001B[1;32m 1055\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m cbook\u001B[38;5;241m.\u001B[39m_setattr_cm(\u001B[38;5;28mself\u001B[39m, eventson\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mFalse\u001B[39;00m):\n\u001B[0;32m-> 1056\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m k, v \u001B[38;5;129;01min\u001B[39;00m \u001B[43mprops\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mitems\u001B[49m():\n\u001B[1;32m 1057\u001B[0m \u001B[38;5;66;03m# Allow attributes we want to be able to update through\u001B[39;00m\n\u001B[1;32m 1058\u001B[0m \u001B[38;5;66;03m# art.update, art.set, setp.\u001B[39;00m\n\u001B[1;32m 1059\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m k \u001B[38;5;241m==\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124maxes\u001B[39m\u001B[38;5;124m\"\u001B[39m:\n\u001B[1;32m 1060\u001B[0m ret\u001B[38;5;241m.\u001B[39mappend(\u001B[38;5;28msetattr\u001B[39m(\u001B[38;5;28mself\u001B[39m, k, v))\n", - "\u001B[0;31mAttributeError\u001B[0m: 'list' object has no attribute 'items'" - ] - } - ], - "source": [ - "img.update([[0, 1], [2, 5]])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 10, - "outputs": [ - { - "data": { - "text/plain": "['_A',\n '_PROPERTIES_EXCLUDED_FROM_SET',\n '__class__',\n '__delattr__',\n '__dict__',\n '__dir__',\n '__doc__',\n '__eq__',\n '__format__',\n '__ge__',\n '__getattribute__',\n '__getstate__',\n '__gt__',\n '__hash__',\n '__init__',\n '__init_subclass__',\n '__le__',\n '__lt__',\n '__module__',\n '__ne__',\n '__new__',\n '__reduce__',\n '__reduce_ex__',\n '__repr__',\n '__setattr__',\n '__sizeof__',\n '__str__',\n '__subclasshook__',\n '__weakref__',\n '_agg_filter',\n '_alpha',\n '_animated',\n '_axes',\n '_callbacks',\n '_check_unsampled_image',\n '_clipon',\n '_clippath',\n '_cm_set',\n '_default_contains',\n '_extent',\n '_filternorm',\n '_filterrad',\n '_get_clipping_extent_bbox',\n '_get_scalar_alpha',\n '_gid',\n '_id_norm',\n '_imcache',\n '_in_layout',\n '_interpolation',\n '_interpolation_stage',\n '_label',\n '_make_image',\n '_mouseover',\n '_norm',\n '_path_effects',\n '_picker',\n '_rasterized',\n '_remove_method',\n '_resample',\n '_rgbacache',\n '_scale_norm',\n '_set_alpha_for_array',\n '_set_gc_clip',\n '_sketch',\n '_snap',\n '_stale',\n '_sticky_edges',\n '_transform',\n '_transformSet',\n '_update_set_signature_and_docstring',\n '_url',\n '_visible',\n 'add_callback',\n 'autoscale',\n 'autoscale_None',\n 'axes',\n 'callbacks',\n 'callbacksSM',\n 'can_composite',\n 'changed',\n 'clipbox',\n 'cmap',\n 'colorbar',\n 'contains',\n 'convert_xunits',\n 'convert_yunits',\n 'draw',\n 'figure',\n 'findobj',\n 'format_cursor_data',\n 'get_agg_filter',\n 'get_alpha',\n 'get_animated',\n 'get_array',\n 'get_children',\n 'get_clim',\n 'get_clip_box',\n 'get_clip_on',\n 'get_clip_path',\n 'get_cmap',\n 'get_cursor_data',\n 'get_extent',\n 'get_figure',\n 'get_filternorm',\n 'get_filterrad',\n 'get_gid',\n 'get_in_layout',\n 'get_interpolation',\n 'get_label',\n 'get_path_effects',\n 'get_picker',\n 'get_rasterized',\n 'get_resample',\n 'get_size',\n 'get_sketch_params',\n 'get_snap',\n 'get_tightbbox',\n 'get_transform',\n 'get_transformed_clip_path_and_affine',\n 'get_url',\n 'get_visible',\n 'get_window_extent',\n 'get_zorder',\n 'have_units',\n 'is_transform_set',\n 'make_image',\n 'mouseover',\n 'norm',\n 'origin',\n 'pchanged',\n 'pick',\n 'pickable',\n 'properties',\n 'remove',\n 'remove_callback',\n 'set',\n 'set_agg_filter',\n 'set_alpha',\n 'set_animated',\n 'set_array',\n 'set_clim',\n 'set_clip_box',\n 'set_clip_on',\n 'set_clip_path',\n 'set_cmap',\n 'set_data',\n 'set_extent',\n 'set_figure',\n 'set_filternorm',\n 'set_filterrad',\n 'set_gid',\n 'set_in_layout',\n 'set_interpolation',\n 'set_interpolation_stage',\n 'set_label',\n 'set_norm',\n 'set_path_effects',\n 'set_picker',\n 'set_rasterized',\n 'set_resample',\n 'set_sketch_params',\n 'set_snap',\n 'set_transform',\n 'set_url',\n 'set_visible',\n 'set_zorder',\n 'stale',\n 'stale_callback',\n 'sticky_edges',\n 'to_rgba',\n 'update',\n 'update_from',\n 'write_png',\n 'zorder']" - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dir(img)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 11, - "outputs": [], - "source": [ - "img.set_data?" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 14, - "outputs": [], - "source": [ - "img.set_data([[0, 1], [2, 2]])" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 15, - "outputs": [], - "source": [ - "fig.canvas.draw_idle()" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 16, - "outputs": [ - { - "data": { - "text/plain": "matplotlib.image.AxesImage" - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(img)" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/TuningCurvesFull.ipynb b/TuningCurvesFull.ipynb deleted file mode 100644 index c7ca555..0000000 --- a/TuningCurvesFull.ipynb +++ /dev/null @@ -1,560 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "# Tuning Curves" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "**Import everything**" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "%matplotlib notebook\n", - "import typing\n", - "\n", - "import matplotlib as mpl\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import ipywidgets as widgets\n", - "import sympy as sp\n", - "sp.init_printing()\n", - "AxOrImg = typing.Union[mpl.axes.Axes, mpl.image.AxesImage]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "k, x0, y0, phi, theta, sigma_x, sigma_y, sigma, x, y = sp.symbols(r'k x_0 y_0 \\phi \\theta \\sigma_x \\sigma_y \\sigma x y')\n", - "defaults = {\n", - " k: 6,\n", - " sigma: 0.2,\n", - " phi: sp.pi / 2,\n", - " theta: 0,\n", - " sigma: 1,\n", - " x0: 0, y0: 0\n", - "}\n", - "sigma_x = sigma_y = sigma" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "#### Formulas" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "data": { - "text/plain": "cos(\\phi + k⋅(x - x₀)⋅cos(\\theta) + k⋅(y - y₀)⋅sin(\\theta))", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZUAAAAVCAYAAACOnHNXAAAACXBIWXMAAA7EAAAOxAGVKw4bAAALeklEQVR4Ae2d65XUNhTHhz1bAIEKAh3wqCDQAYEK2HQAh0/wjUM6SKiAQAchFSTQAXTAZjsg/59G19F4JEu2JY8H9p4jZOtx9b8PXT08wObr16+btaXnz5/fWBum1nguZV6fH+ZsPtVmU/vl8Byy/luUKaXPY5R1KuZcv1j9yWZl9OLFiyeCdGtlsJrC+R5l9gq94WVvqt8WzGfa7Gjljulypi5iLNdedlT2m2mfnKx79VdYjddCEv6BsNxV/rQEk9qdKf1e0rZFG419VXzfKt1ROtf7zbHjqM8omcfyX3t7yc8m4kL5wew4VkclNvNyXfe83+j9YziOrz+o3MJw6b+hUUY817Kf+NzQsB+Unuq5+hwQz2x88bJM9lXfv/Pl1ZxUBAwHf6a8aEHx9h8dxH2/Kpmwosj7YvZZ6f1YphNlHjvMqttLB78K4C9eF6vGCrgSm6nNn2qKb+DLL5XYeOyQ6g4utzBc+u+OVcpfKtqPRYXYd7t89LKWwpiNqWoz21f7uljNoiI1vVL6rUxdXasv3dOEBynjntLZhK79LlzXYZyxNEXmsWMcQ3vsji6OgQZtJn9ycih3u07lFxKKK4LYle4sucXz0n8P6zGz7Ad02ZDN6A/Kf2kgypK+2uliTYvKQym2+PintvdkhJ0rhQlGYSUnTSaPg/6jTyrqM0rmySBX3tHbHV3MssVCYiZtJvwsHFznxRbIG318FeS+9N++Uhd8r2A/h1Z8LhrBXsxXQ12sYlERIO79uEIaQ7fUb0ogHzNGSVuuv7hGGOUYE2UuwXOsbbD/wzWDL7DZM+H/HPqlnm2hvJaQ7dByX/pvwjCFxYe2XxTmgXzV6eLUEHnnZ4cVBse/Vf4u0uaTL+Obxlu16YK7ntmtcYqAj02oRyofujPEsTseeo6S5/1IlVx73dc77cDAR64QN+VLEbJ22IXDfRhTGbIP4RqUWXzob1dzd/X8WIndLvJDO7bZFrX9szEmdIhOBk+rHsPq/NTjwvb9b4J3vFWYcDEqkjvWsVJZdf8N/IS5+UHvnU193WvlP1fCn2XTGM+g/TR2Mh6qjvnM9zby96YT3+e1L/9DOf6Ob0HEAuY+3+SGKBlf1JfY0sJXnS5OQaVBEIpfIDzWs1tElONsLBjsvD4qoRwUQDDvJoie/6RO6VclwL5SjkAd6T13X8jE404uSeLh6o2X8o0SYzrsyndwJRlVrNCYyIteDJsZnnf0SeomlJ5DysmMHp3elHOlgpPhTCxU8MfRugVfz0tQS0xsVPC5JEnuNfupbQCYWCHhH1A3Z7av3Z9ZubuWlR+kz1b+635wI/7MB/w2nAOcRm2eVJYoya4lnqT9vH6T8VD1+MRt5cSJjvTOtT7l8L6m5OIrDVSGP7FQv1NK+RRNh+JLK191unCLigCwWLB4hEEKh4MutplrExOEnRlCMplQwB09X1WyfnR3QZeHBDHWeaJuI17g4ye7FmQJPm7yqsyuGwiyi+1+PFYLguwy3LNydIThMXg/wPhuLkvKrP4YHXmM0CUTkdMKhJ7DelfY8o8pmNTHMH4Rtut67+/iQ8jYn0VjiNbsp3aCJICFMmA3rkdTAaBE7pBfzefq/is58f2/PUg2l/15ndxB1xTMeI3Fo/ZjfJZhhuxHYC+Jh30dGXx85p4wdXFNz2zwqcd24WJNWUjJ+KJGrXzV6eJUABkcR9g5TqmcBcZOLdQz4c1Z9LilQEiuuNhFw/hf5QRUfhFFkN3hve258ydBksC5R+pLgEWBPwaVnErCAEVfmyBBs+2j2rKoxeoZd6P62EkK43XG3HLa+5MJYmP/o/a20H1U+dB1H4ySMqsOXmEQghd4nI4KcG3U5qr6/KVEXko/qx/YYzQKk/hgty/Kne2VMzk41aKzGCFvEqv6UYcf7viSylfhpx5bd4Wh943HzKLykvcEDcpNH/E5Jv91NxteVk4lfdmZh/0y33ybeb3V8t1iPBp3rM8COGk/8cMfpsTDUB//hC/Bc3Ku+DZD8YV51MJXnS5Oxdx2h+wmU2RtLlINVA5QiAD4TInJxKrP8e93pVjgVnWWWDz+UP/c2AgUpdTYKgcjP/fcCVRRJvFCJgjjIjPH1d+UUkE5ziFSGuERm5yRnv8XiQf6yi1s/3fIPE3AhO1/MrbqjxOzqKDvmK2GJgFszAdX56eSxyY4m6iQsBs0tKPMyb0R/+jcUfnq/FeY3DxVztxAL53sKiNGUDZ0gt94HlV81/OCZwmesT4rUQY3h9TXjofwnEzSQ0tfdb58InQ2wfmoliJrY4Bi7dgRuImvnBPLTaUrasiEONMzDpUiVvMUb3h2d47iQzvu7hz5dxwmd8Xme9TJ/Lhge6lnZORqhmvAITnDwYdk7tqJn00Gd2qkQmVcL6b01fVt9ZDDpHr0Aj7zG4NyoYeUfmiPTlJkvNbqp+DubyjYELGhQu4U5eRO9ZtV7v2npf9yyu9O1x4svsxVYF9Ps2Qp7DyIZ6LPMnTSfp7nRvnYeFgo0mCzXHzp26CGrzpdnEhgHJ4B7sQgqv6BEvW027u6UB2OAhFUCRgcITtSPTsVAmKUv29IwMDBY8TYgDVivHCn4z4Eapwu6FrDxrntQg2LHVOdHMLzJDN+VGb1Y8FgR296ZcHq38lzb489FqEJmEJ7hRjP9XItLAieKbeFIyjePnp5V+mnMVuojFMEMoXXtHty+TZJuWMdKpU18d8AG/OgL9ei31MCLDzm8EzxWfgO+e0t1U+Jh/CdS6n4shc3Kvqq08WJR84qzrVEXwEcB5nIEFcZ/GUaFBUSbfgVlgVXAl7fQLxbfdjXnhnjrr308sd6D4///NtgzlmVs5CdKw/re92bvTJB+jsxBrOJdD0zckpmFhMScqG385CPyqjb+7YVtmnwXAsTTtf3DYPLNYH5mpX18zX7KZsaJ5u3m7tK0fNFX4jee4ncvS5VXlv5r4GzeeDevd/iR/0rQmvfOp+KZ8hnwZyzX2k8jM0Lxp5KqfgCv1a+6nRxyggyOFdXfAjn+wfXSHa9xDcCC+AEUDpxlLOJwurPuy0YlHMyOVOZMkfX9Sd8d4zq6yx7owcWiD1SP8blA7Lh4ud1TFiMwNVTLhDt8axU4MY3XsLBaYJdKf+OFfp7aXWJPCUzukSHTMCNeMGPkwvycw3IYrP0qWwsJvMPRAgJnaX8AHkHNweSe7V+KuxsfvBz5gRXdKU/cc/KLV4tqJX/Glbmwmvpg7nKdzC7trRYYe2WynN4pvgs2IfsB89kPJRu2KA/U3K3OHonBuJH+BDl1BNXKCeeUG5zhMWKDTYbrRil4gttW/nqVhep/zNg6XL9u/yflG4Njcu/3a90NtRmTJ14PVB6MqZPzbYaOytzzfGW5CXZ/lXasafevyrt/V85lCl9WhLf1LHAqbQj1wxes+QWjqPxX2F9pfRhqq5q94vhUVmxz4JH7WfZr7ZMfX7Ct5ivhro4iS1xBypjR2OrcAoCK3fNnQ47idQOJYWhZnmJzDXHW5IXOyt2Lo60o+KZn5fHTirsItHFMVBNm82Ve5X+KxtzYuv/uIardXxicRqBZ4zPIsdc+7XWxZK+2uliNYuKDM8xke86HPFS1H1PSTUYU66x+Jkr4x6ECmU+CLa5g0o2fqbNX3gkwPCjBY7pHLt3SHXYG7sfzA47gDIvHmfOTzNc3LXmbLmFZa3+y48U3gRKONSPaQxCER7ps8hnYaq2s+1n4FrlwlgSU7PD52Tt159mOS7bgMDD/eH9xLDczX5rlJP5aOWVs7F7yRHfinIn1ByPpetr2OwY5Y7pOaYL7H5N9mczwbcUvs3WvGGI4RgqK8ZT6LOMdSz2i9lnSFexupysO/Wr+p8fkUZGZQfAz5in/oXEmFJWXfY9yoxBJDdBJ3Ul9s3a7Jjljhnle/PfY7PfHPvkZI3V/wfyW5BPgYgjWgAAAABJRU5ErkJggg==\n", - "text/latex": "$\\displaystyle \\cos{\\left(\\phi + k \\left(x - x_{0}\\right) \\cos{\\left(\\theta \\right)} + k \\left(y - y_{0}\\right) \\sin{\\left(\\theta \\right)} \\right)}$" - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "grating_f = sp.cos(k * (x - x0) * sp.cos(theta) + k * (y - y0) * sp.sin(theta) + phi)\n", - "receptive_field = 1 / (2 * sp.pi * sigma * sigma) * sp.exp(-(x ** 2 + y ** 2) / (2 * sigma ** 2)) * sp.cos(k * x * sp.cos(theta) + k * y * sp.sin(theta) + phi)\n", - "receptive_field = receptive_field.subs(theta, 0).subs(phi, 0)\n", - "grating_f" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "data": { - "text/plain": " 2 2 \n - x - y \n ───────── \n 2 \n 2⋅\\sigma \nℯ ⋅cos(k⋅x)\n───────────────────\n 2 \n 2⋅π⋅\\sigma ", - "image/png": "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\n", - "text/latex": "$\\displaystyle \\frac{e^{\\frac{- x^{2} - y^{2}}{2 \\sigma^{2}}} \\cos{\\left(k x \\right)}}{2 \\pi \\sigma^{2}}$" - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "receptive_field" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "int1 = sp.Integral(grating_f * receptive_field, (x, -1, 1), (y, -1, 1)).subs(defaults)\n", - "# int1.evalf(2).doit()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "data": { - "text/plain": " 2 ⎛ 2 ⎞ \n k ⋅⎝cos (\\theta) + 1⎠ \n ───────────────────── \n 2 ⎛ \nℯ ⋅cos(\\phi - k⋅(x₀⋅cos(\\theta) + y₀⋅sin(\\theta)))⋅cosh⎝\\s\n\n \n \n \n 2 2 ⎞\nigma ⋅k ⋅cos(\\theta)⎠", - "image/png": "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\n", - "text/latex": "$\\displaystyle e^{\\frac{k^{2} \\left(\\cos^{2}{\\left(\\theta \\right)} + 1\\right)}{2}} \\cos{\\left(\\phi - k \\left(x_{0} \\cos{\\left(\\theta \\right)} + y_{0} \\sin{\\left(\\theta \\right)}\\right) \\right)} \\cosh{\\left(\\sigma^{2} k^{2} \\cos{\\left(\\theta \\right)} \\right)}$" - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# p = sp.cos(phi) * sp.cos(k*x0*sp.cos(theta) + k*y0*sp.sin(theta)) * sp.exp(k**2 * (sigma_x **2 * (1 + sp.cos(theta))**2 + sigma_y**2 * sp.sin(theta) **2) / 2)\n", - "p = sp.cosh(k**2 * sigma**2 * sp.cos(theta)) * sp.exp(k ** 2 * (1 + sp.cos(theta) ** 2) / 2) * sp.cos(phi - k * (x0 * sp.cos(theta) + y0 * sp.sin(theta)))\n", - "p" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "#### Plot stuff" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "def get_orientation_phase_grid(step_phase: float, step_orientation: float) -> np.ndarray:\n", - " \"\"\"\n", - " Returns a grid of x and y values for plotting.\n", - " :param step_phase: step for the phase (phi) - in degrees\n", - " :param step_orientation: step for the orientation (theta) - in degrees\n", - " :return: numpy array of shape (n_orientation, n_phase). Each element is a tuple (theta, phi)\n", - " \"\"\"\n", - " # phase <-> phi\n", - " # orientation <-> theta\n", - " step_phase *= np.pi / 180\n", - " step_orientation *= np.pi / 180\n", - " phi = np.arange(0, 2 * np.pi, step_phase)\n", - " theta = np.arange(0, np.pi, step_orientation)\n", - " return np.array(np.meshgrid(theta, phi)).T.reshape(-1, len(phi), 2)\n", - "\n", - "\n", - "def get_spatial_grid(step_x: float, step_y: float, size: float = 1) -> np.ndarray:\n", - " \"\"\"\n", - " Returns a grid of x and y values for plotting.\n", - " :param step_x: step for the x-coordinate\n", - " :param step_y: step for the y-coordinate\n", - " :param size: size of the grid\n", - " :return: numpy array of shape (2 * size / step_x, 2 * size / step_y). Each element is a tuple (x, y)\n", - " \"\"\"\n", - " x = np.arange(-size, size, step_x)\n", - " y = np.arange(-size, size, step_y)\n", - " return np.array(np.meshgrid(x, y)).T.reshape(-1, len(x), 2)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "def eval_func(func: sp.Expr, sub_1: sp.Expr, sub_2: sp.Expr, grid: np.ndarray) -> np.ndarray:\n", - " # return np.array([[float(func.subs(sub_1, x_).subs(sub_2, y_)) for x_, y_ in line] for line in grid])\n", - " func = sp.lambdify([sub_1, sub_2], func, 'numpy')\n", - " return func(grid[:, :, 0], grid[:, :, 1])\n", - "\n", - "def plot_spatial(func: sp.Expr, ax: AxOrImg, step_x: float = 0.05, step_y: float = 0.05, size: float = 1, title: str = None, show: bool = False,\n", - " patch: typing.Optional[typing.Tuple[float, float, float]] = None\n", - " ):\n", - " \"\"\"\n", - " Plots a spatial map of the function.\n", - "\n", - " :param func: function to plot\n", - " :param ax: axes to plot on or the image on axes\n", - " :param step_x: step for the x-coordinate\n", - " :param step_y: step for the y-coordinate\n", - " :param size: size of the grid\n", - " :param title: title of the plot\n", - " :param show: whether to show the plot\n", - " :param patch: optional circle to plot - a tuple (x, y, radius)\n", - " \"\"\"\n", - " grid = get_spatial_grid(step_x, step_y, size)\n", - " image: np.ndarray = eval_func(func, x, y, grid)\n", - " if isinstance(ax, mpl.image.AxesImage):\n", - " ax.set_data(image)\n", - " return ax\n", - " img = ax.imshow(image, extent=[-size, size, -size, size], vmin=-size, vmax=size, cmap='gray')\n", - " ax.invert_yaxis()\n", - " if patch is not None:\n", - " ax.add_patch(plt.Circle(patch[:2], radius=patch[2], color='b', fill=False))\n", - " ax.set_title(title)\n", - " if show:\n", - " plt.show()\n", - " return img\n", - "\n", - "def normalize(img):\n", - " return (img - img.min()) / (img.max() - img.min())\n", - "\n", - "def plot_tuning_curve(func: typing.Union[sp.Expr, typing.Callable], ax: AxOrImg, step_phase: float = 20, step_orientation: float = 15, title: str = None, show: bool = False):\n", - " \"\"\"\n", - " Plots a tuning curve of the function.\n", - "\n", - " :param func: function to plot - sympy or a function of (theta, phi)\n", - " :param ax: axes to plot on or image to update\n", - " :param step_phase: step for the phase (phi) - in degrees\n", - " :param step_orientation: step for the orientation (theta) - in degrees\n", - " :param title: title of the plot\n", - " :param show: whether to show the plot\n", - " \"\"\"\n", - " grid = get_orientation_phase_grid(step_phase, step_orientation)\n", - " if isinstance(func, sp.Expr):\n", - " image: np.ndarray = eval_func(func, theta, phi, grid)\n", - " else:\n", - " image = np.array([[func(theta_val, phi_val) for theta_val, phi_val in line] for line in grid])\n", - " image = normalize(image)\n", - " if isinstance(ax, mpl.image.AxesImage):\n", - " ax.set_data(image)\n", - " return ax\n", - " img = ax.imshow(image, extent=[0, 360, 0, 180], cmap='viridis')\n", - " ax.set_title(title)\n", - " if show:\n", - " plt.show()\n", - " return img" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "def plot_gratings(ax: typing.Union[plt.Axes, mpl.image.AxesImage], k_val: float, phi_val: float, theta_val: float, x0_val: float = 0, y0_val: float = 0):\n", - " phi_val *= np.pi / 180\n", - " theta_val *= np.pi / 180\n", - " return plot_spatial(\n", - " # grating_f.evalf(subs={k: k_val, x0: x0_val, y0: y0_val, theta: theta_val, phi: phi_val}),\n", - " grating_f.subs(k, k_val).subs(x0, x0_val).subs(y0, y0_val).subs(theta, theta_val).subs(phi, phi_val),\n", - " ax, step_x=0.05, step_y=0.05, size=1, title='Grating', show=False)\n", - "\n", - "def plot_receptive_field(ax: plt.Axes, k_val: float, phi_val: float, theta_val: float, sigma_val: float = 1):\n", - " phi_val *= np.pi / 180\n", - " theta_val *= np.pi / 180\n", - " return plot_spatial(\n", - " receptive_field.subs(k, k_val).subs(theta, theta_val).subs(phi, phi_val).subs(sigma, sigma_val),\n", - " ax, step_x=0.05, step_y=0.05, size=1, title='Receptive field', show=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "def get_value(k_val: float = defaults[k], phi_val: float = 90, theta_val: float = 0, x0_val: float = 0, y0_val: float = 0, sigma_val: float = defaults[sigma]):\n", - " phi_val *= np.pi / 180\n", - " theta_val *= np.pi / 180\n", - " grid_size = 0.05\n", - " grid = get_spatial_grid(grid_size, grid_size, 1)\n", - " subs = {\n", - " k: k_val,\n", - " x0: x0_val,\n", - " y0: y0_val,\n", - " theta: theta_val,\n", - " phi: phi_val,\n", - " sigma: sigma_val\n", - " }\n", - " grating_img = eval_func(grating_f.subs(subs), x, y, grid)\n", - " rf_img = eval_func(receptive_field.subs(subs), x, y, grid)\n", - " return np.sum(grating_img * rf_img) * grid_size ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "data": { - "text/plain": "", - "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.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(\n '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\nmpl.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 = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(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 (fig.ratio !== 1) {\n fig.send_message('set_device_pixel_ratio', {\n device_pixel_ratio: fig.ratio,\n });\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\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\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\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.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,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n fig.rubberband_canvas.style.cursor = msg['cursor'];\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.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.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\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 img.type = 'image/png';\n }\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\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\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(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\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(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.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 }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\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 * https://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\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\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.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\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar 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.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\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 var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.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 = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\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;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\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.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.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 / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.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 () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('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 } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.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.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "", - "text/html": "
" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "interactive(children=(FloatSlider(value=6.0, description='k', max=10.0, step=0.2), FloatSlider(value=90.0, des…", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "aba71ad883fd429f9b2bac9e1ba7f917" - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax = plt.subplots(1, 3)\n", - "img_gr = plot_gratings(ax[0], defaults[k], 90, 0, 0, 0)\n", - "img_rf = plot_receptive_field(ax[1], defaults[k], 90, 90, defaults[sigma])\n", - "val = get_value()\n", - "line = ax[2].plot([val, val])[0]\n", - "ax[2].set(ylim=[-0.2, 0.2])\n", - "\n", - "@widgets.interact(\n", - " k_val=widgets.FloatSlider(min=0, max=10, step=0.2, value=defaults[k], description='k'),\n", - " phi_val=widgets.FloatSlider(min=0, max=360, step=10, value=90, description='phi'),\n", - " theta_val=widgets.FloatSlider(min=0, max=180, step=10, value=0, description='theta'),\n", - " x0_val=widgets.FloatSlider(min=-1, max=1, step=0.05, value=0, description='x0'),\n", - " y0_val=widgets.FloatSlider(min=-1, max=1, step=0.05, value=0, description='y0'),\n", - " sigma_val=widgets.FloatSlider(min=0, max=1, step=0.05, value=defaults[sigma], description='sigma'),\n", - ")\n", - "def plot_stuff(k_val, phi_val, theta_val, x0_val, y0_val, sigma_val):\n", - " plot_gratings(img_gr, k_val, phi_val, theta_val, x0_val, y0_val)\n", - " plot_receptive_field(img_rf, k_val, phi_val, theta_val, sigma_val)\n", - " val = get_value(k_val, phi_val, theta_val, x0_val, y0_val, sigma_val)\n", - " line.set_ydata([val, val])\n", - " fig.canvas.draw_idle()" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [ - { - "data": { - "text/plain": "", - "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.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(\n '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\nmpl.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 = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(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 (fig.ratio !== 1) {\n fig.send_message('set_device_pixel_ratio', {\n device_pixel_ratio: fig.ratio,\n });\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\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\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\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.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,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n fig.rubberband_canvas.style.cursor = msg['cursor'];\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.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.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\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 img.type = 'image/png';\n }\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\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\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(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\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(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.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 }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\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 * https://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\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\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.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\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar 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.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\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 var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.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 = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\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;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\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.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.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 / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.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 () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\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 /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('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 } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.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.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "", - "text/html": "
" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": "interactive(children=(FloatSlider(value=0.8, description='k', max=10.0, step=0.2), FloatSlider(value=0.8, desc…", - "application/vnd.jupyter.widget-view+json": { - "version_major": 2, - "version_minor": 0, - "model_id": "75bac2ab8604428a9d0928978ef62b80" - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax = plt.subplots(1, 2)\n", - "DEF_K, DEF_SIGMA, DEF_X = 0.8, 0.05, 0.8\n", - "get_tuning_curve_func = lambda k_val, x0_val, y0_val, sigma_val: lambda theta_val, phi_val: get_value(k_val, phi_val * 180 / np.pi, theta_val * 180 / np.pi, x0_val, y0_val, sigma_val)\n", - "left_img = plot_tuning_curve(get_tuning_curve_func(DEF_K, DEF_X, 0, DEF_SIGMA), ax[0], title='Tuning Curve')\n", - "right_img = plot_tuning_curve(p.subs({k: defaults[k], x0: defaults[x0], y0: defaults[y0], sigma: defaults[sigma]}), ax[1], title='Tuning Curve')\n", - "\n", - "@widgets.interact(\n", - " k_val=widgets.FloatSlider(min=0, max=10, step=0.2, value=DEF_K, description='k'),\n", - " x0_val=widgets.FloatSlider(min=-1, max=1, step=0.05, value=DEF_X, description='x0'),\n", - " y0_val=widgets.FloatSlider(min=-1, max=1, step=0.05, value=0, description='y0'),\n", - " sigma_val=widgets.FloatSlider(min=0, max=1, step=0.05, value=DEF_SIGMA, description='sigma'),\n", - ")\n", - "def plot_tuning_curves(k_val, x0_val, y0_val, sigma_val):\n", - " plot_tuning_curve(get_tuning_curve_func(k_val, x0_val, y0_val, sigma_val), left_img, title='Tuning Curve - Numeric')\n", - " plot_tuning_curve(p.subs({k: k_val, x0: x0_val, y0: y0_val, sigma: sigma_val}), right_img, title='Tuning Curve - Analytic')\n", - " fig.canvas.draw_idle()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "outputs": [], - "source": [ - "# %matplotlib inline\n", - "func_1 = get_tuning_curve_func(DEF_K, DEF_X, 0, DEF_SIGMA)\n", - "func_2 = p.subs({k: DEF_K, x0: DEF_X, y0: 0, sigma: DEF_SIGMA})\n", - "grid = get_orientation_phase_grid(15, 15)\n", - "img_1 = np.array([[func_1(theta, phi) for theta, phi in line] for line in grid])\n", - "img_2 = eval_func(func_2, theta, phi, grid)\n", - "plt.plot(np.mean(img_2, axis=1))\n", - "plt.show()" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 14, - "outputs": [ - { - "data": { - "text/plain": " 2 ⎛ 2 ⎞ \n k ⋅⎝cos (\\theta) + 1⎠ \n ───────────────────── \n 2 ⎛ \nℯ ⋅cos(\\phi - k⋅(x₀⋅cos(\\theta) + y₀⋅sin(\\theta)))⋅cosh⎝\\s\n\n \n \n \n 2 2 ⎞\nigma ⋅k ⋅cos(\\theta)⎠", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkoAAAApCAYAAADK3AZEAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAVb0lEQVR4Ae2d7bXcNBPHNzkpINxU8IQOIKkgoQNIKiB0ACff+JYDHUAqCKQDoAIgHUAHhNtBnv9PKwnZK1uyZe/a947O8crW68x/RtLoxd47Hz58OGzBffvttw91/b0FWrZEg+GyJWkYLYaAIWAIGAK3DYG7W2BYxsDXouOTLdCyQRowIMHHnCFgCBgChoAhYAicGYE7a6woaWB/Kj7u63qo62NdPyvsV/knTuGfK/Cx/G9OIhcImELLAtWtUoR4wFC6lv/jKhVYoYaAIWAIGAKGgCGQRWCtFSUMo7e6vletGEC/5GpXPMbUS/mrGEm+zipacvRtJczj+JXHaytkGR2GgCFgCBgChsCNR2AtQ+nTBDlWla6T5/T2Oz38kAascF9LywpVL1okOIGXOUPAEDAEDAFDwBA4EwKzDSWtbvyga2ilKD2UzeD+xQA/z1TGqttJKr+Wlkii8jzVdXJmSmFf63qBHxL7MFbGVnWqB5zAa/W6VmXECjcEDAFDwBAwBHaEwGxDSTxiJKVGyAnbGtRZBflG/sn5JIVxNmk0/0mBDQFjtKTFKh0GEgeo3/XCMfh+VTgGy+MQp2e2F4srPUo3mkbx1PmLrjFDCLyehbrNNwQMAUPAEDAEDIF1EWgxlD4TaX8OkecNA4ykd7riCkySnvwnBlQSv9htBS1pXd8pfWeVS88PleAT+dF40n264sQ5qBdpIZn7QQPIl+UMtEy+NAi8wM2cIWAIGAKGgCFgCJwBgXsNdTxSXowKjAhWhw66Z3UFHwOKgZ+tKnlu5cjF8eAd+bPnk5SHvM91/a4Lw8C9NefDeaOOlRXqZYXHGS/yMcYIv9L1sZ7dAXH5NbQoi6ObMqMx5AKPP1/J+1tlQRcOP66GKRw6flZYx8AiYY1TXurEoBxddVKav3TBvzlDwBAwBAwBQ8AQOAMCLYZSajRgIGAoBEMpPUA9xAYrLO8HIjGM+KzAQb4zSOST/rX8WLbu/9T1ROEYOKT/Uc+8Rh+NCd3H9IovOXjCGOk7wuHvShd0XFGP/NQRvrYDL3g1ZwgYAoaAIWAIGAJnQGCWoSQjIRhJrIB86Y2GqVtCGB19Y+OQlO3Y17Nb4ZGP8dM3rHh+pDhWdDgzxQoXK1WvdM1x0NSvg3I4P+RWi+SzxZYrn7zRKR3YpMbTU4WlK2hulSxmqLvBaEzLrMtlqQwBQ8AQMAQMAUNgFgKzDCXVhNHC6hEDP6s6rNqwyhK3o/Q8y6kMtqAo674uZ0jJxzD7Q1d/NYXnP3w8K0lskR3kQ9db7ic66oC3vkv54g2+3Ft8HQNLNHS+DQVNuhx9/cInPGeNywn5LakhYAgYAoaAIWAITEBgrqHENherN5zbwSDhTSwMhdSg0OOoI/3Q6gjbaZTPKhHGAStGzhCSz1kkDCjy8hFGwknzhXwMJ8rNfrZA4aNO+THSXmYScSg91OvqTNMoDkPuXRo25d7nx0CDDz7A+buunKEHz/BnzhAwBAwBQ8AQMATOgMAqf2FSQ7cMAYwZXofvH/Kuyb5aGtHD1hoGYPUbeUpbXC2qSVNiytOGQTh1m7NUtMUbAoaAIWAIGAKGQAaBu5mwcwWxAhO/R3SuSkv1yAjhLBKfAuhv82WzKh1v/KVnj7LpFMgqWKtji3P2ylVr5ZbfEDAEDAFDwBC4bQjM3XpbAqc3KoQ3yTbnZPx8r4ttrhrntgVLCVVe58xSKf1APNtzreecBoq2YEPAEDAEDIGtIeDHonAkJEzgw0tUs8hdo8xZhOwk08VWlCQotzIin/M9m3Oiq2oFqDZdK4OqxzUQ+dVbgq11Wn5DwBAwBAyBNgTUZ7/QxSR3ruO8LudkuXiRiHOqgx97rqxkjTIrq95uMuGb3R26mKHkoeIVelshqdMbVqTAy5whYAgYAobADhDQwMvRjE/lt0xw+4YW4wCfrGlZZFijzB1IpEhieImsk/CihpIEzXkgBO5WSzqU2UNEwOMTv+UUI3Z2c9vkfNv47avjXP7n5uvXb8/jCMzFuZSvNX6c6v3ECgeOb/AWc+tiAPn5dM2Sbo0yl6SvWFZJz3IFlPIonjf3+cYhb7lHd1FDyVPBUmJ2uStSaTfg09rYLoqiV7yWGdBF6Z9a+W3jt49PI/9MCjodVb98e25DYGX5lORXim9jbju5OYPbPLZJVu4fJxK2GAt4M3v2iz1rlJnQt/ptg/4WdQ9sxACfAYrnlC/2eYAUSRHEitLn8jf1qYCUxkvdCxMGjLfyp3yj6lLkZusV7Sw/P5ZfdaBd6VgWdl9Czxa4cqDqpoHQyfGV9/d65rth1U7pJ/FbXfBOEtbyr3To9gPP1hs9x47fx/GNtIvpwaXgFs/0h5xB4UzK4vyrzKJ+Ks2gbMDFxw/KpzWeOs7pRC+TuNe6wP4nPTdNTJXfyVD+R0vy4emkb2I7r3OOVs+z+q2xMpekfamyRG+T/io/uj2ou9Dp0zBmuY9LTzaUlPHDUgxbOW0ISBZ32kpYP7dopPH+Jr/6P/eU1h00XJ+68RpEB4MVX36v7jSVdjK/41TsK7aWf6XjO2oscTNbBjO+8N8xSPUM/k/kX+8LhTZqxS8Hf8EHbKp1r6ZWlVfUT6Upyoa6lG5UPq3xNfwsnUY081+ffHjYDZBzy1d+VpIYjKsmhzX1qCyML8rlW3qDbUJx1f1WbZk19J0jjehdRH89RoN9i6/nX/H0ke6vJ38eQJk2PzifQ2BWRzUCHDycuvz8T3Xp6yZklvlqYhVz+J1YxaaTF/lXH0Kag3y3WiKfAYUlcb5fFleVlAS9Ie1sY0HlYXRQ9uIrMyp3FSda+eSI66BXqGBUPqq3VjaQVpJPa/wK7BeLXGrlng8XL/ZhYMkFI4kVRlemfz7Iz9Fb1W/5MmrLLAJ3pgRL6e+obgob+iT6ome6frx7JuY2UY0Yf6qLLb6vdfE17ZZXNjfB0w6IeCacqwcpL5N0sLwIi4luTH1bZRK/F2Fu3UpH+ReudOIsfbsBuUcKg0F0Xm8oj1nkXEfelvxz623KJ56vmwoYzjwonymyoXilp10Pyqc1fpiFbcd4HCFykQPYKo92wcDOmMVkgjbEStV7XR2nuDCmjfZbU8rsVHD5h0X0V/yP6q5nE/m5lcXJK0qXx6mJApb63Z6xfDpPltZshawJ0uHMwpi95NyMZzjT8avoWzirxsyNWUX1gDWT3zEsdhVXyf9LMdX5iyDlC4bMVYZh9MfN6jJxFjQBgQr5TJUNtZfk0xo/gcPNJMVYQcer+44C5Wyl0Ubwo1P5uZXW2n5rSpmxzkverKC/Jd1kG5a+59AxlEQIwqCxhK0PzgywNLeUwFXcRV16TgYr/abwNQiqlymz95TXzp/uJmlQDBxyx6iMsxLdM4uhA6Cc+7pwzxWeYnoM/e+XRhvL+C+4e+fLfq5Q9O4zPZPg0roHr5F20eQOECoM3ofaRJFflUN+luVxj3V9qQtdhH9cRzbHoPpfX/4m5e1pA8f+uY1HnkM6rr5DBuBavSrZL2DOcyIn9JDzU7F+H/da/qxzLMo32JYUhy5wWBefLThXh88TDxsrDhmDJQ49Qm9KE4xB/VRe9HKqbKi7JJ/W+IOn7aw6rTrBfyq+4IFDHtfubuQn4YvBGPz7zp1RU7opB8Kr+q2JZUa6EppT/jp9VpLmbOOJr3Oq/pZ0k52N+5R9LyCgBwBmeY+DYm7rQz4NGsOp37EpaH9O/KQdMQ1vVke3F87FL42dmQOfu38L3fKRM0aQe71UPjKmY8ZAifjonj8sZpk3/J0LB6zpaKPTc25GE+N180gXOjXoVIaLD2XJP+iiTke7/A5dgwUtGKE66bTAJdAWOkyewZMrDpy6D67IrxKCo8NNPltQDH50NBhflI9eOlnJn+SUf+vyDgYiHVTqwBoX9e/46H7pbNHZczu+f4NMkD0ySuXNwBZ0YhJdKg/dGmxLigcD3mhCx6LTM30y4eBxpcu1TRIoDPww5kpvx47p5xzZUH1JPk3x4ukSOo2MnqpuZ3hOwBc8cOQ/2RZzMf7Hl/mbHn/SxThEHvSM7R433iqNG4f1XOWUnjLm9Fu15V9CFiltS+tvSTeDDB/egwoBDAC/6OLbAalw6KAIv1FOPDIg0QmmvN4oHj0zGEAYROnAS2PCXR89ZyTlOlgaK50vgxod8yPdY12HfGR3hgQ3A466grKdJFFZ0Mfr98FwQN/cIKqwsD1zCYM2DMzM6N29fDCiE2Ig6w/0CnKuxC+DEfwEB5YMuKwq4cA5jT+ozvD8j+Ie6Hls0rJpeYv+57pwGCHuxv+AAducOUMJ/aF/OpsTHcj5d18hk4O+DndWZpR+iozo7GvaUr9OT47TPwbxOMnTPW9pEY+upgYdYakb0885sqHsknxa4y+h08gnTgon4Buwph3ndNnFqzzkgJEUJ7BEKJx8nEN6x/MM5/oq5Zvab9VWdQlZpLQtrb8l3aR/xl3dO/pxwGMgZJaL4/smrCoMDQou0d5+xA+dmts6gVddpeXqvbHo6BVfKBUdfoc/hWM0OcNJ98QzCIVBQbdHp7jQ+T7XPXihVP/KRx8wnjEcOmUfc3Z+afhB2ToRyovRQMP+XxLB6lFqCJA3NP4k2fFWaeGRDge/1sUV05EMdJKh7j9UTzDe6MDGthoH+fV1UVbagVIWOFPXQX4c/PwzGP2jcIezfAZI2mTsxEmHUxgYbFbejsgjfXE7KaEbQ+mVT9P3wKsoX/GP0Z7TFWQCPrnVT7DvYE5aObfaerx1ZxT6tFGPC1P+ahlRntLD/5y25MlxHqsOOVfCaUw/0Z2psoGGknxmxwunS+n0XHyDTKAbGQ85xqD+BJa0ad8wlHcsfG6/NVami7ugLFLaltbfkm5GGd7zVNDwawa+lOjd3UvYLGfTIWAgQT9AlQZ70u3RhVk4KxFDLqS5HkqgcPDCMai/1MWgRkNn+6DlOy8YRHzYrVT3YOfh844ZLqpilqM9UC9ls93RMsuLBKgcDK3UsYXTH4TTeHB+EgKUn4EMQ4nX3fu4BFluUt6il8ED11+hBgPc0ErIWOd4zKlflZ8zhAhHX8Grup0rrdNJ+egBdEfaFEZ7ICxMIKfISNmcW7othXJn+eJprmyorySflvhN6/QI2OgPfA85jOucvqJb/XY9VEYufJV+y1e0WVk06G+NbsL++7sJ2ierCkncjbgVoAx6d5KLg5o31YUGN8ZjSBM6yhwWzHxcI5HPytLH4KeENPQXuqdxDzks8qGyKRPD1TmVQzr2jJ3zzzT80vaez7GM5+uFtle6h0eWm9mCHOMzVD7Gb0jjfJUXBmG3ukegwtzBQX8PDWASZEQw7lpXjpaQbqvydsTrp28sYjBjcMNXzoEBuF7CsdoUV/w8AciNbULCp8ro4PPgT21LS/Bf0s+psoGmknxa4reg00vgHssI8ldAMLRjnG7Y/syFp2my9yoXnOf2W9kye4FbkMXS+lujm8BwfdeDAQgP/H3HkwCYkZnbGQKS27VIpuN7lCMdueoinnS5rRwGBByGAgMzs6DolJdZNoN8tnyfEL2i8eYcdaOowVFf2km81jMDaDQkQsKV/bDCEWgJy/COD9ETtqZzZAzyq3wYQawGBVwxwhhwQwdEeZzdQR64FJtjyPGXzuIqDeDe59usvBO+IukKo2+Bl3S7Ncb7G+JTjPrxaz4j837dtJWgG5Nk5Amd25aW4DOrnw2ygaaSfGbHb0Sn5+AOzmO6QZm04+jEK3qBvo21hZg+c9PSb2WK6wZtRBZL62+NbgJEXFFi+Th04A4hAUPHzmyeztfcPhFgRsy2Q9/IQd5Brmzt8BEvGmrqSMPbZ2FQYBDvN36eQ3yaN9xTx+Pw0PO/1HO6/Mz/6rhBST7G2Xv5aXwv+2qPDIT9VQQqc7TJz04oSCA3xi/tiwu+wO29rugURlzNqi6Nuy+HUM6W5Q2NGL2Odo+B27bS/XVgIOOzTRV0NRO9alCQuavEywg59bcP+0SMyYi0tW0pJ2fKnuvG9HOObKCjJJ/W+HPrdAu+QS604+wE0us6fSZ65JzCMJDo80b/muSYevC3pd8aLLQXcW5Z9Kof7V/n6G9JN5ELk9nre1CiG2buGEZ0XGH7g+dLDFSQZG4BBCQ/ts04LM15IozeIFvO3ASjBKMAhWErIAxYbmaj52AEEc4K0guFyXPugX4ptzOY+LjgvdENHcCJUz7qpWMIdPG6M/rH4MC216UGR1d/IFh00FCY5fFGKPi9CnEZf5BfpQVLMHQdpMqiPFaY4J8tSAwoGntwQRbhOfjQl8Vc+Tcrb088xjE6g36xRVjz6QfwulQ/hNxfi170krNfYVszbRcKPnGDMlJK5DrYllTXJ4p/qcsNtHqm/YAbmBFOPDpJOLpIeMAHA4wJBwNazo3p5xzZUEdJPk3x4uUsOq16Au4t+AbM6bsYP7ly7Rj5hHZAHvpSjoXk0hJf49C52DdRlq7afqum/IPKO4ssRohZWn9Lusm46Nr65D/FHWHCogyBEwTUuDAuMIgGDR/F0dk/lc8AsmtXw28tgyqLL8fzx40RO93zp9ScE8saS7Vlr5VOdBXlXVu3ykIvMCaDgVKbNaZTXrb3Jh3mjpl7NyoLgwk9pQN1Tve7kpHoPZt8VNeo/ErxAeM9+l4v6PeCUb1HNjZHs/BcRH9rdE9pmMCyqHC7/uttc1K/HQQxuIQZ7xDHzOJuSodSw+8QDv1wZojMepxTg+Wet1M3aSR5Mpfknxkx5bU4ZumTZ+rCmBl//2UDtrDjrN0TtTcZnVM+JfmV4lvkfum8P4kAtsPMLYvAUvo7qntq+6zQMS4hx8NdfswZAmshIIVjlYgZPbPLIcd2wZYH/yG6T8Ir+T3JlwtQWbzSzkcmGbQ5RM6SPVskm3VL8a9y0Bf0pmmVUfn5pMKcMliJepMAnX25QGXvSkYei1J7TNjO36qcUfm0xudr3VUoW+qds6G7on6jxC6hvyXd9KxzOJ5JqZtk3dsoHkbWzUKAAZ4zFUMzLM5/3CRX4reaVzVUZj57c0vwz0BTWolcExdwvxL+GKhs/bEEn1313KGMziGfkvxK8WvKdvWypROcwcRI5+3i9Ozh6nXfggpa9bdG92j/cbyyM0q3QKu2wKI6C2agdBrMwG+8u2389gXawr/yYpxsfYuxz/KunteUT0l+pfhdATlCrPhk++Y3+fFM20hyi5qAgDCdNZ7U6J7SsBLIQfw4VpmhNEE4ltQQMAQMAUPAEKhFQIMtW7gcLdjjynAtmzcmneSEAcbqcVxNgrm7N4ZDY8QQMAQMAUPAENgQAhpw2Xb7S358KWND5Bkppwhg0LK113H/B32ZJezgbtrMAAAAAElFTkSuQmCC\n", - "text/latex": "$\\displaystyle e^{\\frac{k^{2} \\left(\\cos^{2}{\\left(\\theta \\right)} + 1\\right)}{2}} \\cos{\\left(\\phi - k \\left(x_{0} \\cos{\\left(\\theta \\right)} + y_{0} \\sin{\\left(\\theta \\right)}\\right) \\right)} \\cosh{\\left(\\sigma^{2} k^{2} \\cos{\\left(\\theta \\right)} \\right)}$" - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "p" - ], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - }, - { - "cell_type": "code", - "execution_count": 14, - "outputs": [], - "source": [], - "metadata": { - "collapsed": false, - "pycharm": { - "name": "#%%\n" - } - } - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} \ No newline at end of file diff --git a/plotting_utils.py b/model/plotting_utils.py similarity index 100% rename from plotting_utils.py rename to model/plotting_utils.py diff --git a/sym_model.py b/model/sym_model.py similarity index 100% rename from sym_model.py rename to model/sym_model.py diff --git a/utils.py b/model/utils.py similarity index 100% rename from utils.py rename to model/utils.py diff --git a/model/__init__.py b/numerical/__init__.py similarity index 100% rename from model/__init__.py rename to numerical/__init__.py diff --git a/numerical/decoding.py b/numerical/decoding.py new file mode 100644 index 0000000..148a7c9 --- /dev/null +++ b/numerical/decoding.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +""" +Use cohomology to decode datasets with circular parameters + +Persistent homology from arxiv:1908.02518 +Homological decoding from DOI:10.1007/s00454-011-9344-x and arxiv:1711.07205 +""" +import math +import numpy as np +from scipy.optimize import least_squares +import pandas as pd + +from tqdm import trange + +import ripser + +from persistence import persistence + +EPSILON = 0.0000000000001 + + +def shortest_cycle(graph, node2, node1): + """ + Returns the shortest cycle going through an edge + + Used for computing weights in decode + + Parameters + ---------- + graph: ndarray (n_nodes, n_nodes) + A matrix containing the weights of the edges in the graph + node1: int + The index of the first node of the edge + node2: int + The index of the second node of the edge + + Returns + ------- + cycle: list of ints + A list of indices representing the nodes of the cycle in order + """ + N = graph.shape[0] + distances = np.inf * np.ones(N) + distances[node2] = 0 + prev_nodes = np.zeros(N) + prev_nodes[:] = np.nan + prev_nodes[node2] = node1 + while (math.isnan(prev_nodes[node1])): + distances_buffer = distances + for j in range(N): + possible_path_lengths = distances_buffer + graph[:, j] + if (np.min(possible_path_lengths) < distances[j]): + prev_nodes[j] = np.argmin(possible_path_lengths) + distances[j] = np.min(possible_path_lengths) + prev_nodes = prev_nodes.astype(int) + cycle = [node1] + while (cycle[0] != node2): + cycle.insert(0, prev_nodes[cycle[0]]) + cycle.insert(0, node1) + return cycle + + +def cohomological_parameterization(X, cocycle_number=1, coeff=2, weighted=False): + """ + Compute an angular parametrization on the data set corresponding to a given + 1-cycle + + Parameters + ---------- + X: ndarray(n_datapoints, n_features): + Array containing the data + cocycle_number: int, optional, default 1 + An integer specifying the 1-cycle used + The n-th most stable 1-cycle is used, where n = cocycle_number + coeff: int prime, optional, default 1 + The coefficient basis in which we compute the cohomology + weighted: bool, optional, default False + When true use a weighted graph for smoother parameterization + as proposed in arxiv:1711.07205 + + Returns + ------- + decoding: ndarray(n_datapoints) + The parameterization of the dataset consisting of a number between + 0 and 1 for each datapoint, to be interpreted modulo 1 + """ + # Get the cocycle + result = ripser.ripser(X, maxdim=1, coeff=coeff, do_cocycles=True) + diagrams = result['dgms'] + cocycles = result['cocycles'] + D = result['dperm2all'] + dgm1 = diagrams[1] + idx = np.argsort(dgm1[:, 1] - dgm1[:, 0])[-cocycle_number] + cocycle = cocycles[1][idx] + persistence(X, homdim=1, coeff=coeff, show_largest_homology=0, + Nsubsamples=0, save_path=None, cycle=idx) + thresh = dgm1[idx, 1] - EPSILON + + # Compute connectivity + N = X.shape[0] + connectivity = np.zeros([N, N]) + for i in range(N): + for j in range(i): + if D[i, j] <= thresh: + connectivity[i, j] = 1 + cocycle_array = np.zeros([N, N]) + + # Lift cocycle + for i in range(cocycle.shape[0]): + cocycle_array[cocycle[i, 0], cocycle[i, 1]] = ( + ((cocycle[i, 2] + coeff / 2) % coeff) - coeff / 2 + ) + + # Weights + if (weighted): + def real_cocycle(x): + real_cocycle = ( + connectivity * (cocycle_array + np.subtract.outer(x, x)) + ) + return np.ravel(real_cocycle) + + # Compute graph + x0 = np.zeros(N) + res = least_squares(real_cocycle, x0) + real_cocyle_array = res.fun + real_cocyle_array = real_cocyle_array.reshape(N, N) + real_cocyle_array = real_cocyle_array - np.transpose(real_cocyle_array) + graph = np.array(real_cocyle_array > 0).astype(float) + graph[graph == 0] = np.inf + graph = (D + EPSILON) * graph # Add epsilon to avoid NaNs + + # Compute weights + cycle_counts = np.zeros([N, N]) + iterator = trange(0, N, position=0, leave=True) + iterator.set_description("Computing weights for decoding") + for i in iterator: + for j in range(N): + if (graph[i, j] != np.inf): + cycle = shortest_cycle(graph, j, i) + for k in range(len(cycle) - 1): + cycle_counts[cycle[k], cycle[k + 1]] += 1 + + weights = cycle_counts / (D + EPSILON) ** 2 + weights = np.sqrt(weights) + else: + weights = np.outer(np.ones(N), np.ones(N)) + + def real_cocycle(x): + real_cocycle = ( + weights * connectivity * (cocycle_array + np.subtract.outer(x, x)) + ) + return np.ravel(real_cocycle) + + # Smooth cocycle + print("Decoding...", end=" ") + x0 = np.zeros(N) + res = least_squares(real_cocycle, x0) + decoding = res.x + decoding = np.mod(decoding, 1) + print("done") + + decoding = pd.DataFrame(decoding, columns=["decoding"]) + decoding = decoding.set_index(X.index) + return decoding + + +def remove_feature(X, decoding, shift=0, cut_amplitude=1.0): + """ + Removes a decoded feature from a dataset by making a cut at a fixed value + of the decoding + + Parameters + ---------- + X: dataframe(n_datapoints, n_features): + Array containing the data + decoding : dataframe(n_datapoints) + The decoded feature, assumed to be angular with periodicity 1 + shift : float between 0 and 1, optional, default 0 + The location of the cut + cut_amplitude : float, optional, default 1 + Amplitude of the cut + """ + cuts = np.zeros(X.shape) + decoding = decoding.to_numpy()[:, 0] + for i in range(X.shape[1]): + effective_amplitude = cut_amplitude * (np.max(X[i]) - np.min(X[i])) + cuts[:, i] = effective_amplitude * ((decoding - shift) % 1) + reduced_data = X + cuts + return reduced_data diff --git a/model/decorators.py b/numerical/decorators.py similarity index 100% rename from model/decorators.py rename to numerical/decorators.py diff --git a/model/dimension.py b/numerical/dimension.py similarity index 100% rename from model/dimension.py rename to numerical/dimension.py diff --git a/model/example.py b/numerical/example.py similarity index 100% rename from model/example.py rename to numerical/example.py diff --git a/model/gratings.py b/numerical/gratings.py similarity index 100% rename from model/gratings.py rename to numerical/gratings.py diff --git a/model/load.py b/numerical/load.py similarity index 100% rename from model/load.py rename to numerical/load.py diff --git a/model/noisereduction.py b/numerical/noisereduction.py similarity index 100% rename from model/noisereduction.py rename to numerical/noisereduction.py diff --git a/numerical/persistence.py b/numerical/persistence.py new file mode 100644 index 0000000..cdd6694 --- /dev/null +++ b/numerical/persistence.py @@ -0,0 +1,169 @@ +# -*- coding: utf-8 -*- +""" +Tools to compute persistence diagrams + +Persistent homology from ripser and gudhi library +Confidence sets from arxiv:1303.7117 +""" +import numpy as np +from scipy.spatial.distance import directed_hausdorff + +import matplotlib.pyplot as plt + +from tqdm import trange + +import ripser +from persim import plot_diagrams +import gudhi + +from decorators import multi_input + + +def hausdorff(data1, data2, homdim, coeff): + """Hausdorff metric between two persistence diagrams""" + dgm1 = (ripser.ripser(data1,maxdim=homdim,coeff=coeff))['dgms'] + dgm2 = (ripser.ripser(data2,maxdim=homdim,coeff=coeff))['dgms'] + distance = directed_hausdorff(dgm1[homdim], dgm2[homdim])[0] + return distance + +@multi_input +def confidence(X, alpha=0.05, Nsubsamples=20, homdim=1, coeff=2): + """ + Compute the confidence interval of the persistence diagram of a dataset + + Computation done by subsampling as in arxiv:1303.7117 + + Parameters + ---------- + X: dataframe(n_datapoints, n_features): + Dataframe containing the data + alpha : float between 0 and 1, optional, default 0.05 + 1-alpha is the confidence + Nsubsamples : int, optional, default 20 + The number of subsamples + homdim : int, optional, default 1 + The dimension of the homology + coeff : int prime, optional, default 2 + The coefficient basis + """ + N = X.shape[0] + distances = np.zeros(Nsubsamples) + iterator = trange(0, Nsubsamples, position=0, leave=True) + iterator.set_description("Computing confidence interval") + for i in iterator: + subsample = X.iloc[np.random.choice(N, N, replace=True)] + distances[i] = hausdorff(X, subsample, homdim, coeff) + distances.sort() + confidence = np.sqrt(2) * 2 * distances[int(alpha*Nsubsamples)] + return confidence + +@multi_input +def persistence(X, homdim=1, coeff=2, threshold=float('inf'), + show_largest_homology=0, distance_matrix=False, Nsubsamples=0, + alpha=0.05, cycle=None, save_path=None): + """ + Plot the persistence diagram of a dataset using ripser + + Also prints the five largest homology components + + Parameters + ---------- + X: dataframe(n_datapoints, n_features): + Dataframe containing the data + homdim : int, optional, default 1 + The dimension of the homology + coeff : int prime, optional, default 2 + The coefficient basis + threshold : float, optional, default infinity + The maximum distance in the filtration + show_largest_homology: int, optional, default 0 + Print this many of the largest homology components + distance_matrix : bool, optional, default False + When true X will be interepreted as a distance matrix + Nsubsamples : int, optional, default 0 + The number of subsamples used in computing the confidence interval + Does not compute the confidence interval when this is 0 + alpha : float between 0 and 1, optional, default 0.05 + 1-alpha is the confidence + cycle : int, optional, default None + If given highlight the homology component in the plot corresponding to + this cycle id + save_path : str, optional, default None + When given save the plot here + """ + result = ripser.ripser(X, maxdim=homdim, coeff=coeff, do_cocycles=True, + distance_matrix=distance_matrix, thresh=threshold) + diagrams = result['dgms'] + plot_diagrams(diagrams, show=False) + if (Nsubsamples>0): + conf = confidence(X, alpha, Nsubsamples, homdim, 2) + line_length = 10000 + plt.plot([0, line_length], [conf, line_length + conf], color='green', + linestyle='dashed',linewidth=2) + if cycle is not None: + dgm1 = diagrams[1] + plt.scatter(dgm1[cycle, 0], dgm1[cycle, 1], 20, 'k', 'x') + if save_path is not None: + path = save_path + 'Z' + str(coeff) + if (Nsubsamples>0): + path += '_confidence' + str(1-alpha) + path += '.png' + plt.savefig(path) + plt.show() + + if show_largest_homology != 0: + dgm = diagrams[homdim] + largest_indices = np.argsort(dgm[:, 0] - dgm[:, 1]) + largest_components = dgm[largest_indices[:show_largest_homology]] + print(f"Largest {homdim}-homology components:") + print(largest_components) + return + +@multi_input +def persistence_witness(X, number_of_landmarks=100, max_alpha_square=0.0, + homdim=1): + """ + Plot the persistence diagram of a dataset using gudhi + + Uses a witness complex allowing it to be used on larger datasets + + Parameters + ---------- + X: dataframe(n_datapoints, n_features): + Dataframe containing the data + number_of_landmarks : int, optional, default 100 + The number of landmarks in the witness complex + max_alpha_square : double, optional, default 0.0 + Maximal squared relaxation parameter + homdim : int, optional, default 1 + The dimension of the homology + """ + print("Sampling landmarks...", end=" ") + + witnesses = X.to_numpy() + landmarks = gudhi.pick_n_random_points( + points=witnesses, nb_points=number_of_landmarks + ) + print("done") + message = ( + "EuclideanStrongWitnessComplex with max_edge_length=" + + repr(max_alpha_square) + + " - Number of landmarks=" + + repr(number_of_landmarks) + ) + print(message) + witness_complex = gudhi.EuclideanStrongWitnessComplex( + witnesses=witnesses, landmarks=landmarks + ) + simplex_tree = witness_complex.create_simplex_tree( + max_alpha_square=max_alpha_square, + limit_dimension=homdim + ) + message = "Number of simplices=" + repr(simplex_tree.num_simplices()) + print(message) + diag = simplex_tree.persistence() + print("betti_numbers()=") + print(simplex_tree.betti_numbers()) + gudhi.plot_persistence_diagram(diag, band=0.0) + plt.show() + return diff --git a/model/plotting.py b/numerical/plotting.py similarity index 100% rename from model/plotting.py rename to numerical/plotting.py