From e41b428d568b94723c81a9dc6df82ab2282a5af7 Mon Sep 17 00:00:00 2001 From: ennucore Date: Fri, 8 Jul 2022 19:58:48 +0300 Subject: [PATCH] Add python files with models --- PopulationSampling.ipynb | 146 ++++++++++ TuningCurvesFull.ipynb | 560 +++++++++++++++++++++++++++++++++++++++ plotting_utils.py | 78 ++++++ sym_model.py | 103 +++++++ utils.py | 37 +++ 5 files changed, 924 insertions(+) create mode 100644 PopulationSampling.ipynb create mode 100644 TuningCurvesFull.ipynb create mode 100644 plotting_utils.py create mode 100644 sym_model.py create mode 100644 utils.py diff --git a/PopulationSampling.ipynb b/PopulationSampling.ipynb new file mode 100644 index 0000000..d32db63 --- /dev/null +++ b/PopulationSampling.ipynb @@ -0,0 +1,146 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 4, + "outputs": [], + "source": [ + "%matplotlib notebook\n", + "from sym_model import Population\n", + "import umap\n", + "reducer = umap.UMAP()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [], + "source": [ + "res = Population.random(40).sample_responses(1000)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/lib/python3.10/site-packages/umap/umap_.py:132: UserWarning: A large number of your vertices were disconnected from the manifold.\n", + "Disconnection_distance = inf has removed 812116 edges.\n", + "It has fully disconnected 553 vertices.\n", + "You might consider using find_disconnected_points() to find and remove these points from your data.\n", + "Use umap.utils.disconnected_vertices() to identify them.\n", + " warn(\n", + "/usr/lib/python3.10/site-packages/sklearn/manifold/_spectral_embedding.py:259: UserWarning: Graph is not fully connected, spectral embedding may not work as expected.\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "embedding = reducer.fit_transform(res)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 11, + "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": [ + "import matplotlib.pyplot as plt\n", + "%matplotlib notebook\n", + "plt.scatter(\n", + " embedding[:, 0],\n", + " embedding[:, 1])\n", + "plt.show()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "data": { + "text/plain": "(1000, 2)", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAFAAAAAVCAYAAADRhGlyAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADdklEQVRYCe2Y7XEaMRCGL54UQJwOcAc4qSCmA0wHMR0kP+FfxunASQUZuwMnFWRCB9CBCR2Q9xFaWcg6c+EEM8x4Z3QrrVa70nurz2q1WlVxGo/H3bj8kn/EJ4fNSRXRZDL5pGIvEr1kNxHoeoyC9BURBqliIPZe/LMTJB/JAfZW6Vz5ZVLtipJfe/mD+JnStWRzLwustF4w3CKjPnXV3Mb+TvkFZcmnsVmVCbKl+DfkDkAVOsr/Ej9HaOTl31XGGEYB8Y3kTwCU7I/qvojfiVfi2ETWVz6AqHxRPXy1JfUJ8G7E+2ZLeYIBsOj/T5PDVWYMH8SXNoVRvqEyJhSULpVGkv+I6+K86q9U7og78KhTHpApB7ul9fBTiBg/YwykvhKNjIFZlxJjok1lAA7VwIVkqtmwfCm9jVD37X6LX8g20QiV1ltbbf+9kIlZ1E+zSOQRGERoII8VmHVO9GHtC1MsaP1fhg4wzVMyu9RDpfXWVtt/AWouLIi4HFkAxHWMbfhaH+b9xhyPtbbl+QvbdFR/Wlqvgc/GKuobMyNH7kSi+tzsArM+U5jNYZZr3VB26vXq/h7VgFxaz7vdDxNogMfUtZ05dQRmXQBkcLnplzZoU37bsHFpvYZus2psHncC8mu2do2ZA5DIeC56atoH8XPgW9Q9SLu0XuhA6YxAY5dlTayb2rhkDewQga1ITgx8Ijklk8ULtMliXZM11osbl8xrPBzJWLPDmbDGvgs8ACQybAA1ulvFLKisFylZBNomVVov9deqLNA4kZyJh8hTnutbbmxgtgBAQjGnIHFjYr1gM0qJm81UHVj6itJ6zqzstw2ASjZ6Mpa7ygJqbvkhOOYAOKWh0jayBd6iKujLOYfwhTjOHCnPoIZKH9cSdzspqodd7+evONernUhtCSB+LodmrnQhSTZS2QIgtu+Cg3MgVzQaZ0mNrc4Ow7eSEbX34vHtBYM8HvAz2DTg3Bf5QTEV1ZN9rptuQRcHgNxgY/+5/L2EgMj6l1Laf6sHj5F7C9Q710ypd8xvf+r/QKlziDHIT1dphi+mMPTkMr0WH9WX9WuX6NtlkByuHx8T5JipWLfb7OLgoG2YunLIsrF3ki+mOli55csiEMds3eHpCcER0ZUGVHdjKD0MMApPX+FFGi8e3cEBO1N6cHu1J1x4YOV6x6bl6B/ChE7KlbdLiwAAAABJRU5ErkJggg==\n", + "text/latex": "$\\displaystyle \\left( 1000, \\ 2\\right)$" + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "embedding.shape" + ], + "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 new file mode 100644 index 0000000..c7ca555 --- /dev/null +++ b/TuningCurvesFull.ipynb @@ -0,0 +1,560 @@ +{ + "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": "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\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": "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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": 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/plotting_utils.py new file mode 100644 index 0000000..3b96708 --- /dev/null +++ b/plotting_utils.py @@ -0,0 +1,78 @@ +import typing + +import matplotlib as mpl +import numpy as np +import matplotlib.pyplot as plt + +import sympy as sp + +from utils import eval_func, get_orientation_phase_grid, get_spatial_grid + +sp.init_printing() +AxOrImg = typing.Union[mpl.axes.Axes, mpl.image.AxesImage] + + +# %% + + +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, + patch: typing.Optional[typing.Tuple[float, float, float]] = None + ): + """ + Plots a spatial map of the function. + + :param func: function to plot + :param ax: axes to plot on or the image on axes + :param step_x: step for the x-coordinate + :param step_y: step for the y-coordinate + :param size: size of the grid + :param title: title of the plot + :param show: whether to show the plot + :param patch: optional circle to plot - a tuple (x, y, radius) + """ + grid = get_spatial_grid(step_x, step_y, size) + image: np.ndarray = eval_func(func, x, y, grid) + if isinstance(ax, mpl.image.AxesImage): + ax.set_data(image) + return ax + img = ax.imshow(image, extent=[-size, size, -size, size], vmin=-size, vmax=size, cmap='gray') + ax.invert_yaxis() + if patch is not None: + ax.add_patch(plt.Circle(patch[:2], radius=patch[2], color='b', fill=False)) + ax.set_title(title) + if show: + plt.show() + return img + + +def normalize(img): + return (img - img.min()) / (img.max() - img.min()) + + +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): + """ + Plots a tuning curve of the function. + + :param func: function to plot - sympy or a function of (theta, phi) + :param ax: axes to plot on or image to update + :param step_phase: step for the phase (phi) - in degrees + :param step_orientation: step for the orientation (theta) - in degrees + :param title: title of the plot + :param show: whether to show the plot + """ + grid = get_orientation_phase_grid(step_phase, step_orientation) + if isinstance(func, sp.Expr): + image: np.ndarray = eval_func(func, theta, phi, grid) + else: + image = np.array([[func(theta_val, phi_val) for theta_val, phi_val in line] for line in grid]) + image = normalize(image) + if isinstance(ax, mpl.image.AxesImage): + ax.set_data(image) + return ax + img = ax.imshow(image, extent=[0, 360, 0, 180], cmap='viridis') + ax.set_title(title) + if show: + plt.show() + return img diff --git a/sym_model.py b/sym_model.py new file mode 100644 index 0000000..d44a25d --- /dev/null +++ b/sym_model.py @@ -0,0 +1,103 @@ +from __future__ import annotations +import typing +from dataclasses import dataclass, field + +import matplotlib as mpl +import sympy as sp +import numpy as np + +from utils import get_orientation_phase_grid + +sp.init_printing() +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') +defaults = { + k: 6, + sigma: 0.2, + phi: sp.pi / 2, + theta: 0, + sigma: 1, + x0: 0, y0: 0 +} +sigma_x = sigma_y = sigma +grating_f = sp.cos(k * (x - x0) * sp.cos(theta) + k * (y - y0) * sp.sin(theta) + phi) +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) +receptive_field = receptive_field.subs(theta, 0).subs(phi, 0) +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))) + +sigma_split = np.arange(0.1, 1, 0.05) +k_split = np.arange(0.2, 6, 0.2) +xy_split = np.arange(-1, 1, 0.05) + + +@dataclass +class Cell: + sigma_val: float = defaults[sigma] + x0_val: float = defaults[x0] + y0_val: float = defaults[y0] + k_val: float = defaults[k] + + @classmethod + def random(cls, sigma_dist: np.ndarray = np.ones(len(sigma_split)), + k_val: float = defaults[k], + xy_dist: np.ndarray = np.ones(len(xy_split))): + return cls( + sigma_val=np.random.choice(sigma_split, p=sigma_dist / np.sum(sigma_dist)), + x0_val=np.random.choice(xy_split, p=xy_dist / np.sum(xy_dist)), + y0_val=np.random.choice(xy_split, p=xy_dist / np.sum(xy_dist)), + k_val=k_val + ) + + @property + def sympy_func(self) -> sp.Expr: + return receptive_field.subs(sigma, self.sigma_val).subs(x0, self.x0_val).subs(y0, self.y0_val).subs(k, + self.k_val) + + def get_tuning_function(self) -> typing.Callable[[np.ndarray, np.ndarray], np.ndarray]: + """ + Get the tuning sympy function as a numpy lambda function of theta and phi. + :return: a function (theta, phi) -> value + """ + return sp.lambdify( + (theta, phi), + p.subs(sigma, self.sigma_val).subs(x0, self.x0_val).subs(y0, self.y0_val).subs(k, self.k_val), + 'numpy') + + def get_value(self, theta_deg: float, phi_deg: float) -> float: + return float(self.get_tuning_function()(theta_deg * np.pi / 180, phi_deg * np.pi / 180)) + + def get_tuning_plot(self, theta_step_deg: float, phi_step_deg: float) -> np.ndarray: + grid = get_orientation_phase_grid(theta_step_deg, phi_step_deg) + return self.get_tuning_function()(grid[:, :, 0], grid[:, :, 1]) + + +@dataclass +class Grating: + k_val: float = defaults[k] + phi_val: float = defaults[phi] + theta_val: float = defaults[theta] + + @property + def sympy_func(self) -> sp.Expr: + return grating_f.subs(k, self.k_val).subs(phi, self.phi_val).subs(theta, self.theta_val) + + +@dataclass +class Population: + cells: typing.List[Cell] = field(default_factory=list) + + @classmethod + def random(cls, n: int, sigma_dist: np.ndarray = np.ones(len(sigma_split)), + k_val: float = defaults[k], + xy_dist: np.ndarray = np.ones(len(xy_split))): + return cls(cells=[Cell.random(sigma_dist, k_val, xy_dist) for _ in range(n)]) + + def get_response(self, phi_deg: float, theta_deg: float) -> typing.List[float]: + return [cell.get_value(theta_deg, phi_deg) for cell in self.cells] + + def sample_responses(self, n: int) -> np.ndarray: + return np.array([ + self.get_response(phi_deg, theta_deg % 180) + for phi_deg, theta_deg in np.random.uniform(0, 360, (n, 2)) + ]) diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..8cc5f7e --- /dev/null +++ b/utils.py @@ -0,0 +1,37 @@ +import numpy as np +import sympy as sp + + +def eval_func(func: sp.Expr, sub_1: sp.Expr, sub_2: sp.Expr, grid: np.ndarray) -> np.ndarray: + # return np.array([[float(func.subs(sub_1, x_).subs(sub_2, y_)) for x_, y_ in line] for line in grid]) + func = sp.lambdify([sub_1, sub_2], func, 'numpy') + return func(grid[:, :, 0], grid[:, :, 1]) + + +def get_orientation_phase_grid(step_phase: float, step_orientation: float) -> np.ndarray: + """ + Returns a grid of x and y values for plotting. + :param step_phase: step for the phase (phi) - in degrees + :param step_orientation: step for the orientation (theta) - in degrees + :return: numpy array of shape (n_orientation, n_phase). Each element is a tuple (theta, phi) + """ + # phase <-> phi + # orientation <-> theta + step_phase *= np.pi / 180 + step_orientation *= np.pi / 180 + phi = np.arange(0, 2 * np.pi, step_phase) + theta = np.arange(0, np.pi, step_orientation) + return np.array(np.meshgrid(theta, phi)).T.reshape(-1, len(phi), 2) + + +def get_spatial_grid(step_x: float, step_y: float, size: float = 1) -> np.ndarray: + """ + Returns a grid of x and y values for plotting. + :param step_x: step for the x-coordinate + :param step_y: step for the y-coordinate + :param size: size of the grid + :return: numpy array of shape (2 * size / step_x, 2 * size / step_y). Each element is a tuple (x, y) + """ + x = np.arange(-size, size, step_x) + y = np.arange(-size, size, step_y) + return np.array(np.meshgrid(x, y)).T.reshape(-1, len(x), 2)