From 27e196fbd9ef3af1cd90ac2acdb4c38b245ecffc Mon Sep 17 00:00:00 2001 From: Chichi Lalescu <clalesc1@jhu.edu> Date: Fri, 27 Feb 2015 14:06:32 -0500 Subject: [PATCH] use MPI_FLOAT instead of MPI_REAL4 --- makefile | 2 +- src/Morton_shuffler.cpp | 12 +- src/fftwf_tools.cpp | 2 +- src/field_descriptor.cpp | 12 +- test3.ipynb | 709 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 694 insertions(+), 43 deletions(-) diff --git a/makefile b/makefile index 5e0bc143..d85138e3 100644 --- a/makefile +++ b/makefile @@ -23,7 +23,7 @@ MPICXX = mpicxx LINKER = mpicxx -DEFINES = -DNDEBUG +DEFINES = #-DNDEBUG CFLAGS = -Wall \ -O2 \ #-pg \ diff --git a/src/Morton_shuffler.cpp b/src/Morton_shuffler.cpp index 7d97447c..c351c1a7 100644 --- a/src/Morton_shuffler.cpp +++ b/src/Morton_shuffler.cpp @@ -53,15 +53,15 @@ Morton_shuffler::Morton_shuffler( n[1] = N1; n[2] = N2; n[3] = this->d; - this->dinput = new field_descriptor(4, n, MPI_REAL4, MPI_COMM_WORLD); + this->dinput = new field_descriptor(4, n, MPI_FLOAT, MPI_COMM_WORLD); n[0] = N0/8; n[1] = N1/8; n[2] = N2/8; n[3] = 8*8*8*this->d; - this->drcubbie = new field_descriptor(4, n, MPI_REAL4, MPI_COMM_WORLD); + this->drcubbie = new field_descriptor(4, n, MPI_FLOAT, MPI_COMM_WORLD); n[0] = (N0/8) * (N1/8) * (N2/8); n[1] = 8*8*8*this->d; - this->dzcubbie = new field_descriptor(2, n, MPI_REAL4, MPI_COMM_WORLD); + this->dzcubbie = new field_descriptor(2, n, MPI_FLOAT, MPI_COMM_WORLD); //set up output file descriptor int out_rank, out_nprocs; @@ -78,7 +78,7 @@ Morton_shuffler::Morton_shuffler( n[0] = ((N0/8) * (N1/8) * (N2/8)) / nfiles; n[1] = 8*8*8*this->d; MPI_Comm_split(MPI_COMM_WORLD, this->out_group, out_rank, &this->out_communicator); - this->doutput = new field_descriptor(2, n, MPI_REAL4, this->out_communicator); + this->doutput = new field_descriptor(2, n, MPI_FLOAT, this->out_communicator); } Morton_shuffler::~Morton_shuffler() @@ -138,14 +138,14 @@ int Morton_shuffler::shuffle( if (myrank == rid) MPI_Send( rz, cubbie_size, - MPI_REAL4, + MPI_FLOAT, zid, z, MPI_COMM_WORLD); else MPI_Recv( rtmp + zz*cubbie_size, cubbie_size, - MPI_REAL4, + MPI_FLOAT, rid, z, MPI_COMM_WORLD, diff --git a/src/fftwf_tools.cpp b/src/fftwf_tools.cpp index 67d7a818..5135a795 100644 --- a/src/fftwf_tools.cpp +++ b/src/fftwf_tools.cpp @@ -163,7 +163,7 @@ int fftwf_get_descriptors_3D( ntmp[0] = n0; ntmp[1] = n1; ntmp[2] = n2; - *fr = new field_descriptor(3, ntmp, MPI_REAL4, MPI_COMM_WORLD); + *fr = new field_descriptor(3, ntmp, MPI_FLOAT, MPI_COMM_WORLD); ntmp[0] = n0; ntmp[1] = n1; ntmp[2] = n2/2+1; diff --git a/src/field_descriptor.cpp b/src/field_descriptor.cpp index ce9b145f..fba906b8 100644 --- a/src/field_descriptor.cpp +++ b/src/field_descriptor.cpp @@ -160,7 +160,7 @@ field_descriptor::field_descriptor( tsubsizes, tstarts, MPI_ORDER_C, - MPI_REAL4, + MPI_FLOAT, &this->mpi_array_dtype); MPI_Type_commit(&this->mpi_array_dtype); } @@ -232,15 +232,15 @@ int field_descriptor::read( MPI_File_set_view( f, 0, - MPI_REAL4, + MPI_FLOAT, this->mpi_array_dtype, - "native", //this needs to be made more general + "external32", //this needs to be made more general info); MPI_File_read_all( f, buffer, read_size, - MPI_REAL4, + MPI_FLOAT, MPI_STATUS_IGNORE); MPI_File_close(&f); } @@ -271,7 +271,7 @@ int field_descriptor::write( MPI_File_set_view( f, 0, - MPI_REAL4, + MPI_FLOAT, this->mpi_array_dtype, "native", //this needs to be made more general info); @@ -279,7 +279,7 @@ int field_descriptor::write( f, buffer, read_size, - MPI_REAL4, + MPI_FLOAT, MPI_STATUS_IGNORE); MPI_File_close(&f); } diff --git a/test3.ipynb b/test3.ipynb index da102d8e..538027d7 100644 --- a/test3.ipynb +++ b/test3.ipynb @@ -1,7 +1,7 @@ { "metadata": { "name": "", - "signature": "sha256:36cd47ff064b589a2110031c054c1d9eaa0011c1b0627ee83866e04516e42c63" + "signature": "sha256:f59c9e48a9601e13f2d225340408e49bbfb8c42b2b77b5262263c89e6773b9da" }, "nbformat": 3, "nbformat_minor": 0, @@ -21,7 +21,7 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 1 + "prompt_number": 3 }, { "cell_type": "code", @@ -57,22 +57,14 @@ "Kdata0 = generate_data_3D(n, p = 2).astype(np.complex64)\n", "Kdata1 = generate_data_3D(n, p = 2).astype(np.complex64)\n", "Kdata2 = generate_data_3D(n, p = 2).astype(np.complex64)\n", - "Kdata0.T.copy().tofile(\"Kdata0\")\n", - "Kdata1.T.copy().tofile(\"Kdata1\")\n", - "Kdata2.T.copy().tofile(\"Kdata2\")" + "Kdata0.T.copy().astype('>c8').tofile(\"Kdata0\")\n", + "Kdata1.T.copy().astype('>c8').tofile(\"Kdata1\")\n", + "Kdata2.T.copy().astype('>c8').tofile(\"Kdata2\")" ], "language": "python", "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stderr", - "text": [ - "-c:15: RuntimeWarning: divide by zero encountered in true_divide\n" - ] - } - ], - "prompt_number": 2 + "outputs": [], + "prompt_number": 37 }, { "cell_type": "code", @@ -126,19 +118,12 @@ " 0:a.shape[2]//8]\n", " bindices = np.array([bi, bj, bk])\n", " cindices = cm.grid3D_to_zindex(bindices)\n", - " plist = []\n", - " zlist = []\n", " for k in range(a.shape[0]//8):\n", " for j in range(a.shape[1]//8):\n", " for i in range(a.shape[2]//8):\n", " z = cm.grid3D_to_zindex(np.array([k, j, i]))\n", " c[z] = a[8*k:8*(k+1), 8*j:8*(j+1), 8*i:8*(i+1)]\n", - " plist.append([i, j, k])\n", - " zlist.append(z)\n", - " plist = np.array(plist)\n", - " zlist = np.array(zlist)\n", - " i = np.argsort(zlist)\n", - " return c, zlist[i], plist[i]\n", + " return c\n", "\n", "d0 = transform_py(Kdata0)\n", "d1 = transform_py(Kdata1)\n", @@ -146,7 +131,7 @@ "\n", "Rdata_py_tmp = np.array([d0, d1, d2]).transpose((1, 2, 3, 0))\n", "\n", - "Rdata_py, zlist, plist = array_to_8cubes(Rdata_py_tmp)\n", + "Rdata_py = array_to_8cubes(Rdata_py_tmp)\n", "\n", "# i0 = np.random.randint(16)\n", "# i1 = np.random.randint(16)\n", @@ -156,7 +141,7 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 3 + "prompt_number": 38 }, { "cell_type": "code", @@ -209,13 +194,14 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 19 + "prompt_number": 39 }, { "cell_type": "code", "collapsed": true, "input": [ "distance = np.max(np.abs(Rdata_py - Rdata), axis = (1, 2, 3, 4))\n", + "print(np.max(distance))\n", "if np.max(distance) > 1e-5:\n", " ax = plt.figure(figsize=(6,2)).add_subplot(111)\n", " ax.plot(distance)\n", @@ -237,7 +223,8 @@ " print (ta0[ta0.shape[0]/2-1:ta0.shape[0]/2+7])\n", " print (ta1[ta1.shape[0]/2-1:ta1.shape[0]/2+7])\n", "else:\n", - " print('distance is small')" + " print('distance is small')\n", + "print(np.max(np.abs(Rdata)))" ], "language": "python", "metadata": {}, @@ -246,11 +233,675 @@ "output_type": "stream", "stream": "stdout", "text": [ - "distance is small\n" + "14.3243\n" + ] + }, + { + "javascript": [ + "/* Put everything inside the global mpl namespace */\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('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox \u2265 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.focus_on_mousover = false;\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('<div/>');\n", + " this.root.attr('style', 'display: inline-block');\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " 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", + " fig.waiting = false;\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", + " 'ui-helper-clearfix\"/>');\n", + " var titletext = $(\n", + " '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", + " 'text-align: center; padding: 3px;\"/>');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('<div/>');\n", + " canvas_div.resizable({ resize: mpl.debounce_resize(\n", + " function(event, ui) { fig.request_resize(ui.size.width, ui.size.height); }\n", + " , 50)});\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both;');\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('<canvas/>');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0;\")\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('<canvas/>');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + " function mouse_event_fn(event) {\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keydown('key_release', canvas_keyboard_event);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('<button/>');\n", + " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", + " 'ui-button-icon-only');\n", + " button.attr('role', 'button');\n", + " button.attr('aria-disabled', 'false');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + "\n", + " var icon_img = $('<span/>');\n", + " icon_img.addClass('ui-button-icon-primary ui-icon');\n", + " icon_img.addClass(image);\n", + " icon_img.addClass('ui-corner-all');\n", + "\n", + " var tooltip_span = $('<span/>');\n", + " tooltip_span.addClass('ui-button-text');\n", + " tooltip_span.html(tooltip);\n", + "\n", + " button.append(icon_img);\n", + " button.append(tooltip_span);\n", + "\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " var fmt_picker_span = $('<span/>');\n", + "\n", + " var fmt_picker = $('<select/>');\n", + " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", + " fmt_picker_span.append(fmt_picker);\n", + " nav_element.append(fmt_picker_span);\n", + " this.format_dropdown = fmt_picker[0];\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = $(\n", + " '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", + " fmt_picker.append(option)\n", + " }\n", + "\n", + " // Add hover states to the ui-buttons\n", + " $( \".ui-button\" ).hover(\n", + " function() { $(this).addClass(\"ui-state-hover\");},\n", + " function() { $(this).removeClass(\"ui-state-hover\");}\n", + " );\n", + "\n", + " var status_bar = $('<span class=\"mpl-message\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "}\n", + "\n", + "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", + "}\n", + "\n", + "mpl.figure.prototype.send_message = function(type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "}\n", + "\n", + "mpl.figure.prototype.send_draw_message = function() {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_resize = function(fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1]);\n", + " fig.send_message(\"refresh\", {});\n", + " };\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", + " var x0 = msg['x0'];\n", + " var y0 = fig.canvas.height - msg['y0'];\n", + " var x1 = msg['x1'];\n", + " var y1 = fig.canvas.height - msg['y1'];\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0, 0, fig.canvas.width, fig.canvas.height);\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", + " var cursor = msg['cursor'];\n", + " switch(cursor)\n", + " {\n", + " case 0:\n", + " cursor = 'pointer';\n", + " break;\n", + " case 1:\n", + " cursor = 'default';\n", + " break;\n", + " case 2:\n", + " cursor = 'crosshair';\n", + " break;\n", + " case 3:\n", + " cursor = 'move';\n", + " break;\n", + " }\n", + " fig.rubberband_canvas.style.cursor = cursor;\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_message = function(fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_draw = function(fig, msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message(\"ack\", {});\n", + "}\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function(fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " evt.data.type = \"image/png\";\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src);\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " evt.data);\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig[\"handle_\" + msg_type];\n", + " } catch (e) {\n", + " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", + " }\n", + " }\n", + " };\n", + "}\n", + "\n", + "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function(e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e)\n", + " e = window.event;\n", + " if (e.target)\n", + " targ = e.target;\n", + " else if (e.srcElement)\n", + " targ = e.srcElement;\n", + " if (targ.nodeType == 3) // defeat Safari bug\n", + " targ = targ.parentNode;\n", + "\n", + " // jQuery normalizes the pageX and pageY\n", + " // pageX,Y are the mouse positions relative to the document\n", + " // offset() returns the position of the element relative to the document\n", + " var x = e.pageX - $(targ).offset().left;\n", + " var y = e.pageY - $(targ).offset().top;\n", + "\n", + " return {\"x\": x, \"y\": y};\n", + "};\n", + "\n", + "mpl.figure.prototype.mouse_event = function(event, name) {\n", + " var canvas_pos = mpl.findpos(event)\n", + "\n", + " if (this.focus_on_mouseover && name === 'motion_notify')\n", + " {\n", + " this.canvas.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x;\n", + " var y = canvas_pos.y;\n", + "\n", + " this.send_message(name, {x: x, y: y, button: event.button});\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 = function(event, name) {\n", + " /* Don't fire events just when a modifier is changed. Modifiers are\n", + " sent along with other keys. */\n", + " if (event.keyCode >= 16 && event.keyCode <= 20) {\n", + " return;\n", + " }\n", + "\n", + " value = '';\n", + " if (event.ctrlKey) {\n", + " value += \"ctrl+\";\n", + " }\n", + " if (event.altKey) {\n", + " value += \"alt+\";\n", + " }\n", + " value += String.fromCharCode(event.keyCode).toLowerCase();\n", + "\n", + " this.send_message(name, {key: value});\n", + "}\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", + " if (name == 'download') {\n", + " var format_dropdown = this.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " this.ondownload(this, format);\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", + "mpl.debounce_event = function(func, time){\n", + " var timer;\n", + " return function(event){\n", + " clearTimeout(timer);\n", + " timer = setTimeout(function(){ func(event); }, time);\n", + " };\n", + "}\n", + "\n", + "mpl.debounce_resize = function(func, time){\n", + " var timer;\n", + " return function(event, ui){\n", + " clearTimeout(timer);\n", + " timer = setTimeout(function(){ func(event, ui); }, time);\n", + " };\n", + "}\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.close = function() {\n", + " comm.close()\n", + " };\n", + " ws.send = function(m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function(msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " // Pass the mpl event to the overriden (by mpl) onmessage function.\n", + " ws.onmessage(msg['content']['data'])\n", + " });\n", + " return ws;\n", + "}\n", + "\n", + "mpl.mpl_figure_comm = function(comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = $(\"#\" + id);\n", + " var ws_proxy = comm_websocket_adapter(comm)\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy,\n", + " function() { },\n", + " element.get(0));\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element.get(0);\n", + " fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", + "\n", + " var output_index = fig.cell_info[2]\n", + " var cell = fig.cell_info[0];\n", + "\n", + " // Disable right mouse context menu.\n", + " $(fig.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable()\n", + " $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", + " fig.send_message('closing', {});\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 dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", + "}\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function() {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message(\"ack\", {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () { fig.push_to_output() }, 1000);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('<div/>')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items){\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) { continue; };\n", + "\n", + " var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", + " var button = $('<button class=\"btn btn-mini btn-danger\" href=\"#\" title=\"Close figure\"><i class=\"fa fa-times icon-remove icon-large\"></i></button>');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Close figure', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\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 (cell.output_area.outputs[j]['text/html'] == html_output) {\n", + " var output = cell.output_area.outputs[j];\n", + " return [cell, output, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "metadata": {}, + "output_type": "display_data", + "text": [ + "<IPython.core.display.Javascript at 0x7fd54d44e438>" + ] + }, + { + "html": [ + "<img src=\"data:image/png;base64,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\">" + ], + "metadata": {}, + "output_type": "display_data", + "text": [ + "<IPython.core.display.HTML at 0x7fd54d44e080>" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "[0 3 0]\n", + "18 0 3.30636\n", + "[-0.83038074 1.17252076 -0.34368908 -0.71512604 -3.7917273 ]\n", + "[ 0. 0. 0. 0. 0.]\n", + "[ 2.00750494 -2.65982461 0.07980549 1.95563817 -3.33233905 0.33735609\n", + " 2.60928655 -4.097404 ]\n", + "[ 0. 0. 0. 0. 0. 0. 0. 0.]" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "\n", + "0.0" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "\n" ] } ], - "prompt_number": 20 + "prompt_number": 40 }, { "cell_type": "code", -- GitLab