Skip to content

Instantly share code, notes, and snippets.

@kif
Last active June 29, 2021 09:31
Show Gist options
  • Save kif/d3c4c30bea79cd5c86b0074091128e40 to your computer and use it in GitHub Desktop.
Save kif/d3c4c30bea79cd5c86b0074091128e40 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "e91c9ff2",
"metadata": {},
"source": [
"# Profiling of application and core placement"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "7f7974ed",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Architecture : x86_64\r\n",
"Mode(s) opératoire(s) des processeurs : 32-bit, 64-bit\r\n",
"Boutisme : Little Endian\r\n",
"Tailles des adresses: 43 bits physical, 48 bits virtual\r\n",
"Processeur(s) : 16\r\n",
"Liste de processeur(s) en ligne : 0-15\r\n",
"Thread(s) par cœur : 2\r\n",
"Cœur(s) par socket : 8\r\n",
"Socket(s) : 1\r\n",
"Nœud(s) NUMA : 1\r\n",
"Identifiant constructeur : AuthenticAMD\r\n",
"Famille de processeur : 23\r\n",
"Modèle : 49\r\n",
"Nom de modèle : AMD EPYC 7262 8-Core Processor\r\n",
"Révision : 0\r\n",
"Accroissement de fréquence : activé\r\n",
"Vitesse du processeur en MHz : 2309.118\r\n",
"Vitesse maximale du processeur en MHz : 3400,0000\r\n",
"Vitesse minimale du processeur en MHz : 1500,0000\r\n",
"BogoMIPS : 6400.10\r\n",
"Virtualisation : AMD-V\r\n",
"Cache L1d : 256 KiB\r\n",
"Cache L1i : 256 KiB\r\n",
"Cache L2 : 4 MiB\r\n",
"Cache L3 : 128 MiB\r\n",
"Nœud NUMA 0 de processeur(s) : 0-15\r\n",
"Vulnerability Itlb multihit: Not affected\r\n",
"Vulnerability L1tf: Not affected\r\n",
"Vulnerability Mds: Not affected\r\n",
"Vulnerability Meltdown: Not affected\r\n",
"Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass dis\r\n",
" abled via prctl and seccomp\r\n",
"Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and\r\n",
" __user pointer sanitization\r\n",
"Vulnerability Spectre v2: Mitigation; Full AMD retpoline, IBPB con\r\n",
" ditional, IBRS_FW, STIBP conditional, RS\r\n",
" B filling\r\n",
"Vulnerability Srbds: Not affected\r\n",
"Vulnerability Tsx async abort: Not affected\r\n",
"Drapeaux : fpu vme de pse tsc msr pae mce cx8 apic \r\n",
" sep mtrr pge mca cmov pat pse36 clflush \r\n",
" mmx fxsr sse sse2 ht syscall nx mmxext f\r\n",
" xsr_opt pdpe1gb rdtscp lm constant_tsc r\r\n",
" ep_good nopl nonstop_tsc cpuid extd_apic\r\n",
" id aperfmperf pni pclmulqdq monitor ssse\r\n",
" 3 fma cx16 sse4_1 sse4_2 movbe popcnt ae\r\n",
" s xsave avx f16c rdrand lahf_lm cmp_lega\r\n",
" cy svm extapic cr8_legacy abm sse4a misa\r\n",
" lignsse 3dnowprefetch osvw ibs skinit wd\r\n",
" t tce topoext perfctr_core perfctr_nb bp\r\n",
" ext perfctr_llc mwaitx cpb cat_l3 cdp_l3\r\n",
" hw_pstate sme ssbd mba sev ibrs ibpb st\r\n",
" ibp vmmcall sev_es fsgsbase bmi1 avx2 sm\r\n",
" ep bmi2 cqm rdt_a rdseed adx smap clflus\r\n",
" hopt clwb sha_ni xsaveopt xsavec xgetbv1\r\n",
" xsaves cqm_llc cqm_occup_llc cqm_mbm_to\r\n",
" tal cqm_mbm_local clzero irperf xsaveerp\r\n",
" tr rdpru wbnoinvd amd_ppin arat npt lbrv\r\n",
" svm_lock nrip_save tsc_scale vmcb_clean\r\n",
" flushbyasid decodeassists pausefilter p\r\n",
" fthreshold avic v_vmsave_vmload vgif umi\r\n",
" p rdpid overflow_recov succor smca\r\n"
]
}
],
"source": [
"!lscpu"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "80b0066a",
"metadata": {},
"outputs": [],
"source": [
"\n",
"ncpu = 8\n",
"nthread = 16"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "2bdab178",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<module 'pyFAI' from '/home/jerome/workspace-ssd/pyFAI/build/lib.linux-x86_64-3.9/pyFAI/__init__.py'>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%matplotlib nbagg\n",
"from matplotlib.pyplot import subplots\n",
"import sys\n",
"import os\n",
"import time\n",
"import subprocess\n",
"sys.path.insert(1, os.path.abspath(\"../build/lib.linux-x86_64-3.9/\"))\n",
"import pyopencl, numpy\n",
"from pyopencl import array\n",
"import fabio\n",
"import pyFAI\n",
"from pyFAI.test.utilstest import UtilsTest\n",
"#os.environ[\"PYOPENCL_CTX\"] = \"0:1\"\n",
"# os.environ[\"PYOPENCL_COMPILER_OUTPUT\"]=\"1\"\n",
"from pyFAI.gui import jupyter\n",
"import pyFAI.azimuthalIntegrator\n",
"from matplotlib.pyplot import subplots\n",
"pyFAI"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "cf9611b9",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Detector Detector\t Spline= None\t PixelSize= 1.720e-04, 1.720e-04 m\n",
"SampleDetDist= 3.000000e-01m\tPONI= 2.254060e-01, 2.285880e-01m\trot1=0.000000 rot2= 0.000000 rot3= 0.000000 rad\n",
"DirectBeamDist= 300.000mm\tCenter: x=1329.000, y=1310.500 pix\tTilt=0.000 deg tiltPlanRotation= 0.000 deg"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"img = fabio.open(UtilsTest.getimage(\"Pilatus6M.cbf\")).data\n",
"ai = pyFAI.load(UtilsTest.getimage(\"Pilatus6M.poni\"))\n",
"npt = 1000\n",
"split = \"no\"\n",
"method = [split, \"csr\", \"cython\"]\n",
"unit=\"r_mm\"\n",
"ai"
]
},
{
"cell_type": "markdown",
"id": "6affff8a",
"metadata": {},
"source": [
"## Enforcing the placement of threads on core using `Taskset`"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "d15fd72a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2048103\n",
"masque d'affinité actuel du PID 2048103 : ffff\n",
"\n"
]
}
],
"source": [
"pid = os.getpid()\n",
"print(pid)\n",
"res = subprocess.run([\"taskset\", \"-p\", str(pid)], capture_output=True)\n",
"print(res.stdout.decode())"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "34aa55f3",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"WARNING:pyFAI.ext.splitBBoxCSR:Pixel splitting desactivated !\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 1 0b1 0x1 taskset -a -p 1 2048103\n",
"662 ms ± 18.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"1 3 0b11 0x3 taskset -a -p 3 2048103\n",
"390 ms ± 24 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"2 7 0b111 0x7 taskset -a -p 7 2048103\n",
"312 ms ± 13.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"3 15 0b1111 0xf taskset -a -p f 2048103\n",
"235 ms ± 5.59 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"4 31 0b11111 0x1f taskset -a -p 1f 2048103\n",
"206 ms ± 7.69 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"5 63 0b111111 0x3f taskset -a -p 3f 2048103\n",
"170 ms ± 2.48 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"6 127 0b1111111 0x7f taskset -a -p 7f 2048103\n",
"156 ms ± 1.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"7 255 0b11111111 0xff taskset -a -p ff 2048103\n",
"136 ms ± 3.24 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"8 511 0b111111111 0x1ff taskset -a -p 1ff 2048103\n",
"126 ms ± 1.66 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"9 1023 0b1111111111 0x3ff taskset -a -p 3ff 2048103\n",
"122 ms ± 1.62 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"10 2047 0b11111111111 0x7ff taskset -a -p 7ff 2048103\n",
"117 ms ± 1.74 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"11 4095 0b111111111111 0xfff taskset -a -p fff 2048103\n",
"115 ms ± 2.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"12 8191 0b1111111111111 0x1fff taskset -a -p 1fff 2048103\n",
"112 ms ± 2.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"13 16383 0b11111111111111 0x3fff taskset -a -p 3fff 2048103\n",
"105 ms ± 918 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"14 32767 0b111111111111111 0x7fff taskset -a -p 7fff 2048103\n",
"99.9 ms ± 387 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"15 65535 0b1111111111111111 0xffff taskset -a -p ffff 2048103\n",
"65.6 ms ± 8.62 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
]
}
],
"source": [
"method = [split, \"csr\", \"cython\"]\n",
"warmup = ai.integrate1d(img, npt, unit=unit, method=method)\n",
"time.sleep(1)\n",
"omp_seq = {}\n",
"m=0\n",
"for j in range(0,nthread):\n",
" m = m | 1<<j\n",
" print(j, m, bin(m), hex(m), f\"taskset -a -p {m:x} {pid}\")\n",
" os.system(f\"taskset -a -p {m:x} {pid}\")\n",
" omp_seq[j+1] = %timeit -o ai.integrate1d(img, npt, unit=unit, method=method)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "fd68fb3c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"IntegrationMethod(1d int, no split, CSR, OpenCL, Intel(R) OpenCL / AMD EPYC 7262 8-Core Processor)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Select the OpenCL integrator\n",
"ai_methods = pyFAI.method_registry.IntegrationMethod.select_method(dim=1, \n",
" split=split, algo=\"csr\", impl=\"opencl\")\n",
"ai_methods = [ai_method for ai_method in ai_methods if \"Intel\" \n",
" in ai_method.target_name]\n",
"method = ai_methods[0]\n",
"method"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "92220cf0",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/jerome/.venv/py39/lib/python3.9/site-packages/pyopencl/__init__.py:267: CompilerWarning: Non-empty compiler output encountered. Set the environment variable PYOPENCL_COMPILER_OUTPUT=1 to see more.\n",
" warn(\"Non-empty compiler output encountered. Set the \"\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 1 0b1 0x1 taskset -a -p 1 2048103\n",
"202 ms ± 13.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n",
"1 3 0b11 0x3 taskset -a -p 3 2048103\n",
"106 ms ± 3.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"2 7 0b111 0x7 taskset -a -p 7 2048103\n",
"72.2 ms ± 1.09 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"3 15 0b1111 0xf taskset -a -p f 2048103\n",
"55.1 ms ± 958 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"4 31 0b11111 0x1f taskset -a -p 1f 2048103\n",
"46.3 ms ± 746 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"5 63 0b111111 0x3f taskset -a -p 3f 2048103\n",
"39.6 ms ± 761 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"6 127 0b1111111 0x7f taskset -a -p 7f 2048103\n",
"36.4 ms ± 699 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"7 255 0b11111111 0xff taskset -a -p ff 2048103\n",
"32.2 ms ± 462 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"8 511 0b111111111 0x1ff taskset -a -p 1ff 2048103\n",
"30.8 ms ± 698 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"9 1023 0b1111111111 0x3ff taskset -a -p 3ff 2048103\n",
"29.7 ms ± 488 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"10 2047 0b11111111111 0x7ff taskset -a -p 7ff 2048103\n",
"28.4 ms ± 379 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"11 4095 0b111111111111 0xfff taskset -a -p fff 2048103\n",
"27.2 ms ± 278 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"12 8191 0b1111111111111 0x1fff taskset -a -p 1fff 2048103\n",
"26.5 ms ± 400 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"13 16383 0b11111111111111 0x3fff taskset -a -p 3fff 2048103\n",
"25.5 ms ± 253 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"14 32767 0b111111111111111 0x7fff taskset -a -p 7fff 2048103\n",
"24.6 ms ± 229 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n",
"15 65535 0b1111111111111111 0xffff taskset -a -p ffff 2048103\n",
"23.7 ms ± 47.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
]
}
],
"source": [
"warmup = ai.integrate1d(img, npt, unit=unit, method=method)\n",
"time.sleep(1)\n",
"ocl_seq = {}\n",
"m=0\n",
"for j in range(0,nthread):\n",
" m = m | 1<<j\n",
" print(j, m, bin(m), hex(m), f\"taskset -a -p {m:x} {pid}\")\n",
" os.system(f\"taskset -a -p {m:x} {pid}\")\n",
" ocl_seq[j+1] = %timeit -o ai.integrate1d(img, npt, unit=unit, method=method)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "77f99958",
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"/* global mpl */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function () {\n",
" if (typeof WebSocket !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof MozWebSocket !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert(\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",
"\n",
"mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = this.ws.binaryType !== undefined;\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById('mpl-warnings');\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent =\n",
" 'This browser does not support binary websocket messages. ' +\n",
" 'Performance may be slow.';\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = 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_dpi_ratio', { dpi_ratio: fig.ratio });\n",
" }\n",
" fig.send_message('refresh', {});\n",
" };\n",
"\n",
" this.imageObj.onload = function () {\n",
" if (fig.image_mode === 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function () {\n",
" fig.ws.close();\n",
" };\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"};\n",
"\n",
"mpl.figure.prototype._init_header = function () {\n",
" var titlebar = 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",
"\n",
"mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n",
"\n",
"mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n",
"\n",
"mpl.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",
" if (this.ratio !== 1) {\n",
" fig.send_message('set_dpi_ratio', { dpi_ratio: this.ratio });\n",
" }\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 (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",
" // 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",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', { width: x_pixels, height: y_pixels });\n",
"};\n",
"\n",
"mpl.figure.prototype.send_message = function (type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"};\n",
"\n",
"mpl.figure.prototype.send_draw_message = function () {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_resize = function (fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1], msg['forward']);\n",
" fig.send_message('refresh', {});\n",
" }\n",
"};\n",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_cursor = function (fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch (cursor) {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_message = function (fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_draw = function (fig, _msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"};\n",
"\n",
"mpl.figure.prototype.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",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype.updated_canvas_event = function () {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message('ack', {});\n",
"};\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function (fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = 'image/png';\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src\n",
" );\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data\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 http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function (e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e) {\n",
" e = window.event;\n",
" }\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",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys(original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object') {\n",
" obj[key] = original[key];\n",
" }\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function (event, name) {\n",
" var canvas_pos = mpl.findpos(event);\n",
"\n",
" if (name === 'button_press') {\n",
" 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",
"\n",
"mpl.figure.prototype._key_event_extra = function (_event, _name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"};\n",
"\n",
"mpl.figure.prototype.key_event = function (event, name) {\n",
" // Prevent repeat events\n",
" if (name === 'key_press') {\n",
" if (event.which === this._key) {\n",
" return;\n",
" } else {\n",
" this._key = event.which;\n",
" }\n",
" }\n",
" if (name === 'key_release') {\n",
" this._key = null;\n",
" }\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which !== 17) {\n",
" value += 'ctrl+';\n",
" }\n",
" if (event.altKey && event.which !== 18) {\n",
" value += 'alt+';\n",
" }\n",
" if (event.shiftKey && event.which !== 16) {\n",
" value += 'shift+';\n",
" }\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n",
" return false;\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function (name) {\n",
" if (name === 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message('toolbar_button', { name: name });\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"\n",
"///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n",
"// prettier-ignore\n",
"var _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\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"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, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";/* global mpl */\n",
"\n",
"var comm_websocket_adapter = function (comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function () {\n",
" comm.close();\n",
" };\n",
" ws.send = function (m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function (msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data']);\n",
" });\n",
" return ws;\n",
"};\n",
"\n",
"mpl.mpl_figure_comm = function (comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = 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(\"<div id='\" + id + \"'></div>\");\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",
"\n",
"mpl.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",
" '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
" fig.close_ws(fig, msg);\n",
"};\n",
"\n",
"mpl.figure.prototype.close_ws = function (fig, msg) {\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"};\n",
"\n",
"mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width / this.ratio;\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] =\n",
" '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
"};\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function () {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message('ack', {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () {\n",
" fig.push_to_output();\n",
" }, 1000);\n",
"};\n",
"\n",
"mpl.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 = '<i class=\"fa ' + image + ' fa-lg\"></i>';\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 = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\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",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype._root_extra_style = function (el) {\n",
" el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n",
"};\n",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype._key_event_extra = function (event, _name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager) {\n",
" manager = IPython.keyboard_manager;\n",
" }\n",
"\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",
"\n",
"mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
" fig.ondownload(fig, null);\n",
"};\n",
"\n",
"mpl.find_output_cell = function (html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i = 0; i < ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code') {\n",
" for (var j = 0; j < cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] === html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"};\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel !== null) {\n",
" IPython.notebook.kernel.comm_manager.register_target(\n",
" 'matplotlib',\n",
" mpl.mpl_figure_comm\n",
" );\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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\" width=\"640\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"fig,ax = subplots()\n",
"x = list(omp_seq.keys())\n",
"reference = min(omp_seq[1].best, ocl_seq[1].best)\n",
"y_omp = [reference/i.best for i in omp_seq.values()]\n",
"y_ocl = [reference/i.best for i in ocl_seq.values()]\n",
"ax.plot(x,numpy.clip(x, 0, ncpu), label=\"Ideal\")\n",
"ax.plot(x,y_omp, label=\"OpenMP speed-up\")\n",
"ax.plot(x,y_ocl, label=\"OpenCL speed-up\")\n",
"\n",
"ax.set_xlabel(\"Number of threads\")\n",
"ax.set_ylabel(\"Actual speed-up\")\n",
"ax.set_title(\"Csr integration on 6Mpix image\")\n",
"fig.suptitle(f\"PyFAI on EPYC 7262 {ncpu}cores / {nthread}threads\")\n",
"_=ax.legend()"
]
},
{
"cell_type": "markdown",
"id": "b51e4b85",
"metadata": {},
"source": [
"## Profile external processes to ensure a proper initialization of OpenMP"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "882a8d4e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OpenMP excution time: {res.stdout.decode().strip()} s\n",
"OpenCL excution time: {res.stdout.decode().strip()} s\n"
]
}
],
"source": [
"script_omp = f\"\"\"\n",
"import timeit\n",
"repeat = 5\n",
"number = 10\n",
"\n",
"warmup = '''\n",
"import pyFAI, fabio, timeit, time\n",
"from pyFAI.test.utilstest import UtilsTest\n",
"img = fabio.open(UtilsTest.getimage(\"Pilatus6M.cbf\")).data\n",
"ai = pyFAI.load(UtilsTest.getimage(\"Pilatus6M.poni\"))\n",
"npt = {npt}\n",
"method = ['{split}', \"csr\", \"cython\"]\n",
"unit=\"r_mm\"\n",
"warmup = ai.integrate1d(img, npt, unit=unit, method=method)\n",
"'''\n",
"\n",
"res = timeit.repeat(stmt='ai.integrate1d(img, npt, unit=unit, method=method)', \n",
" setup=warmup,\n",
" repeat=repeat, \n",
" number=number, \n",
" globals=locals())\n",
"print(min(res)/number)\n",
"\"\"\"\n",
"\n",
"script_ocl = f\"\"\"\n",
"import timeit\n",
"repeat = 5\n",
"number = 10\n",
"\n",
"warmup = '''\n",
"import pyFAI, fabio, timeit, time\n",
"from pyFAI.test.utilstest import UtilsTest\n",
"img = fabio.open(UtilsTest.getimage(\"Pilatus6M.cbf\")).data\n",
"ai = pyFAI.load(UtilsTest.getimage(\"Pilatus6M.poni\"))\n",
"npt = {npt}\n",
"import pyFAI.azimuthalIntegrator\n",
"ai_methods = pyFAI.method_registry.IntegrationMethod.select_method(dim=1, \n",
" split='{split}', algo=\"csr\", impl=\"opencl\")\n",
"ai_methods = [ai_method for ai_method in ai_methods if \"Intel\" in ai_method.target_name]\n",
"method = ai_methods[0]\n",
"unit=\"r_mm\"\n",
"warmup = ai.integrate1d(img, npt, unit=unit, method=method)\n",
"'''\n",
"\n",
"res = timeit.repeat(stmt='ai.integrate1d(img, npt, unit=unit, method=method)', \n",
" setup=warmup,\n",
" repeat=repeat, \n",
" number=number, \n",
" globals=locals())\n",
"print(min(res)/number)\n",
"\"\"\"\n",
"\n",
"with open(\"script_omp.py\", \"w\") as prg:\n",
" prg.write(script_omp)\n",
"res=subprocess.run([\"python3\", \"script_omp.py\"], capture_output=True)\n",
"print(\"OpenMP excution time: {res.stdout.decode().strip()} s\")\n",
"\n",
"\n",
"with open(\"script_ocl.py\", \"w\") as prg:\n",
" prg.write(script_ocl)\n",
"res=subprocess.run([\"python3\", \"script_ocl.py\"], capture_output=True)\n",
"print(\"OpenCL excution time: {res.stdout.decode().strip()} s\")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "7f1e1ede",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 0b1 0x1 omp: 155.84781211800873, ocl: 175.9440187830478 ms\n",
"1 0b11 0x3 omp: 127.49150283634664, ocl: 89.12619799375534 ms\n",
"2 0b111 0x7 omp: 102.71244808100164, ocl: 62.889611721038825 ms\n",
"3 0b1111 0xf omp: 88.89783849008381, ocl: 48.8206519279629 ms\n",
"4 0b11111 0x1f omp: 81.39346721582115, ocl: 42.210298124700785 ms\n",
"5 0b111111 0x3f omp: 74.05657176859677, ocl: 36.33927311748266 ms\n",
"6 0b1111111 0x7f omp: 70.29799562878907, ocl: 33.407765021547675 ms\n",
"7 0b11111111 0xff omp: 66.99484218843281, ocl: 30.810917355120182 ms\n",
"8 0b111111111 0x1ff omp: 69.52240257523954, ocl: 29.973910097032785 ms\n",
"9 0b1111111111 0x3ff omp: 68.01167340017855, ocl: 28.939576633274555 ms\n",
"10 0b11111111111 0x7ff omp: 66.70261640101671, ocl: 28.38998017832637 ms\n",
"11 0b111111111111 0xfff omp: 64.52505881898105, ocl: 27.331707999110222 ms\n",
"12 0b1111111111111 0x1fff omp: 62.83443118445575, ocl: 27.060849498957396 ms\n",
"13 0b11111111111111 0x3fff omp: 61.9805843103677, ocl: 26.170672103762627 ms\n",
"14 0b111111111111111 0x7fff omp: 61.14264577627182, ocl: 25.58348341844976 ms\n",
"15 0b1111111111111111 0xffff omp: 60.1161336991936, ocl: 25.414626579731703 ms\n"
]
}
],
"source": [
"omp_seq = {}\n",
"ocl_seq = {}\n",
"m = 0\n",
"for j in range(0,nthread):\n",
" m = m | 1<<j\n",
" res_omp = subprocess.run([\"taskset\", \"-a\", hex(m), \"python3\", \"script_omp.py\"], capture_output=True)\n",
" omp_seq[j+1] = float(res_omp.stdout.decode())\n",
" res_ocl = subprocess.run([\"taskset\", \"-a\", hex(m), \"python3\", \"script_ocl.py\"], capture_output=True)\n",
" print(f\"{j} {bin(m)} {hex(m)} omp: {float(res_omp.stdout.decode())*1000}, ocl: {float(res_ocl.stdout.decode())*1000} ms\")\n",
" ocl_seq[j+1] = float(res_ocl.stdout.decode())"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "c1f6d5cc",
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"/* global mpl */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function () {\n",
" if (typeof WebSocket !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof MozWebSocket !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert(\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",
"\n",
"mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = this.ws.binaryType !== undefined;\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById('mpl-warnings');\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent =\n",
" 'This browser does not support binary websocket messages. ' +\n",
" 'Performance may be slow.';\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = 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_dpi_ratio', { dpi_ratio: fig.ratio });\n",
" }\n",
" fig.send_message('refresh', {});\n",
" };\n",
"\n",
" this.imageObj.onload = function () {\n",
" if (fig.image_mode === 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function () {\n",
" fig.ws.close();\n",
" };\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"};\n",
"\n",
"mpl.figure.prototype._init_header = function () {\n",
" var titlebar = 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",
"\n",
"mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n",
"\n",
"mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n",
"\n",
"mpl.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",
" if (this.ratio !== 1) {\n",
" fig.send_message('set_dpi_ratio', { dpi_ratio: this.ratio });\n",
" }\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 (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",
" // 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",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', { width: x_pixels, height: y_pixels });\n",
"};\n",
"\n",
"mpl.figure.prototype.send_message = function (type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"};\n",
"\n",
"mpl.figure.prototype.send_draw_message = function () {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_resize = function (fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1], msg['forward']);\n",
" fig.send_message('refresh', {});\n",
" }\n",
"};\n",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_cursor = function (fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch (cursor) {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_message = function (fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_draw = function (fig, _msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"};\n",
"\n",
"mpl.figure.prototype.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",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype.updated_canvas_event = function () {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message('ack', {});\n",
"};\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function (fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = 'image/png';\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src\n",
" );\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data\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 http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function (e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e) {\n",
" e = window.event;\n",
" }\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",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys(original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object') {\n",
" obj[key] = original[key];\n",
" }\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function (event, name) {\n",
" var canvas_pos = mpl.findpos(event);\n",
"\n",
" if (name === 'button_press') {\n",
" 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",
"\n",
"mpl.figure.prototype._key_event_extra = function (_event, _name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"};\n",
"\n",
"mpl.figure.prototype.key_event = function (event, name) {\n",
" // Prevent repeat events\n",
" if (name === 'key_press') {\n",
" if (event.which === this._key) {\n",
" return;\n",
" } else {\n",
" this._key = event.which;\n",
" }\n",
" }\n",
" if (name === 'key_release') {\n",
" this._key = null;\n",
" }\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which !== 17) {\n",
" value += 'ctrl+';\n",
" }\n",
" if (event.altKey && event.which !== 18) {\n",
" value += 'alt+';\n",
" }\n",
" if (event.shiftKey && event.which !== 16) {\n",
" value += 'shift+';\n",
" }\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n",
" return false;\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function (name) {\n",
" if (name === 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message('toolbar_button', { name: name });\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"\n",
"///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n",
"// prettier-ignore\n",
"var _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\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"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, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";/* global mpl */\n",
"\n",
"var comm_websocket_adapter = function (comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function () {\n",
" comm.close();\n",
" };\n",
" ws.send = function (m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function (msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data']);\n",
" });\n",
" return ws;\n",
"};\n",
"\n",
"mpl.mpl_figure_comm = function (comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = 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(\"<div id='\" + id + \"'></div>\");\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",
"\n",
"mpl.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",
" '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
" fig.close_ws(fig, msg);\n",
"};\n",
"\n",
"mpl.figure.prototype.close_ws = function (fig, msg) {\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"};\n",
"\n",
"mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var width = this.canvas.width / this.ratio;\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] =\n",
" '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
"};\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function () {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message('ack', {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () {\n",
" fig.push_to_output();\n",
" }, 1000);\n",
"};\n",
"\n",
"mpl.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 = '<i class=\"fa ' + image + ' fa-lg\"></i>';\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 = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\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",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype._root_extra_style = function (el) {\n",
" el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n",
"};\n",
"\n",
"mpl.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",
"\n",
"mpl.figure.prototype._key_event_extra = function (event, _name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager) {\n",
" manager = IPython.keyboard_manager;\n",
" }\n",
"\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",
"\n",
"mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
" fig.ondownload(fig, null);\n",
"};\n",
"\n",
"mpl.find_output_cell = function (html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i = 0; i < ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code') {\n",
" for (var j = 0; j < cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] === html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"};\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel !== null) {\n",
" IPython.notebook.kernel.comm_manager.register_target(\n",
" 'matplotlib',\n",
" mpl.mpl_figure_comm\n",
" );\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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\" width=\"640\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"fig, ax = subplots()\n",
"x = list(omp_seq.keys())\n",
"reference = min(omp_seq[1], ocl_seq[1])\n",
"y_omp_seq = [reference/i for i in omp_seq.values()]\n",
"y_ocl_seq = [reference/i for i in ocl_seq.values()]\n",
"\n",
"ax.plot(x,numpy.clip(x,0,ncpu), label=\"Ideal\")\n",
"ax.plot(x, y_omp_seq, label=\"OpenMP speed-up\")\n",
"ax.plot(x, y_ocl_seq, label=\"OpenCL speed-up\")\n",
"\n",
"\n",
"ax.set_xlabel(\"Number of threads\")\n",
"ax.set_ylabel(\"Actual speed-up, vs best single threaded\")\n",
"ax.set_title(f\"MP/CL {split}-csr integration on 6Mpix image\")\n",
"fig.suptitle(f\"PyFAI on EPYC 7262 {ncpu}cores / {nthread}threads\")\n",
"_=ax.legend()"
]
},
{
"cell_type": "markdown",
"id": "68f8a9e6",
"metadata": {},
"source": [
"## Conclusion:\n",
"\n",
"`OpenMP` and probably `OpenCL` counts the number of threads at start-time ... which disturbs the measurement of he performances."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.9 (local venv)",
"language": "python",
"name": "py39-venv"
},
"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.9.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment