{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "[MIT License](https://github.com/cdslaborg/paramonte#license) \n", "[ParaMonte: plain powerful parallel Monte Carlo library](https://github.com/cdslaborg/paramonte). \n", "Copyright (C) 2012-present, [The Computational Data Science Lab](https://www.cdslab.org/#about) \n", "https://github.com/cdslaborg/paramonte \n", "[References](https://www.cdslab.org/paramonte/notes/overview/preface/#how-to-acknowledge-the-use-of-the-paramonte-library-in-your-work) " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import warnings\n", "warnings.filterwarnings('ignore')\n", "warnings.simplefilter('ignore')" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "paramonte 2.3.0\n", "numpy 1.19.2\n", "scipy 1.5.2\n", "pandas 1.1.3\n", "seaborn 0.11.0\n", "matplotlib 3.3.2\n" ] } ], "source": [ "%matplotlib notebook\n", "import paramonte as pm\n", "import numpy as np\n", "import scipy as sp\n", "import pandas as pd\n", "import seaborn as sns\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "print('\\n'.join(f'{m.__name__} {m.__version__}' for m in globals().values() if getattr(m, '__version__', None)))\n", "sns.set()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running ParaDRAM simulations in parallel on multiple processors \n", "\n", "There are **two parallelism paradigms** currently implemented **in the ParaDRAM sampler**:\n", "\n", "1. The **single-chain parallelism**, in which only a single Markov chain is generated, but all processors contribute to the construction of this chain. \n", "1. The **multi-chain parallelism**, in which each processor creates its Markov chain separately from the rest of the processors. However, at the end of the simulation, all processors communicate with each other to compute the probability that convergence to the target density has occurred and that all processors have sampled the same region of high probability in the domain of the objective function. \n", "\n", "### Which parallelism paradigm should be used when?\n", "\n", "- The **single-chain parallelism** becomes very useful for large-scale problems that are highly computationally demanding. \n", "- The **multi-chain parallelism** is useful when you suspect that the target objective function that has to be sampled is multi-modal. In such cases, the multi-chain parallelism could provide further evidence on whether convergence to single-mode or multi-modal target density function has occurred or not. \n", " \n", " \n", "In either parallelism case, the ParaMonte library currently uses the MPI library for inter-process communications. As such, if you want to run a ParaDRAM simulation in parallel, you will have to first save your Python scripts in external Python files and then call them from the command line via the MPI launcher application. \n", " \n", "\n", "To see how this can be done, consider the simple toy problem of [sampling a 4-dimensional Multivariate Normal (MVN) distribution as described in this jupyter notebook](https://nbviewer.jupyter.org/github/cdslaborg/paramontex/blob/main/Python/Jupyter/sampling_multivariate_normal_distribution_via_paradram/sampling_multivariate_normal_distribution_via_paradram.ipynb). " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n", ":::: ::::\n", "\n", " _/_/_/_/ _/_/ _/_/\n", " _/ _/ _/_/_/_/_/ _/\n", " _/ _/ _/_/_/_/ _/ /_/_/ _/_/_/_/ _/ _/ _/ _/_/ _/_/_/ _/_/_/ _/_/_/\n", " _/_/_/ _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/_/_/_/ \n", " _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ \n", " _/_/_/ _/_/_/_/ _/ _/_/_/_/ _/_/_/ _/_/_/ _/_/ _/ _/ _/_/ _/_/_/\n", "\n", "\n", " ParaMonte\n", " plain powerful parallel\n", " Monte Carlo library\n", " Version 2.3.0\n", "\n", ":::: ::::\n", ":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n", "\n", "ParaMonte - NOTE: The ParaMonte::Kernel samplers have no Python package dependencies \n", "ParaMonte - NOTE: beyond numpy. However, the ParaMonte::Python post-processing and \n", "ParaMonte - NOTE: visualization tools require the following Python packages, \n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: numpy : 1.19.2\n", "ParaMonte - NOTE: scipy : 1.5.2\n", "ParaMonte - NOTE: pandas : 1.1.2\n", "ParaMonte - NOTE: seaborn : 0.11.0\n", "ParaMonte - NOTE: matplotlib : 3.3.2\n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: If you do not intend to use the postprocessing and visualization tools, \n", "ParaMonte - NOTE: you can ignore this message. Otherwise, UPDATE THE ABOVE PACKAGES TO \n", "ParaMonte - NOTE: THE REQUESTED VERSIONS OR NEWER, SO THAT THE VISUALIZATION TOOLS \n", "ParaMonte - NOTE: OF THE ParaMonte::Python LIBRARY FUNCTION PROPERLY.\n", "\n", "\n", "ParaMonte - NOTE: Intel MPI library for 64-bit architecture detected at: \n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2020.1.216\\windows\\mpi\\intel64\\bin\n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: To perform ParaMonte simulations in parallel on a single node, \n", "ParaMonte - NOTE: run the following two commands, in the form and order specified, \n", "ParaMonte - NOTE: on a Python-aware mpiexec-aware command-line interface such as \n", "ParaMonte - NOTE: Anaconda3 Windows command prompt: \n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: \"C:\\Program Files (x86)\\IntelSWTools\\compilers_and_libraries_2020.1.216\\windows\\mpi\\intel64\\bin\\mpivars.bat\"\n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: mpiexec -localonly -n NUM_PROCESSES python main.py\n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: where, \n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: 0. the first command defines the essential environment variables, \n", "ParaMonte - NOTE: and the second command runs in the simulation in parallel, where, \n", "ParaMonte - NOTE: 1. you should replace NUM_PROCESSES with the number of processors \n", "ParaMonte - NOTE: you wish to assign to your simulation task and, \n", "ParaMonte - NOTE: 2. the flag '-localonly' indicates a parallel simulation on only \n", "ParaMonte - NOTE: a single node (this flag will obviate the need for the MPI \n", "ParaMonte - NOTE: library credentials registration). For more information, visit: \n", "ParaMonte - NOTE: https://software.intel.com/en-us/get-started-with-mpi-for-windows \n", "ParaMonte - NOTE: 3. main.py is the Python file which serves as the entry point to \n", "ParaMonte - NOTE: your simulation, where you call the ParaMonte sampler routines. \n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: Note that the above two commands must be executed on a command-line that \n", "ParaMonte - NOTE: recognizes both Python and mpiexec applications, such as the Anaconda \n", "ParaMonte - NOTE: command-line interface. For more information, in particular, on how \n", "ParaMonte - NOTE: to register to run Hydra services for multi-node simulations \n", "ParaMonte - NOTE: on Windows servers, visit: \n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: https://www.cdslab.org/paramonte\n", "\n", "\n", "ParaMonte - NOTE: To check for the MPI library installation status or display the above \n", "ParaMonte - NOTE: messages in the future, type the following on the Python command-line: \n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: import paramonte as pm\n", "ParaMonte - NOTE: pm.verify()\n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: To get started, type the following on the Python command-line,\n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: import paramonte as pm\n", "ParaMonte - NOTE: pm.helpme()\n", "\n" ] } ], "source": [ "import paramonte as pm\n", "pm.verify()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "ParaMonte - NOTE: You have the latest version of the ParaMonte library. \n", "ParaMonte - NOTE: To see the most recent changes to the library, visit, \n", "ParaMonte - NOTE: \n", "ParaMonte - NOTE: https://www.cdslab.org/paramonte/notes/overview/paramonte-python-release-notes\n", "\n" ] } ], "source": [ "pm.checkForUpdate() # check for new versions of the library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running a single-chain ParaDRAM simulation in parallel on multiple processors\n", "\n", "We will save our parallel script in a file with the same name as this Jupyter Notebook's name, " ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "with open(\"./sampling_multivariate_normal_distribution_via_paradram_parallel_singleChain.py\",\"w\") as file:\n", " contents = \"\"\"\n", "import numpy as np\n", "\n", "NDIM = 4 # number of dimensions of the domain of the MVN PDF\n", "MEAN = np.double([-10, 15., 20., 0.0]) # This is the mean of the MVN PDF.\n", "COVMAT = np.double( [ [1.0,.45,-.3,0.0] # This is the covariance matrix of the MVN PDF.\n", " , [.45,1.0,0.3,-.2]\n", " , [-.3,0.3,1.0,0.6]\n", " , [0.0,-.2,0.6,1.0]\n", " ] )\n", "\n", "INVCOV = np.linalg.inv(COVMAT) # This is the inverse of the covariance matrix of the MVN distribution.\n", "\n", "# The following is the log of the coefficient used in the definition of the MVN.\n", "\n", "MVN_COEF = NDIM * np.log( 1. / np.sqrt(2.*np.pi) ) + np.log( np.sqrt(np.linalg.det(INVCOV)) )\n", "\n", "# the logarithm of objective function: log(MVN)\n", "\n", "def getLogFunc(point):\n", " '''\n", " Return the logarithm of the MVN PDF.\n", " '''\n", " normedPoint = MEAN - point\n", " return MVN_COEF - 0.5 * ( np.dot(normedPoint,np.matmul(INVCOV,normedPoint)) )\n", "\n", "import paramonte as pm\n", "\n", "pmpd = pm.ParaDRAM() # define a ParaMonte sampler instance\n", "\n", "pmpd.mpiEnabled = True # This is essential as it enables the invocation of the MPI-parallelized ParaDRAM routines.\n", "\n", "pmpd.spec.overwriteRequested = True # overwrite existing output files if needed\n", "pmpd.spec.randomSeed = 3751 # initialize the random seed to generate reproducible results.\n", "pmpd.spec.outputFileName = \"./out/mvn_parallel_singleChain\"\n", "pmpd.spec.progressReportPeriod = 20000\n", "pmpd.spec.chainSize = 30000 # the default 100,000 unique points is too large for this simple example.\n", "\n", "# call the ParaDRAM sampler\n", "\n", "pmpd.runSampler ( ndim = 4\n", " , getLogFunc = getLogFunc\n", " )\n", "\"\"\"\n", " file.write(contents)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is the saved [output MPI-parallelized Python script](https://github.com/cdslaborg/paramontex/blob/main/Python/Jupyter/sampling_multivariate_normal_distribution_via_paradram_parallel/sampling_multivariate_normal_distribution_via_paradram_parallel_singleChain.py).\n", "> **Note** the only difference in the above parallel script with the serial version, which is the extra Python statement `pmpd.mpiEnabled = True`. This flag tells the ParaDRAM sampler initiate the simulation in parallel and silence all output messages that would otherwise be printed by all processes. ." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**IMPORTANT**: At this point, we have assumed that you already have an MPI runtime library installed on your system. We highly recommend the use of the Intel MPI library on your system if it is Windows or Linux, and Open-MPI if it is macOS. You can run `pm.verify()` on your python command line, just as described in the Jupyter notebook for the serial sampling of the MVN distribution, to verify the existence of the MPI library on your system." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will now run this code in parallel on 3 processors. We will invoke the `mpiexe` launcher to run the code in parallel, however, depending on your system, your platform, or the supercomputer on which you are running this code, you may need a different MPI launcher (e.g., `ibrun`, `mpirun`, ...). In the following, we will assume that you will be using the Intel MPI library if your operating system is Windows (as implied by the flag `-localonly`). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we run the MPI-enabled Python script in parallel on three cores, **on the terminal** (not in the Python session). On Linux or macOS, we can try the following command, \n", "```bash \n", "mpiexec -n 3 python main_mpi_singleChain.py\n", "``` \n", "\n", "On windows, if you are using the Intel MPI library, we can try the following, \n", "```batch \n", "mpiexec -localonly -n 3 python main_mpi_singleChain.py\n", "``` \n", "Otherwise, the same syntax and flags as used in the cases of Linux and macOS should work fine. To understand the meaning of the extra `-localonly` flag, see the [ParaMonte library documentation page](https://www.cdslab.org/paramonte/notes/run/python/#running-python-simulations-on-the-command-prompt-on-multiple-processors). \n", "\n", "The following command combines the above two commands in a single line so that it works, whether you are using a Windows machine or Linux/macOS," ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " \n", "************************************************************************************************************************************\n", "************************************************************************************************************************************\n", "**** ****\n", "**** ****\n", "**** ParaMonte ****\n", "**** Plain Powerful Parallel ****\n", "**** Monte Carlo Library ****\n", "**** ****\n", "**** Version 1.4.0 ****\n", "**** ****\n", "**** Build: Thu Oct 29 01:05:57 2020 ****\n", "**** ****\n", "**** Department of Physics ****\n", "**** Computational & Data Science Lab ****\n", "**** Data Science Program, College of Science ****\n", "**** The University of Texas at Arlington ****\n", "**** ****\n", "**** originally developed at ****\n", "**** ****\n", "**** Multiscale Modeling Group ****\n", "**** Center for Computational Oncology (CCO) ****\n", "**** Oden Institute for Computational Engineering and Sciences ****\n", "**** Department of Aerospace Engineering and Engineering Mechanics ****\n", "**** Department of Neurology, Dell-Seton Medical School ****\n", "**** Department of Biomedical Engineering ****\n", "**** The University of Texas at Austin ****\n", "**** ****\n", "**** For questions and further information, please contact: ****\n", "**** ****\n", "**** Amir Shahmoradi ****\n", "**** ****\n", "**** shahmoradi@utexas.edu ****\n", "**** amir.shahmoradi@uta.edu ****\n", "**** ashahmoradi@gmail.com ****\n", "**** ****\n", "**** cdslab.org/pm ****\n", "**** ****\n", "**** https://www.cdslab.org/paramonte/ ****\n", "**** ****\n", "**** ****\n", "************************************************************************************************************************************\n", "************************************************************************************************************************************\n", " \n", " \n", " \n", "************************************************************************************************************************************\n", "**** ****\n", "**** Setting up the ParaDRAM simulation environment ****\n", "**** ****\n", "************************************************************************************************************************************\n", " \n", " \n", " ParaDRAM - NOTE: Variable outputFileName detected among the input variables to ParaDRAM:\n", " ParaDRAM - NOTE: ./out/mvn_parallel_singleChain\n", " ParaDRAM - NOTE: \n", " ParaDRAM - NOTE: Absolute path to the current working directory:\n", " ParaDRAM - NOTE: D:\\Dropbox\\Projects\\20180101_ParaMonte\\paramontex\\Python\\Jupyter\\sampling_multivariate_normal_distribution_via_paradram_parallel\n", " ParaDRAM - NOTE: \n", " ParaDRAM - NOTE: Generating the requested directory for the ParaDRAM output files:\n", " ParaDRAM - NOTE: .\\out\\\n", " ParaDRAM - NOTE: \n", " ParaDRAM - NOTE: ParaDRAM output files will be prefixed with:\n", " ParaDRAM - NOTE: .\\out\\mvn_parallel_singleChain\n", " \n", " \n", " \n", " ParaDRAM - NOTE: Searching for previous runs of ParaDRAM...\n", " \n", " \n", " \n", " ParaDRAM - NOTE: No pre-existing ParaDRAM run detected.\n", " ParaDRAM - NOTE: Starting a fresh ParaDRAM run...\n", " \n", " \n", " \n", " ParaDRAM - NOTE: Generating the output report file:\n", " ParaDRAM - NOTE: .\\out\\mvn_parallel_singleChain_process_1_report.txt\n", " \n", " \n", " \n", " ParaDRAM - NOTE: Running the simulation in parallel on 3 processes.\n", " ParaDRAM - NOTE: Please see the output report and progress files for further realtime simulation details.\n", " \n", " \n", " \n", " Accepted/Total Func. Call Dynamic/Overall Acc. Rate Elapsed/Remained Time [s] \n", " ========================= ========================= ========================= \n", "\n", " 4490 / 20000 0.2223 / 0.2223 0.2480 / 1.4090\n", " 10297 / 40000 0.2901 / 0.2562 0.5330 / 1.0199\n", " 16265 / 60000 0.3000 / 0.2708 0.8100 / 0.6840\n", " 22469 / 80000 0.3083 / 0.2801 1.0840 / 0.3633\n", " 28630 / 100000 0.3101 / 0.2861 1.3600 / 0.0651\n", " 30000 / 104525 0.3081 / 0.2871 1.4200 / 0.0000 \n", " \n", " \n", " \n", " ParaDRAM - NOTE: Computing the statistical properties of the Markov chain...\n", " \n", " \n", " \n", " ParaDRAM - NOTE: Computing the final decorrelated sample size...\n", " \n", " \n", " \n", " ParaDRAM - NOTE: Generating the output sample file:\n", " ParaDRAM - NOTE: .\\out\\mvn_parallel_singleChain_process_1_sample.txt\n", " \n", " \n", " \n", " ParaDRAM - NOTE: Computing the statistical properties of the final refined sample...\n", " \n", " \n", " \n", " \n", " \n", " ParaDRAM - NOTE: Mission Accomplished.\n", " \n", " \n", " \n", " \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "'ls' is not recognized as an internal or external command,\n", "operable program or batch file.\n" ] } ], "source": [ "!ls && \\\n", "mpiexec -n 3 python sampling_multivariate_normal_distribution_via_paradram_parallel_singleChain.py || \\\n", "mpiexec -localonly -n 3 python sampling_multivariate_normal_distribution_via_paradram_parallel_singleChain.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The sampler has now generated 5 output files that are [accessible here](https://github.com/cdslaborg/paramontex/tree/main/Python/Jupyter/sampling_multivariate_normal_distribution_via_paradram_parallel/out), all prefixed with `mvn_parallel_singleChain_*`. In particular, the [simulation report file](https://github.com/cdslaborg/paramontex/tree/main/Python/Jupyter/sampling_multivariate_normal_distribution_via_paradram_parallel/out/mvn_parallel_singleChain_process_1_report.txt) contains a lot of interesting information about the performance of the parallel simulation. We can process these files in the same way we did for [the serial version of sampling the MVN PDF via the ParaDRAM sampler](https://nbviewer.jupyter.org/github/cdslaborg/paramontex/blob/main/Python/Jupyter/sampling_multivariate_normal_distribution_via_paradram/sampling_multivariate_normal_distribution_via_paradram.ipynb). For example, to parse the contents of the report file, we can try, " ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ParaMonte Python Interface Version 2.3.0\n", "ParaMonte Python Kernel Version 1.4.0\n", "\n", "ParaDRAM - NOTE: 1 files detected matching the pattern: \"./out/mvn_parallel_singleChain*_report.txt\"\n", "\n", "\n", "ParaDRAM - NOTE: processing report file: D:\\Dropbox\\Projects\\20180101_ParaMonte\\paramontex\\Python\\Jupyter\\sampling_multivariate_normal_distribution_via_paradram_parallel\\out\\mvn_parallel_singleChain_process_1_report.txt\n", "ParaDRAM - NOTE: reading the file contents... ParaDRAM - NOTE: parsing the report file contents...\n", "done in 0.008975 seconds.\n", "\n", "ParaDRAM - NOTE: The processed report files are now stored in the newly-created\n", "ParaDRAM - NOTE: component `reportList` of the ParaDRAM object as a Python list.\n", "ParaDRAM - NOTE: For example, to access the entire contents of the first (or the only) report file, try:\n", "ParaDRAM - NOTE: \n", "ParaDRAM - NOTE: pmpd.reportList[0].contents.print()\n", "ParaDRAM - NOTE: \n", "ParaDRAM - NOTE: where you will have to replace `pmpd` with your ParaDRAM instance name.\n", "ParaDRAM - NOTE: To access the simulation statistics and information, examine the contents of the\n", "ParaDRAM - NOTE: components of the following structures:\n", "ParaDRAM - NOTE: \n", "ParaDRAM - NOTE: pmpd.reportList[0].contents.print() # to print the contents of the report file.\n", "ParaDRAM - NOTE: pmpd.reportList[0].setup # to get information about the simulation setup.\n", "ParaDRAM - NOTE: pmpd.reportList[0].stats.time # to get the timing information of the simulation.\n", "ParaDRAM - NOTE: pmpd.reportList[0].stats.chain # to get the statistics of the simulation output sample.\n", "ParaDRAM - NOTE: pmpd.reportList[0].stats.numFuncCall # to get information about the number of function calls.\n", "ParaDRAM - NOTE: pmpd.reportList[0].stats.parallelism # to get information about the simulation parallelism.\n", "ParaDRAM - NOTE: pmpd.reportList[0].spec # to get the simulation specification in the report file.\n", "ParaDRAM - NOTE: \n", "ParaDRAM - NOTE: For more information and examples on the usage, visit:\n", "ParaDRAM - NOTE: \n", "ParaDRAM - NOTE: https://www.cdslab.org/paramonte\n", "\n" ] } ], "source": [ "import paramonte as pm\n", "print(pm.version.interface.get())\n", "print(pm.version.kernel.get())\n", "\n", "pmpd = pm.ParaDRAM()\n", "pmpd.readReport(\"./out/mvn_parallel_singleChain\")\n", "report = pmpd.reportList[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are a lot of detailed information about different aspects of the parallel simulation in this file. Here is a glance through some of the information extracted from the file, " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.313000702847873e-05\n", "This is the average pure time cost of each function call, in seconds.\n" ] } ], "source": [ "print(report.stats.time.perFuncCall.value)\n", "print(report.stats.time.perFuncCall.description)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.315229732036249e-06\n", "This is the average time cost of inter-process communications per used (accepted or rejected or delayed-rejection) function call, in seconds.\n" ] } ], "source": [ "print(report.stats.time.perInterProcessCommunication.value)\n", "print(report.stats.time.perInterProcessCommunication.description)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "This is the number of processes (images) used in this simulation.\n" ] } ], "source": [ "print(report.stats.parallelism.current.numProcess.value)\n", "print(report.stats.parallelism.current.numProcess.description)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.573338694004443\n", "This is the estimated maximum speedup gained via singleChain parallelization model compared to serial mode.\n" ] } ], "source": [ "print(report.stats.parallelism.current.speedup.value)\n", "print(report.stats.parallelism.current.speedup.description)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.573338694004443\n", "This is the predicted optimal maximum speedup gained via singleChain parallelization model, given the current MCMC sampling efficiency.\n" ] } ], "source": [ "print(report.stats.parallelism.optimal.current.speedup.value)\n", "print(report.stats.parallelism.optimal.current.speedup.description)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.962074970857193\n", "This is the predicted absolute optimal maximum speedup gained via singleChain parallelization model, under any MCMC sampling efficiency. This simulation will likely NOT benefit from any additional computing processors beyond the predicted absolute optimal number, 3, in the above. This is true for any value of MCMC sampling efficiency. Keep in mind that the predicted absolute optimal number of processors is just an estimate whose accuracy depends on many runtime factors, including the topology of the communication network being used, the number of processors per node, and the number of tasks to each processor or node.\n" ] } ], "source": [ "print(report.stats.parallelism.optimal.absolute.speedup.value)\n", "print(report.stats.parallelism.optimal.absolute.speedup.description)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[13792, 9551, 6657]\n", "These are contributions of individual processes to the construction of the MCMC chain. Essentially, they represent the total number of accepted states by the corresponding processor, starting from the first processor to the last. This information is mostly informative in parallel Fork-Join (singleChain) simulations.\n" ] } ], "source": [ "print(report.stats.parallelism.processContribution.value)\n", "print(report.stats.parallelism.processContribution.description)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The ParaDRAM sampler also automatically computes the strong scaling behavior of the parallel simulation under the current and absolutely optimal simulation conditions. For example, we can plot these scaling results like the following, " ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1.0, 1.4744375, 1.5733387, 1.5095088, 1.394962, 1.2742799]\n", "This is the predicted strong-scaling speedup behavior of the singleChain parallelization model, given the current MCMC sampling efficiency, for increasing numbers of processes, starting from a single process.\n" ] } ], "source": [ "print(report.stats.parallelism.optimal.current.scaling.strong.speedup.value)\n", "print(report.stats.parallelism.optimal.current.scaling.strong.speedup.description)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1.0, 1.7002015, 1.962075, 1.9436468, 1.809423, 1.6461051]\n", "This is the predicted absolute strong-scaling speedup behavior of the singleChain parallelization model, under any MCMC sampling efficiency, for increasing numbers of processes, starting from a single process.\n" ] } ], "source": [ "print(report.stats.parallelism.optimal.absolute.scaling.strong.speedup.value)\n", "print(report.stats.parallelism.optimal.absolute.scaling.strong.speedup.description)" ] }, { "cell_type": "code", "execution_count": 17, "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", " var resizeObserver = new 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", " resizeObserver.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", "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(\"
\");\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.one(\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", "\n", " // Update the output cell to use the data from the current canvas.\n", " fig.push_to_output();\n", " var dataURL = fig.canvas.toDataURL();\n", " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", " // the notebook keyboard shortcuts fail.\n", " IPython.keyboard_manager.enable();\n", " fig.parent_element.innerHTML =\n", " '';\n", " fig.close_ws(fig, msg);\n", "};\n", "\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", " '';\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 = '';\n", " button.addEventListener('click', on_click_closure(method_name));\n", " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", " buttonGroup.appendChild(button);\n", " }\n", "\n", " if (buttonGroup.hasChildNodes()) {\n", " toolbar.appendChild(buttonGroup);\n", " }\n", "\n", " // Add the status bar.\n", " var status_bar = document.createElement('span');\n", " status_bar.classList = 'mpl-message pull-right';\n", " toolbar.appendChild(status_bar);\n", " this.message = status_bar;\n", "\n", " // Add the close button to the window.\n", " var buttongrp = document.createElement('div');\n", " buttongrp.classList = 'btn-group inline pull-right';\n", " button = document.createElement('button');\n", " button.classList = 'btn btn-mini btn-primary';\n", " button.href = '#';\n", " button.title = 'Stop Interaction';\n", " button.innerHTML = '';\n", " button.addEventListener('click', function (_evt) {\n", " fig.handle_close(fig, {});\n", " });\n", " button.addEventListener(\n", " 'mouseover',\n", " on_mouseover_closure('Stop Interaction')\n", " );\n", " buttongrp.appendChild(button);\n", " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", "};\n", "\n", "mpl.figure.prototype._remove_fig_handler = function (event) {\n", " var fig = event.data.fig;\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": [ "