pax_global_header00006660000000000000000000000064141253314460014515gustar00rootroot0000000000000052 comment=527d7b2264c5ecca012c6d248990dc18c1058834 ipyparallel-7.1.0/000077500000000000000000000000001412533144600140405ustar00rootroot00000000000000ipyparallel-7.1.0/.coveragerc000066400000000000000000000003501412533144600161570ustar00rootroot00000000000000[run] parallel = True omit = ipyparallel/tests/* [report] exclude_lines = if self.debug: pragma: no cover raise NotImplementedError if __name__ == .__main__.: ignore_errors = True omit = ipyparallel/tests/* ipyparallel-7.1.0/.eslintignore000066400000000000000000000000531412533144600165410ustar00rootroot00000000000000node_modules dist coverage **/*.d.ts tests ipyparallel-7.1.0/.eslintrc.js000066400000000000000000000036231412533144600163030ustar00rootroot00000000000000module.exports = { env: { browser: true, es6: true, commonjs: true, node: true, }, root: true, extends: [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "prettier/@typescript-eslint", "plugin:react/recommended", ], parser: "@typescript-eslint/parser", parserOptions: { project: "tsconfig.eslint.json", }, plugins: ["@typescript-eslint"], rules: { "@typescript-eslint/no-floating-promises": ["error", { ignoreVoid: true }], "@typescript-eslint/naming-convention": [ "error", { selector: "interface", format: ["PascalCase"], custom: { regex: "^I[A-Z]", match: true, }, }, ], "@typescript-eslint/no-unused-vars": ["warn", { args: "none" }], "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/camelcase": "off", "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/no-namespace": "off", "@typescript-eslint/interface-name-prefix": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/ban-ts-comment": ["warn", { "ts-ignore": true }], "@typescript-eslint/ban-types": "warn", "@typescript-eslint/no-non-null-asserted-optional-chain": "warn", "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/triple-slash-reference": "warn", "@typescript-eslint/no-inferrable-types": "off", "no-inner-declarations": "off", "no-prototype-builtins": "off", "no-control-regex": "warn", "no-undef": "warn", "no-case-declarations": "warn", "no-useless-escape": "off", "prefer-const": "off", "react/prop-types": "warn", }, settings: { react: { version: "detect", }, }, }; ipyparallel-7.1.0/.flake8000066400000000000000000000010521412533144600152110ustar00rootroot00000000000000[flake8] # Ignore style and complexity # E: style errors # W: style warnings # C: complexity # F401: module imported but unused # F403: import * # F841: local variable assigned but never used # E402: module level import not at top of file # I100: Import statements are in the wrong order # I101: Imported names are in the wrong order. Should be ignore = E, C, W, F403, F841, E402, I100, I101, D400 builtins = c, get_config exclude = .cache, .github, docs, jupyterhub/alembic*, onbuild, scripts, share, tools, setup.py ipyparallel-7.1.0/.github/000077500000000000000000000000001412533144600154005ustar00rootroot00000000000000ipyparallel-7.1.0/.github/workflows/000077500000000000000000000000001412533144600174355ustar00rootroot00000000000000ipyparallel-7.1.0/.github/workflows/release.yml000066400000000000000000000022671412533144600216070ustar00rootroot00000000000000# Build releases and (on tags) publish to PyPI name: Release # always build releases (to make sure wheel-building works) # but only publish to PyPI on tags on: push: pull_request: jobs: build-release: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: python-version: 3.8 - name: install build package run: | pip install --upgrade pip pip install build pip freeze - name: build release run: | python -m build --sdist --wheel . ls -l dist - name: verify wheel run: | cd dist pip install ./*.whl jupyterlab==3.* ipcluster --help-all ipcontroller --help-all ipengine --help-all jupyter labextension list 2>&1 | grep ipyparallel jupyter server extension list 2>&1 | grep ipyparallel - name: Publish to PyPI if: startsWith(github.ref, 'refs/tags/') env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | pip install twine twine upload --skip-existing dist/* ipyparallel-7.1.0/.github/workflows/test.yml000066400000000000000000000107111412533144600211370ustar00rootroot00000000000000name: Test on: pull_request: push: env: # UTF-8 content may be interpreted as ascii and causes errors without this. LANG: C.UTF-8 IPP_DISABLE_JS: "1" jobs: test: runs-on: ${{ matrix.runs_on || 'ubuntu-20.04' }} timeout-minutes: 20 strategy: # Keep running even if one variation of the job fail fail-fast: false matrix: include: - python: "3.9" cluster_type: ssh - python: "3.8" cluster_type: mpi - python: "3.7" cluster_type: slurm - python: "3.6" tornado: "5.1.1" - python: "3.7" env: IPP_CONTROLLER_IP: "*" - python: "3.9" env: IPP_ENABLE_CURVE: "1" - python: "3.8" runs_on: windows-2019 - python: "3.9" runs_on: macos-10.15 steps: - uses: actions/checkout@v2 - name: Cache conda environment uses: actions/cache@v2 with: path: | ~/conda key: conda - name: Cache node_modules uses: actions/cache@v2 with: path: | node_modules key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - name: Set environment variables if: ${{ matrix.env }} env: MATRIX_ENV: ${{ toJSON(matrix.env) }} run: | python3 <> $GITHUB_ENV - name: Install Python ${{ matrix.python }} if: ${{ matrix.cluster_type != 'mpi' }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - name: Install Python dependencies run: | pip install --upgrade pip pip install --pre --upgrade .[test] distributed joblib codecov pip install --only-binary :all: matplotlib || echo "no matplotlib" - name: Install pinned tornado if: matrix.tornado run: | pip install tornado==${{ matrix.tornado }} - name: Show environment run: pip freeze - name: Run ${{ matrix.cluster_type }} tests if: ${{ matrix.cluster_type }} run: | pytest -ra -v --maxfail=2 --color=yes --cov=ipyparallel ipyparallel/tests/test_${{ matrix.cluster_type }}.py - name: Run tests if: ${{ ! matrix.cluster_type }} # FIXME: --color=yes explicitly set because: # https://github.com/actions/runner/issues/241 run: | pytest -ra -v --maxfail=3 --color=yes --cov=ipyparallel ipyparallel/tests - name: Submit codecov report run: | codecov - name: Report on slurm if: ${{ matrix.cluster_type == 'slurm' && failure() }} run: | set -x slurmd -C ls -l squeue sinfo scontrol show node=localhost sudo cat /var/log/slurm-llnl/* ipyparallel-7.1.0/.gitignore000066400000000000000000000006521412533144600160330ustar00rootroot00000000000000MANIFEST build dist _build docs/man/*.gz docs/source/api/generated docs/source/config/options docs/source/interactive/magics-generated.txt examples/daVinci Word Count/*.txt examples/testdill.py *.py[co] __pycache__ *.egg-info *~ *.bak .ipynb_checkpoints .tox .DS_Store \#*# .#* .coverage *coverage.xml .coverage.* .idea htmlcov id_*sa .vscode node_modules lib ipyparallel/labextension tsconfig.tsbuildinfo dask-worker-space ipyparallel-7.1.0/.mailmap000066400000000000000000000250671412533144600154730ustar00rootroot00000000000000A. J. Holyoake ajholyoake Aaron Culich Aaron Culich Aron Ahmadia ahmadia Benjamin Ragan-Kelley Benjamin Ragan-Kelley Min RK Benjamin Ragan-Kelley MinRK Barry Wark Barry Wark Ben Edwards Ben Edwards Bradley M. Froehle Bradley M. Froehle Bradley M. Froehle Bradley Froehle Brandon Parsons Brandon Parsons Brian E. Granger Brian Granger Brian E. Granger Brian Granger <> Brian E. Granger bgranger <> Brian E. Granger bgranger Christoph Gohlke cgohlke Cyrille Rossant rossant Damián Avila damianavila Damián Avila damianavila Damon Allen damontallen Darren Dale darren.dale <> Darren Dale Darren Dale <> Dav Clark Dav Clark <> Dav Clark Dav Clark David Hirschfeld dhirschfeld David P. Sanders David P. Sanders David Warde-Farley David Warde-Farley <> Doug Blank Doug Blank Eugene Van den Bulke Eugene Van den Bulke Evan Patterson Evan Patterson Evan Patterson Evan Patterson Evan Patterson epatters Evan Patterson epatters Ernie French Ernie French Ernie French ernie french Ernie French ernop Fernando Perez Fernando Perez Fernando Perez Fernando Perez fperez <> Fernando Perez fptest <> Fernando Perez fptest1 <> Fernando Perez Fernando Perez Fernando Perez Fernando Perez <> Fernando Perez Fernando Perez Frank Murphy Frank Murphy Gabriel Becker gmbecker Gael Varoquaux gael.varoquaux <> Gael Varoquaux gvaroquaux Gael Varoquaux Gael Varoquaux <> Ingolf Becker watercrossing Jake Vanderplas Jake Vanderplas Jakob Gager jakobgager Jakob Gager jakobgager Jakob Gager jakobgager Jason Grout Jason Grout Jason Gors jason gors Jason Gors jgors Jens Hedegaard Nielsen Jens Hedegaard Nielsen Jens Hedegaard Nielsen Jens H Nielsen Jens Hedegaard Nielsen Jens H. Nielsen Jez Ng Jez Ng Jonathan Frederic Jonathan Frederic Jonathan Frederic Jonathan Frederic Jonathan Frederic Jonathan Frederic Jonathan Frederic jon Jonathan Frederic U-Jon-PC\Jon Jonathan March Jonathan March Jonathan March jdmarch Jörgen Stenarson Jörgen Stenarson Jörgen Stenarson Jorgen Stenarson Jörgen Stenarson Jorgen Stenarson <> Jörgen Stenarson jstenar Jörgen Stenarson jstenar <> Jörgen Stenarson Jörgen Stenarson Juergen Hasch juhasch Juergen Hasch juhasch Julia Evans Julia Evans Kester Tong KesterTong Kyle Kelley Kyle Kelley Kyle Kelley rgbkrk Laurent Dufréchou Laurent Dufréchou Laurent Dufréchou laurent dufrechou <> Laurent Dufréchou laurent.dufrechou <> Laurent Dufréchou Laurent Dufrechou <> Laurent Dufréchou laurent.dufrechou@gmail.com <> Laurent Dufréchou ldufrechou Lorena Pantano Lorena Luis Pedro Coelho Luis Pedro Coelho Marc Molla marcmolla Martín Gaitán Martín Gaitán Matthias Bussonnier Matthias BUSSONNIER Matthias Bussonnier Bussonnier Matthias Matthias Bussonnier Matthias BUSSONNIER Matthias Bussonnier Matthias Bussonnier Michael Droettboom Michael Droettboom Nicholas Bollweg Nicholas Bollweg (Nick) Nicolas Rougier Nikolay Koldunov Nikolay Koldunov Omar Andrés Zapata Mesa Omar Andres Zapata Mesa Omar Andrés Zapata Mesa Omar Andres Zapata Mesa Pankaj Pandey Pankaj Pandey Pascal Schetelat pascal-schetelat Paul Ivanov Paul Ivanov Pauli Virtanen Pauli Virtanen <> Pauli Virtanen Pauli Virtanen Pierre Gerold Pierre Gerold Pietro Berkes Pietro Berkes Piti Ongmongkolkul piti118 Prabhu Ramachandran Prabhu Ramachandran <> Puneeth Chaganti Puneeth Chaganti Robert Kern rkern <> Robert Kern Robert Kern Robert Kern Robert Kern Robert Kern Robert Kern <> Robert Marchman Robert Marchman Satrajit Ghosh Satrajit Ghosh Satrajit Ghosh Satrajit Ghosh Scott Sanderson Scott Sanderson smithj1 smithj1 smithj1 smithj1 Steven Johnson stevenJohnson Steven Silvester blink1073 S. Weber s8weber Stefan van der Walt Stefan van der Walt Silvia Vinyes Silvia Silvia Vinyes silviav12 Sylvain Corlay Sylvain Corlay sylvain.corlay Ted Drain TD22057 Théophile Studer Théophile Studer Thomas Kluyver Thomas Thomas Spura Thomas Spura Timo Paulssen timo vds vds2212 vds vds Ville M. Vainio Ville M. Vainio ville Ville M. Vainio ville Ville M. Vainio vivainio <> Ville M. Vainio Ville M. Vainio Ville M. Vainio Ville M. Vainio Walter Doerwald walter.doerwald <> Walter Doerwald Walter Doerwald <> W. Trevor King W. Trevor King Yoval P. y-p ipyparallel-7.1.0/.pre-commit-config.yaml000066400000000000000000000023201412533144600203160ustar00rootroot00000000000000repos: - repo: https://github.com/asottile/reorder_python_imports rev: v2.6.0 hooks: - id: reorder-python-imports exclude: setupbase.py - repo: https://github.com/psf/black rev: 21.9b0 hooks: - id: black exclude: setupbase.py - repo: https://github.com/pre-commit/mirrors-prettier rev: v2.4.1 hooks: - id: prettier - repo: https://github.com/PyCQA/flake8 rev: "3.9.2" hooks: - id: flake8 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 hooks: - id: end-of-file-fixer - id: check-case-conflict - id: check-executables-have-shebangs - id: requirements-txt-fixer - repo: https://github.com/pre-commit/mirrors-eslint rev: v8.0.0-rc.0 hooks: - id: eslint files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx exclude: ipyparallel/nbextension/.* types: [file] additional_dependencies: - "@typescript-eslint/eslint-plugin@2.27.0" - "@typescript-eslint/parser@2.27.0" - eslint@^6.0.0 - eslint-config-prettier@6.10.1 - eslint-plugin-prettier@3.1.4 - eslint-plugin-react@7.21.5 - typescript@4.1.3 ipyparallel-7.1.0/.prettierignore000066400000000000000000000001401412533144600170760ustar00rootroot00000000000000node_modules docs/build htmlcov ipyparallel/labextension **/node_modules **/lib **/package.json ipyparallel-7.1.0/CONTRIBUTING.md000066400000000000000000000025671412533144600163030ustar00rootroot00000000000000# Contributing We follow the [Jupyter Contributing Guide](https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html). Make sure to follow the [Jupyter Code of Conduct](https://jupyter.org/conduct/). ## Development install A basic development install of IPython parallel is the same as almost all Python packages: ```bash pip install -e . ``` To enable the server extension from a development install: ```bash # for jupyterlab jupyter server extension enable --sys-prefix ipyparallel # for classic notebook jupyter serverextension enable --sys-prefix ipyparallel ``` As described in the [JupyterLab documentation](https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html#developing-a-prebuilt-extension) for a development install of the jupyterlab extension you can run the following in this directory: ```bash jlpm # Install npm package dependencies jlpm build # Compile the TypeScript sources to Javascript jupyter labextension develop . --overwrite # Install the current directory as an extension ``` If you are working on the lab extension, you can run jupyterlab in dev mode to always rebuild and reload your extensions. In two terminals, run: ```bash [term 1] $ jlpm watch [term 2] $ jupyter lab --extensions-in-dev-mode ``` You should then be able to refresh the JupyterLab page and it will pick up the changes to the extension as you work. ipyparallel-7.1.0/COPYING.md000066400000000000000000000056121412533144600154760ustar00rootroot00000000000000# Licensing terms Traitlets is adapted from enthought.traits, Copyright (c) Enthought, Inc., under the terms of the Modified BSD License. This project is licensed under the terms of the Modified BSD License (also known as New or Revised or 3-Clause BSD), as follows: - Copyright (c) 2001-, IPython Development Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the IPython Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## About the IPython Development Team The IPython Development Team is the set of all contributors to the IPython project. This includes all of the IPython subprojects. The core team that coordinates development on GitHub can be found here: https://github.com/jupyter/. ## Our Copyright Policy IPython uses a shared copyright model. Each contributor maintains copyright over their contributions to IPython. But, it is important to note that these contributions are typically only changes to the repositories. Thus, the IPython source code, in its entirety is not the copyright of any single person or institution. Instead, it is the collective copyright of the entire IPython Development Team. If individual contributors want to maintain a record of what changes/contributions they have specific copyright on, they should indicate their copyright in the commit message of the change, when they commit the change to one of the IPython repositories. With this in mind, the following banner should be used in any source code file to indicate the copyright and license terms: # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. ipyparallel-7.1.0/MANIFEST.in000066400000000000000000000010421412533144600155730ustar00rootroot00000000000000include COPYING.md include CONTRIBUTING.md include README.md include setupbase.py # enable-at-install config graft etc # lab extension graft lab prune lab/lib include install.json include package*.json include tsconfig*.json graft ipyparallel/labextension # Documentation graft docs # Examples graft examples # docs subdirs we want to skip prune docs/_build # Patterns to exclude from any directory global-exclude *~ global-exclude *.pyc global-exclude *.pyo global-exclude .git global-exclude .ipynb_checkpoints global-exclude .DS_Store ipyparallel-7.1.0/README.md000066400000000000000000000015201412533144600153150ustar00rootroot00000000000000# Interactive Parallel Computing with IPython IPython Parallel (`ipyparallel`) is a Python package and collection of CLI scripts for controlling clusters of IPython processes, built on the Jupyter protocol. IPython Parallel provides the following commands: - ipcluster - start/stop/list clusters - ipcontroller - start a controller - ipengine - start an engine ## Install Install IPython Parallel: pip install ipyparallel This will install and enable the IPython Parallel extensions for Jupyter Notebook and (as of 7.0) Jupyter Lab 3.0. ## Run Start a cluster: ipcluster start Use it from Python: ```python import os import ipyparallel as ipp cluster = ipp.Cluster(n=4) with cluster as rc: ar = rc[:].apply_async(os.getpid) pid_map = ar.get_dict() ``` See [the docs](https://ipyparallel.readthedocs.io) for more info. ipyparallel-7.1.0/ci/000077500000000000000000000000001412533144600144335ustar00rootroot00000000000000ipyparallel-7.1.0/ci/slurm/000077500000000000000000000000001412533144600155755ustar00rootroot00000000000000ipyparallel-7.1.0/ci/slurm/slurm.conf000066400000000000000000000025231412533144600176100ustar00rootroot00000000000000# slurm.conf file generated by configurator easy.html. # Put this file on all nodes of your cluster. # See the slurm.conf man page for more information. # SlurmctldHost=localhost # #MailProg=/bin/mail MpiDefault=none #MpiParams=ports=#-# ProctrackType=proctrack/linuxproc ReturnToService=1 SlurmctldPidFile=/var/run/slurmctld.pid #SlurmctldPort=6817 SlurmdPidFile=/var/run/slurmd.pid #SlurmdPort=6818 SlurmdSpoolDir=/var/spool/slurmd SlurmUser=root #SlurmdUser=root StateSaveLocation=/var/spool/slurmctl SwitchType=switch/none #TaskPlugin=task/affinity #CoreSpecPlugin=core_spec/none # # TIMERS #KillWait=30 #MinJobAge=300 #SlurmctldTimeout=120 #SlurmdTimeout=300 # # # SCHEDULING SchedulerType=sched/backfill SelectType=select/cons_tres SelectTypeParameters=CR_Core # # # LOGGING AND ACCOUNTING AccountingStorageType=accounting_storage/none ClusterName=cluster #JobAcctGatherFrequency=30 JobAcctGatherType=jobacct_gather/none SlurmctldDebug=debug5 SlurmctldLogFile=/var/log/slurm-llnl/slurmctld.log SlurmdDebug=debug5 SlurmdLogFile=/var/log/slurm-llnl/slurmd.log # # # COMPUTE NODES # Note: CPUs apparently cannot be oversubscribed # this can only run where at least 2 CPUs are available NodeName=localhost NodeHostName=localhost NodeAddr=127.0.0.1 CPUs=2 State=UNKNOWN PartitionName=part Nodes=localhost Default=YES MaxTime=INFINITE State=UP OverSubscribe=YES ipyparallel-7.1.0/ci/ssh/000077500000000000000000000000001412533144600152305ustar00rootroot00000000000000ipyparallel-7.1.0/ci/ssh/Dockerfile000066400000000000000000000022461412533144600172260ustar00rootroot00000000000000# syntax = docker/dockerfile:1.2.1 FROM ubuntu:20.04 RUN --mount=type=cache,target=/var/cache/apt \ rm -f /etc/apt/apt.conf.d/docker-clean \ && apt-get update \ && apt-get -y install wget openssh-server ENV MAMBA_ROOT_PREFIX=/opt/conda ENV PATH=$MAMBA_ROOT_PREFIX/bin:$PATH RUN wget -qO- https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba \ && mv bin/micromamba /usr/local/bin/micromamba RUN --mount=type=cache,target=${MAMBA_ROOT_PREFIX}/pkgs \ micromamba install -y -p $MAMBA_ROOT_PREFIX -c conda-forge \ python=3.8 \ pip \ ipyparallel # generate a user with home directory and trusted ssh keypair RUN useradd -m -s /bin/bash -N ciuser USER ciuser RUN mkdir ~/.ssh \ && chmod 0700 ~/.ssh \ && ssh-keygen -q -t rsa -N '' -f /home/ciuser/.ssh/id_rsa \ && cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys \ && chmod 0600 ~/.ssh/* USER root ENV PIP_CACHE_DIR=/tmp/pip-cache COPY . /src/ipyparallel RUN --mount=type=cache,target=${PIP_CACHE_DIR} python3 -m pip install -e 'file:///src/ipyparallel#egg=ipyparallel[test]' # needed for sshd to start RUN mkdir /run/sshd # run sshd in the foreground CMD /usr/sbin/sshd -D -e ipyparallel-7.1.0/ci/ssh/docker-compose.yaml000066400000000000000000000002171412533144600210260ustar00rootroot00000000000000services: sshd: image: ipyparallel-sshd build: context: ../.. dockerfile: ci/ssh/Dockerfile ports: - "2222:22" ipyparallel-7.1.0/ci/ssh/ipcluster_config.py000066400000000000000000000012211412533144600211350ustar00rootroot00000000000000import os c.Cluster.controller_ip = '0.0.0.0' c.Cluster.engine_launcher_class = 'SSH' ssh_key = os.path.join(os.path.dirname(__file__), "id_rsa") c.Cluster.controller_ip = '0.0.0.0' c.Cluster.engine_launcher_class = 'SSH' c.SSHEngineSetLauncher.scp_args = c.SSHLauncher.ssh_args = [ "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no", "-i", ssh_key, ] c.SSHEngineSetLauncher.engines = {"ciuser@127.0.0.1:2222": 4} c.SSHEngineSetLauncher.remote_python = "/opt/conda/bin/python3" c.SSHEngineSetLauncher.remote_profile_dir = "/home/ciuser/.ipython/profile_default" c.SSHEngineSetLauncher.engine_args = ['--debug'] ipyparallel-7.1.0/docs/000077500000000000000000000000001412533144600147705ustar00rootroot00000000000000ipyparallel-7.1.0/docs/Makefile000066400000000000000000000164551412533144600164430ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext default: html help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/IPythonparallel.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/IPythonparallel.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/IPythonparallel" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/IPythonparallel" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ipyparallel-7.1.0/docs/environment.yml000066400000000000000000000003631412533144600200610ustar00rootroot00000000000000name: ipyparallel-docs channels: - conda-forge dependencies: - python=3.8 - pip - sphinx>=1.8 - sphinx-copybutton - pydata-sphinx-theme - nbsphinx - myst-parser - matplotlib # nodejs needed for extension build - nodejs ipyparallel-7.1.0/docs/make.bat000066400000000000000000000161471412533144600164060ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 2> nul if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\IPythonparallel.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\IPythonparallel.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end ipyparallel-7.1.0/docs/requirements.txt000066400000000000000000000001231412533144600202500ustar00rootroot00000000000000matplotlib myst-parser nbsphinx numpy pydata-sphinx-theme sphinx sphinx-copybutton ipyparallel-7.1.0/docs/source/000077500000000000000000000000001412533144600162705ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/_static/000077500000000000000000000000001412533144600177165ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/_static/basic.mp4000066400000000000000000002071521412533144600214300ustar00rootroot00000000000000 ftypisomisomiso2avc1mp41free0mdatEH, #x264 - core 163 r3060 5db6aa6 - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=15 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.006"e76;qK1<Gh}![~Y`FyVZ󪋙zsѠ }fV ѥ8{r$t,=iލ8mdžL6B+U'v!U85kC*3{(~b zwĀnrGd/e֙BTf[V* b 9N~SE{_} L||F[咣CҠ(j?P fJ'1#&3FD.,{P\#s>\7j#SJyedKvn2R59SO-{O'ׂ%# ;`@^5 YS\͌'q-3(v3i w$V@"pKY p7"hZpY% iR\86iF|JXăSDb=&3T$iw][|̛h$UEEAb"+/hk$Mi%68_̼MhIzh`}ɡA>ė9-?HC}_vfr5xVaҦ)Cs29iUX{T'ڼCm!63.I @iQv(# (Z6dH7$Ї~<(&[^u2ՠsA9׍v w]>L_IzPuԛ! `P-w^<=|c }u ۘ\5{Su~ Mto>y~DdkͳTBL h0[_mJ6i>dt8yg:s [X=Tea6@%YiĜT n (^z/Sܵ kd3 **P[lwܒ20{N޽yl)9gE{UZ}äs/:S#7z/;f)/.⚲v귵0 T~]ksaF> #XH*?rԏ  4`5u`"'tһ& 7sfeLvDmfb30#y$81rF K+@I2:#뗣yƄ<#{x o>7$)e:qW,q~6A QHYL=Ho̷a"0 q樤>Hn MH;4o}Jb(͠-hXMx5YD.'}lH aQBz"9[a+ȩ { qIIs=v{2e֖5wX$/cLOl {ft ZP>$%nxm`r; s k]+mI-{]V8e*$1{A*ؖB8 pASԣohؒB;C,eQ5-,^HuT" h.?bTe%G”Yi'Q:d8uv_z\&:&1x]Xp,)r c=2csӍ@(qvյs*[L6O./>cI&-k遧+}1p, <3|i>hCDM}\ ( t}rd7=?`kvEj\'ǎ@]]0^SɲeOu I-cf$&IroyhˁٕkF5$$[ʟGWSM$Ce 5Ux p`.w?/o0N#Y/p-:rFڮPIry'OW3٘u+C8!lt,lM6,C#9SK1 @9Z) >L6?( [y1 ܴ_ T$,AlZYsHh gMy`~}f=OhxX?C2DZйCϾ-,|-^T6Yֺ%Ysw9Y)\|xXxe(ƚ[|ti>.GnCO$}ÓA OLwE{U6t1%IYY/Vx߷=ËFr2t@DfܨK4cT lM0Y࿔,*N(ܺ#_9f GloT`[D$%wVsY{uࢁOὂ6U q0 J3շ BQ7ӁEbOU0@P.s?}' 4fɠ/{"0h ~j8Fz-I,"x\E)}ﯯ( _z'4wyj{Rܩaq @7ـuo(*r؉dxI= B"+L}WeQp{2d5o_kl \BiUczTV0-iTla#Ɍ-)}3Mcm% V1!^/^URq M*3 *I6k Ep bYn" /1%, ΒßR]8'9'g |+CbtpZSe\ٛ* mΏs~:JWh,a|>sj,vlrX9WṆһhF|̳b]Ug9wzS8nNs`,@D8;<_͹Ɛ|XC˂=@əW+ܨ9}@Z܃8ć+^Aaݑ+N3 (60S@F73LjJ3d7EjG>m¡a_S(e1t~Q)])jYdՂV7Ӽ:Q&Z$ȃTU˹JqhӔLؿQm^hƎcL^߯j:5# ɑ E=#D},.q7TrW׀5֎+t|l ^bpZ21cP%=G/ȆU{C0؏[DErSԑoyx_&uGI97]1Z~T莌H JSp03zuL3STt!eg G ٜ[=OuqJro|ѧ퍤l^O'RWSdSpȎZ@#Ġht#ľ\k?!ԟR< RGZ`O_>M&5O]_LBrޒt] IS~~KdB[E#yOԯԲ|RYZ]7B1ÅƪX*Jr0ךXD|l|s1"FFʡY^t/riG>C\ו*ۮu݉'kx;hhn~ksvUHKPBA+) lYWk}_&;p c mn* D2Zj%lN#*cM!IJH t@}881J3O fʪoJ/ Gclr&l/5TV3eU̮esNrr׳/GA)~w}WdF| 2-,dx~"stOBۍf'0-uړ9e9eqY ƛ%+==m>QwY"]VFJm*`]e~9,>x~[4rˆ"ݮmވa 3?MT2K{$~ߕ֠mN{DC=*[F.2%!*D8A" !#Ք.wq쥭7ͧFYļ:' y{ N _l,x:#c#DT{b*\=~~DL<‹ˇ$p9K']"흥zE0@wLN 7txZ4#wk?BS,I>c1֍}&Wj*&U~y4UݐҢtΚNDwiYbI-VUWmI"o%5A˗Df.>&+a\~@)>*`5ywpJwV9;Y+OA[7 Ȅ)b >$r`4R u:ȵ(luOzl]Bl\o{OX7Ed"H7*bǚdZ*>{H<= E6vpOuAn/|zj7Vn[Vq T ߵ-KK|,O'+:tF2)4 }/膰osRi1*k*Er.R|pL %!BePߗGϱxžS5%8k)0BX.oxaPah~OpnyG ǭX< BWXf,N6;B8'dȻ.*|K7}]k5KÊ}![Q~KxS_qC&yoPS.F|= *C[yFR ҀVEe凜 ~ esSO?06DfW?ʜ[Figs3a&+ʝf8Ȥ+\ ۟lP&Ba @=,ђ*MB>%닰 9W^tEBh A*P>BSza>,@xS e$ta|^Kb \ԕskQfў-{"?Q/8ȹNYN]׎>0wZBLrM8Q?{:C/ldhɒ5/St>6gUm)`ki70L !5-#Z]T K4g-A$M I|0M4{ƛudd:19bb\vBzTAhNB7px_8O2վFi?YkIq9&H,Onۓ6ɭۤm+SaQFCArđݣGjlHƷnrHHfe/z,P#mKB,Gl-*bn:>y^9임LhoA| /HܟQ.=d|1H9jhm6UrwOv}lb"s1A0_BoPTε_@GKK9 &9of{K!wp㧤.̄~ UXcPX<bX|f'}RզdNz7V.`l GFSdϋPfLLqfܵa,L@pk ks{c>lz.[W%]Fg͐kQ理 Z &H!>JIXh3^)azgx C7 0uJO5Ui_Qi60pPb2%Dzx_tzy#j\)I8ƂaiyZ%觰pXY\h|3FJI2B?&ot@מcIʓ܀^>,&# MFȏ3@,*@Ʌfx j5) .9bg04=-P6Oz0Zuy40exd;J3 \T|<[zrM:)iFzj^W`F<۾N'i(#> +,7Xo(T<49MU9"\ GP3<['`R*HN[(q] l Ej(,`qcEĹAsb8b-JGq?y8e䯞S<%oBz>a+~:#)~]RZޮ.U!bugf$GSZ|KkY&]3Ğ,[fA좜Cؼf)<)krH BZ;N8o=\| ^+z%j,.d(7OOyr0Pɞ6nDQ!oÃ9"R3Id2-V lR%ETKKF B1Z~EIL.V &}Z∹*j>>US9tj?I ЪF!!qlaA7ӟ@7Cg]FT8 &M"Gj"-n )M ȡ$soI{ ;4CD27ghrT=mǟz&@q 6^tB1CE21Q' #'ICz]JچUO?`m #t* R9T؋EﴍYWNm(#M~?kш^~Y%m1^X_o \0#NҰԲ R_ `r6J6/CƍEMnd~jCSs㍑q%k~%!Ǿ. Imm+ z$0%@շ{Wl+O+8R gk(Yx쓁2Vկ0[#&á-_j۬*hi}mӀ&fX;#B%~z+B5^bBG:|:q9$LMں@K,YД/];%@k;T2{sBFPl+P(`JF7A颢6WeޕgCOJ{k44۹N,2:m]Ǻ>UI;M}c&Ԙf维%v <' Uela LE0u1m.`sQK3L2bhդ婴HGx ^ h`"9;>z?W!0+7i޾mIu zpDa^8K#fW]u"80i PCW2ƍyǪ{Y6s;.]Ady>85:%跇8k\N\J"&$I,IY[O8I47h8gx3@d8 ZG,ߕ gzzPv#b{?aRU,.ѳ ~b ^@l6>rC1 $.dDKUɫ-P9'*JYtw/75뢿}Ñܓ3Bjpނ@Pb-kPK9f[)U*jYs0"N"s ԉd7*'2 jyMx{8GyxNC 8!嫨aIx8B:`zY'&(zZ-Dۛ2}&($ߢ; V; .i:ƈȯl5z,CuŠ; gs`^X*dAO7rYSA+3 PZB5zHnK/d/ ~:7߶ @@[GQ<\#%tN񔖟#LF?qS7VjMd[ȉT>B^j@&*w#(2 S'TTc% 5H_<,*hVVp@[T"2t^~reL\:=Z;)zW̞CBVёrak((sǠyZ8iwEVncԹU%?'玦iMhvoΊ.P~#JFM:YP_E$wwdPE0,cƂ* ~ bI_?0dۢcJmqHݶb CBFP||ݰh}&'4 d*-r$C)* @Pۤ_BN/z,&p[%10Z8 lVd{tG$I\@ÂdyeL̒P Cw-_fc$gfRm#ky8 Y= be&x\3odp*MbZdL?ݜIF9Q%;лB[y|#.l,\%],+5?Zmd&ɶb&Kr_ 2<Ƽv pqȺ':7-'7jĄ ^ t9'ˡ5h'JEӲh$9r\~j\RHY~4JACV =},fЀm7yu#銋]Qc J!qdNtPUU7ŀX/fV6EG<v8P*ՎF-;.lX JpNlj_+j%Ou|^-O2U,ʔ٢֗.=iGn$VQgթ|಩~s dcfY,C%Y'o3{r0v=v˝ PES7G%q,R ̶{a"In, {^Hb(l +޷(>+B 5}GxO ́`w)#_yvi(sׁ^@L@0`]XϾ(DIK,zkd{@2VМƐ K0?hQS8 l4)/(`1l Vu3vשΥGӤ6WF# 'Dy Ay=U:0UꋧKH(_\Nkřx2v]c5 ԸA!Wn12`(hv%aD7*1બ ^r0ܫ~FAY0Y;S&[{CbQrq|TvB47B? ,ϫ,jP#fhB`J]oaGl1 seA,O !:O.A. x3nUREsDHhE>A#0RD&CQ>htsނY j3s"CxCVg&{`|F=v!(:H);_(Ff'Bh e[ڞVi[ڛ;$>?DHȅkg9@F2.(A$lCϺ"WSf,HʬC'D+0^h-{kPK])gpٛ_A̎Q^g:/Ni'1z߮ WNCfzR4դFLQ@q$ࡢReҳN7ƿ0r /VY=;hFg d_7,H7Bl 5u&ebhwʖ*6l``%֭V(fluKvXһm7Y|wcx.|/?wQ;/ %7V@|d\y83%˵FŢz;-tu /!0%Ib6dҊMɪKy^K/_~y ]{[jp>wl׵B~9~M.hόQ)F}Zi.ᘭBׯwfal(iVg4|? LE9_{i nW dvGX %mL"AI3)?@؁} Yδu T| {'EAYLHls4oJ+Q,W\hN:z oGp r|Oof|_.HowDS|EWgOF4@%SDYvUoȽy9hMFB+}!m>$H '-o}"M]١,,_M$vi <JK1DuiX#V\R*zbj{^hzO;(ZǏIo7ؕ<{E|@s;Z#I*"*Tb+KD9ߋHBE+~/j:-9Cp?;-=խy?j(Up,GR~~k0X^FW.vn',p ~?W`{رօ#!L\z,2r. ɤF&Ǖm{_s>*hc\԰o 2% 9V3lUPK\e6F{+;hXk_ #/NB2v9'֔-` uTF#M Y25mNčl Z z-¦,C1AlcCÀ2Z:K cZzʡG*0W"i x=$5V辪R/y8ɎksswYFt6pφ["vAD5@#-/U%/Z߇}jl $} 9ߓ&B43i4ބ+#E&; |vdJR:KSRaŨy85(Uug5|Fs2GqO1Vʊ@YERZa(iY\?E1݆uT{a/ Nmߧ =Ihy5|?@Ep?qV.GN*uA!0S5!"Nk^bE*DG37ʖQnzV cWs*τ4I? کa=LrDvc&'r!K m& Mh:oo>kQR+m< ;_O2 1yoԒ?!;GRt91%ƀ38%@27;Y {5DI Lnw% DŽXɡTrnn91!ӻ j ß'O{; =7[j [&os9[Rl#݆vjIu` lg#h KA2+ ÍgAch?[Q'}U5|Q&YujR商l^X \D />iX~0E)0J^MF83v6{BN>ҼeZ)uit̛YO*@o->3j}廀ۅهZU3s8xBX kXT+=?SK5~K7v~C疸l\cs2jU>eOM;a:䳋JLDhiXlSw`D,B+: *AŋpZkFe $pd:J.y9p\}pi-hKC P>`󆸪-*9̡n?"'pš Jݭ"I|唚Ϋ7xzXd넟^R |9e#f@ʦurTNáZ9Up+4?6tW%&yQ]7(wdJwK^B5S %KhA=Xلa&haUi324Vjqথw}jR) 鉛#n :pf^Ea<Vΰ[EðM1!e S;IEU5@ډj8`TOϻ ӌ46i@ĬP;i`f+яv@'wUQb]\Q }T"mKelerz{H ~NZb$ZfFVJ#F"U[&_}V'iKWQo#F :߁:;=-sEŸ57Zi?k-Cpfm$-\} 4ARX<nW8XJ{5H7!'A{dUֆ`hU6acEϫ =S\_@ϻuWCn $f&侊P@U*nWz$P֬Lj Ҫ>k=BCPŹWa.Lq8Gy2yެzNڂNw_z1.ܝS랦?yȵ3It~]Q3,IfjBi.<VLgy Rgf6颏YCޟ'F,FUcY2MEڼS 9:1&{֝1AaMhmk\ kaOC{T)[0 R^4=4Uo-!^=V%:x0{uuJa M+ kB+SJ(*H'9tjYQ_WP!g3DAZ9Z2wtNݹyR|Jk=Ŕ54BggWi;$ܤT=[~TE b;lwyͦ4In};e:S704!IN fhJ)1wq (%'#x g/f >5HE<1DӈY3{c%E-2u\?$r~{!r<=|vвVc2&k$\PDfnَ_wmO7"Y 3)#';e 5+7Ń',\bŲ0h"no4mm\)J &_ :6g2B'oָx:w~lדSГ ͟\]$ݮ0C\:|)]AsZ^e8(Q!Mq(gdŔ{0",Pcbg'%;"W;q-p:I>FŶTV)<'6A| .B;* H/BF]ܛl+ǻQc1vaU*3sQĮcn.I"e?Jb?$nTt=>G (stGՙ.U("ÿbփn'jJ*0 ƥF;%D%Q(^q -$xd 8Elx.8yW lߪGCrRO`&mзAWP_K4G(]F>`3:wO-(ᏚB\M t]Uk,qᬥ(}uAeRNCQpߣ?JJna7` Fg9Cj;~J(A pq/GghW`{ГlN1;yIuR\_uǓF-.89!ܗTyNV(!^8 4OBǍ-ûY8Gu6qھAb V#: ::{"Z[5rKY sB1f=>?펟>|(q`d /9|6bꍣx{CdC]z@eQl>(۽T(7AU;[ȂЭ +' gȥ ̤4n+Kmo8Ef< Eq,O j- u^h)j"8窄Ϙ{T4HD3(K&=a걩^ ,nPBp۫si6YDh]E\$(kZZ9#D/zKTuYk9+ncXΤ+8!rM|\TΏPR{z蠡]9$rb` OTLh \8.pȇk{8Rw;}%l[ikqDg[urnEx%J\f[ޮ*&H>.['<{uܙƩpYtu84x!,2,?f2˟ֲ땁/u- y?$0o,&#hw m18PyHh,olQ~ a- kzE}p .Po|lKٰyptx<ǩna"MbL4:\q?sC|d  "- ic5h̻=#40>; @NoycE;<Aaiw {F{Ͳ).- ,ex-cڋ%bgq4xYזTiq:K .9A=HlI+I(ޏ#)0XftW}0%S]I|<6GvyКcny9(4,^-0S]V ")mF@./dxԵccf ZwjS(i>& ?P:tsHh#I <~w ӵ;3um4?oBjxxvک$l @Gү,ӿ$,Xpa+Њ?Q5jF$9G)"$J=b; tt1&RL? [~DTmopxB-⦝`3ΉBnK%!.{D*?E6|Kkm] e/ÛspykLNN4t1f 1[e<, $E^=.JPpGU*F^p>:Յ` b뭗 sRР:팓ͨ:yKعpAN혠bCp0Dl@<A zi3Ѝ:+< 1Fkw7^w^)\ EQu)dNv6"F%Qredam< 8H_X >+ke,c*-ݠy(cZRM#yq'sCMd.7V]r$bh9Uj^8GV^ն߾ꁥ@^{Z7濚Z YLS꧞osGF9O7HRmON6#ʙ N%K;\o8rSOo%3MΓcbL KPJ O،K^vH0\;rͶRkifJrBwD9/LVf@%g$yXzHxDܥʚlih(b" X܏Sx6`k'Лd* `3f (r'Gbݐ"w;f.̵x`:<_ۘ Yv\a0OQ'Y:|oylh\թh[Z9 OY"Np00/Vj3 {GS*žZ} )?t $iaѲNWN7F;--Ճ|GYOE姱Z$lE& X#Gda,s8bWέUB/94L.O>8M^w A:qO-<pۙxYWm  &}IZ.3)k'XorN){>HAZ!}vug`+{ +Z5h^RnNTje$"_"1:-7-qDΞNሲ#¯2drdF#|[*) d-НLJ /VV<9ʇ[/2Fsc]n+o ;\M6u&Zcȧyl6GJK%B3Pg3MGhIBchQ;Ǔ3 8-azo<'2-?RXg|xpi]3,x#7jݠ([Vc|kҺ&GFFsD LH5ǖģR9LCEogZ(;amH5LaSN KI5I0 w'~tbńq's1/l(RyN216I(6|i.%H:^ av9es?YL:nSw\? *Thl3Eˎ$ܨض~x:C:|F$4{FZ{T;LL {\fvNWbp6{ 93^y)0IToLrUkv[mEU b1Q0k,8蘢͔%>T@F}1j4R ZǍ q@/"uZ.h7æ{?,ʺ'=.ބ(1s>;V_ޏnod=Lc F֚i] &S9oc:*b(trxrF܇":_hƾ3ɺV#}81PɝF$qalSTL9ABxey~WvIo2atBtF`|@95cjB#턷J= CmJ IvYvpف'AfIAhL6޲7,Qvs((x XNK4&]"2wlz? ?7|>tegr_-xVvMtzߖĒ~io+F-KH~#Z8gY_K;=>SrMissK5)eX`qUE@mC0 nǦ\nY67.>_7HjуrhS m'Z'vqvepD5`6_/F&CrЭ8'y֕H0HPzw`=a72h*g[%nD* .=g>JhK6._ c\^ABXk4n2SC:tP:MWYE9E XaN<&:g !5^p7a؜]k=dfcn*)`~dHPP:5FQ8WN AJO;U (YS5S-2OS1:e~-ؼXS=qPRܾ6(A\[#RO$eu'uxDFdZ+骰cQ$%ARװ5Up]aq2<6R+- GaGN6z[LQwINӃR8SKDIucp%o!fYWT*A7Q/;'[BxrkKHݩ}(]gEpm|ҼgZ*ڎtiTEl/ӧgݚU꟝8`oi\-|tU1H@4+4UiNN'SbA(*= ^2 H߆$j.]>b#9#N%|(82+zh 2fNKJs1 ayTo~]XM:Ks =|ūM@ @Ibgpx=(-dȂԥBݣ/"#jXǀ3۞:` .w);maZ6O1: c yM^[LBVIJE?JЭü,C('1lHYqR}2D$vw Cy&"x#3c tܟؠ0/`"rC ]cUA9 ^1x'7ʒtUQ{ym+z%#Tr]"U5K@e2Mk Qe-Rg͟1ūb;I [ r-x~Tu{Hna utJrrS~~ݩ/K|ܣ࿺Xl7aW}%H44 H@yߡjͻ^;I^;`^ݎ8~CA Fu{\X9um*hnVh=s"8ϗ%!1~{ʐ$XS~>v&_^Byr|C!$4m $KLkz (YW,6+_k|]6\e,g]/C󶹮y(=~d[i+ 7d?#SPcFB7mo%`6 JlHXd,s$c z~y{gfݬN$}ЈE'ږ| j90 #f +|}ɍl\͹> M3ǼKWڕS BX, 0&4c\g@fq֒Ks$CDpFXtXq ~,el'0Qb@r B0jĬΦΥ6K*n KCsCnf;G*}UF˗?2/"&t\KUUpלbE-0c ݵO<׽%;x%eu6.yOm_.Y['L+!NtMk /-J2u/$x \"S~K6/S@~({"u&>AU d|G")sHhJz"~ _:'QPv?:e[X;`mu)eC<4chn;{4;d$20zFEoE_p0Է%$[U{l|e~#D,Du e;h=0iWJG,2>?O!DeQxc;p'#WhfABxc+'7(U5s??S,IcP5E\$Ӷ7a.pe9a-6>k_݈L|,Wa~=h! 7jKDn)1zC7\;!vM /,]oU-&'7{Ԉ;I?u#I%H!3v &v?чr@Y*gf^CKG'- A){2#HZ8?oGkiUJjf6aZig`bo>UpY&.3)Ⱦ;չ)4dJrh^*rFVފR S>FbZH9 fi}^5%F0R#M<4hZAC}N줣X ͡KlzFZ14g뒬6ѦoW s%hC*1uKf&2OJ9?>FYcD3E!UPN 1C H[7I$O aqM;oLm9ڂ8U=˻4)S<ϭ z vD,*=WbQ H[i%ܵT&h=2(P˻}̫p>|RIђQDyeKB;ԏ}>6hQ A1dU+6+c.'1wx\hs~9!DE/- lEIbt +a \rNdn^3FR3%֦ZXzl6M#Lӎ]q`+#^ 8nb bVP`2Z: ߸ 8"sXax\CApLKAEU!TgpNTj|jiFp2^-#V[@v|vA[ryf,+z"+FgEuj,[捞~ dod幷,̐ⵓNq| Պj! blZpm;](fE/T)"+d=Mg&ކJ2ۤxpP"e؆„Pd>)!^Ō;A ܍ QѾL~~vT&Or{ dagS][DݩL_~@voBǸNImHc_FPX w1Bv|]-vQnJ߭A%;9D*QoT]RKNNA!@2ڳUBa5k맭ɲe@~LAe~X0'IY=~@#|I{9se;#u_1miy4 q6^UEygȣ;?7*-sx[ A{ ^$S? oSa19F-jļ(y,[iGǵR"/Ǚ%;M+w rٲ5`h\辢C-Hp:WXīIi:P*z+ǡ9 cfLv~0eh4zsb?.#(q$qsw=_B U|zGg8Omg" 6kdɈ{e~ F- kŃx& .J퐽2KRT]_ ?3ɜ a!"wQޏ 'v3OuVu)!KGr@XKr llLE16-&jդնmkqbM\4 ]e*~ /ĻM:Ş:c>ojezq) Fs6/#5=GSW-kr$E:Qv1Ѣt|8p-$i70s*:-C.w '4updHC!D)C[o@Mt޿0Iד8QiWV8(bmG9)eKP<ͫ{w`E_\9gTA>&rGM˜H#&6m٧y {?G%h݂U}> gkk4qBn9>GKSY]3-zO%c,M8YZ0CG$߿P\{EB3TpEjjHXg,7M3|rP;g> <0 9cC>=sgng3:{QV4Sni>2G̿V4ooc{^jh'D9tghd3`R)f;;EZåX Zզ׫Hnd)Ϡ 7Un *UF*{ߑ{42ڸM)!j4@h^՜gi@h$w:ꗽr J dԀqRJ^jFBϯT[у7vVSYJ奒w3ȳ!Wxdxw)wK{z\ ?^͉LK[ dj0T::oY4yD!r}Դ>V,)VvKF,Iw?"5ys[`̦w/ߩ'cd:_1,w`tX@yyg[Vaatbݾr)h :*bl˗*JRmݶo^le TfQh;E>MkEf֭?`WL'p*DR+]fmu5wG"6ړlvpAK[/Tv2U*H~eUV*>沼[2d.dm;7*D{_`k۴铱~#e Xh0BF-}tL4ұ]mA=}{rS*vT^#b8{Ee _>~;=OLӠ욤?[+u;C|iy,WD^iL6s#NuJ#y؉Ks=cÂD7HdibK#r1XF)7ag^rFE3÷eW\<@AcT;SIj3{4?ߟ!JpWRrM&zas 1t2j$DϨJp9,zE>/M%5Y0!C/ 6mm8ˎ^d+ #9=%p :Sr@q o Hn⛣l #Jw9Wh% vӕ ,׼rT9prli}s(*T ̛zQq¿\kՉ}7lA:7?Zg) ~V<l=CeB=%A.(¥d"ܹWIBNXEˏTҍrI&kfS˖oʃܓ2Lȿ컌O~OrܙѭqQ$8ff2%Arv@CLAq&I8}S5 ,O&H̨k6n-.7kElb"ƅ7b*B ~|MH]tVqqw3=VGJeQHȊ5& {Q$MŜ7u?[&La#Iڻ4ަ#Y׏^3cuJ|ީlrȤ-bOJQKd BHy$Y YZU3S8-0Wy==UJϙhfF2~*ZQd0NyGu1 <ݘI atSK/K {>p]'/my#7e+:9l4lkf?RRaTG,%@ i%'EtSDJ=h&Ua~sXX"7`=ɘhp:%b"x>Y r'R0i;?P̟chkY$iGClV96!o /7jG#2CP+oBֵ`kJ P#pmj})6ֵj#xXQ*//z4k&rNyRDk5] E؇dl%֗S'lJ` $(zh#^fU d?u?qӃ?S@%CzTjc4h{ ~X؋pno* HY\"|6}C /(bhSSrFNW2FƘ~VD4MrZXS;b0 ,$l P;nl,NJ`=dcXqIxE:AbzNVULJ=Rђ*N?g]ȓ/);ȥ72ڤٽ?IQBQ9"@.MdAb -LkD/# z l~&$/i~w. p>2QyQb0 nQMacky^I&` Fwkqbޕt)߯LV ?I\v]'f>"'Ku$QgK1Vڄu\\ p)DOxnz[e}&aêZI~ۨ??ӖOIՏby\O͘,Ǽ\(h o \a*J`_.^91墉Co҇q6jB[$5 }{P!ڦX~D@(!AI Re0!@P)y er%UT (',D.7nk@O(MGQ&B 7_"ߠ`0m/|ȝT2MGVw׺#v[%0J$W7NJ nA@@y?MOP˹-Ljr$5](36hVO|_hM&!N"Vt) ѯ'O"Yg4ve& ,5*S6iHٛńT`+,SS}MEӇ[*[0H$®,3>9:Nq`.ډ_(-8hpwOCEP;:tdfSU{$# 3qﮦ QiW,RJO]8ch ԨiH{~h, p)b+S%y~$)V4G\j|łԹ6I^LdUt9vpM](W8;1  OL,X Е1lJ\f+&Wh,8pElvr6"FcYhwȉx(UiĜ͌B"ٜ77eBi($YE ȶD^ EdG`3OL)sP5c^fozLzک"AIU@R ްb A>r[ig{Ԛ]S̽'$)gfZ#ƃ}b_?mIy۝95}Q HDZ6ٚ$ԏFS;c]scllG4Yv$Wq)*#r㤓#co]B(E1y<- e,pp$iGl];ƙFA) (*>QTL'K )p3z]u0tBJY.B`7V莧%y׈F4 +VR5R3_CYj*<]NRL]U#Zpkd0xF7;WGE͡OktG}'Hn尙w>׀x8p|x;uԾ\x@T Ub%!'ͣZai=o{CЩ2`ɭ&anY MIg +[ ]2һr햓MDž4t!9hytU=eC2K/mW=sܻźL ..Y|*$~ke}h>=T~;3Ir5쒸o- J}k/+uBred#6T&A ,P LډenEB<=)?ڎݑt x6h(kAW`wW _K*z(0u?ᖶϮcN)ܓ׮kL{k4(nYte6gqv[Jk^΋~٣m#6$.c,s:D߉1|=19 ӄ7 [ij ܗ`8O묉+<+Z\m_jJGħV<hϗz O-@M/uc W GvHDpkBR+PJiHV%>c^VHf"{H4`ȷ<|v*_6 4fN{=)BnŸ584rNY@]p \V6q2B([_΍5Nr42U^d 6 U _ߋy^?;A/N+s;4dᅡ n_0] 1q/!U/e@իv)-^3qhRGrYoL$sV}AL>#8:u"ݔA8qi5ҁF9v+>y>҄4 s29!9՛ 1dјU4պjjvxw}C*xt1( s>w; KsyCN;D$ JC_&\כEV/7JZGM_,Ԯ֫[l||FUFbx,<pnңM^TחiW 8NcWo [R_7kC]yOZ$9}3U4i6Ze]>Vh]ͫB~2n>Ɉr_zll fJ0 @s̭cj:-+' |Ę#WދBK`GL]jjUb L*yNN:R "OMVUr[^&4rjM G"7\syE;,߷>0f)v\L'H^,zjjIjȲc-Zmמ6ʏigܛ4LҧSV7Tu*)FDyC ~k[3~*~ H#1 .G| ]q~G Ęw.L]cxĊԧ9Ϊ>G[,v@Kd|p^ftxyUlBA(Ahyvɟ7X y6/+S#?QTxp]KVnK7YW9|1e Mt/ d~gz?)fl =~fnC8,{~h̎:(z_-#`$9N6 ieZVJD8N[\LgN7h@'y|&e ]qR4tp#+#*g9|V89>Ã/"Wlj8\0;yzFzg9U DtւPY1`*@u$7D#Gܼ/x@n).T|U,~b8$Q$>J9]o&;$}dA[SKKMi>prcu%݂ UA6)+E`]:]i9ljInT>:ZoX9`ztyl܃.2j{BضĶzy2S}:f &0{=@y:{yx|nd#>-Ρ[!NmR6ԅtKSOM0qO]d_fo[%yK?pt}txƚR %[E;V#5gDy?NO9[_/b&4.N_':NoN ǒ}Z.eh*Mb^Jpܓ@O w?TZ{xzeO1`53vω%TYNJ'5_DJ1srĐPЁ&P EHk. ͉Kh}][d5o.Q"K$'k "CiyypJ԰ήל{\PUXwmV7c. (}ׂ8m7.ڋ` G)|/?J\Q<3 -:9#_<`^Py]\Q)ʗLG.u@JިvfjQ X:vpUC:K.l!=:-ٞn0,:iB?D0c-SzOGDbTI#3oZp(`/['¶^(_GNIHN׈Wʶ4P7/㠍0v!5s0js}-7W gȔdf) <ֲK4a `Q`Xyͱ˗*p:bMDj&,/MBn 9o9Bnȴzc!DYRkD4ϋ ` [0MUY˓eH+MjdYUlvnztXNtÒ%Pj;ek\rGR]tpeK:R`<s|8/|\P< ޼#]}pKNHjSWL٬qV17#{BQJ1/8麎*#m߂_/ÿvm)OL (p ,n{}?g$ϊ7Ը,eF7P?6#NeS"rиR~um:%/WP ߇aIXQ7.@7mY2I$n-Wyc8%TV'Z- y z`5|4EDP?:ЁACSHNP@)_Yp%47OTwIw}զ~;̛6!gB/hO.O{{Yj>$T.*滒M70tI('n{ʄRwy_uQ kzQŠ;D.&u6"rq)ڣć%eSCϔ|A;m,Paөg"\]IȀc CWⳭqjw4v-\U۶Ҳ$>WB&Uس?=G}LBW%Ӱְ- $, gLt)!oX\=!iڋO}Έ]]kR.´l }֧R`p8a Fl1~Kd/{ w ?dm \Wo72S O{_`[ZC*a ]Zfز=V6*,TH05{RR>cąiҖCM]KݐBFjN5Mw.9\%v5Ajh5 mud1I }D L^2+Vj *,i0P2גդtDm$:sxˠ…zԓ>-wj zZ+Mp Kd]O 9:z SS嫼h) .;FfNJN/=N/``L 1b?Z]wNc vIWUB)RsJ*̾*2GoqHK&hhLeuOMz?-kPO^2K3oa 5 %kя#AN:kߡvE`=^hN l@Հˀ`$tY 2ܱ37A$t795x誧oE$E Q(­X4=gNEѧtF[GB.WX`T‚܎lSpu[f> |͉Sa5qX?< ufصG؈&1VME-K""u#B_i7n P#QSqg͝P\;@vO=$X={V=L*x.7 m Bx5idD8]v!-6!Oa)̀%4=O d#I%J˕(tmq{6ѻHf,|zox.G۪ҍxJq l`9]Emw4_%:B'c@ߚQQhޙŔ')n9@|Jp>VHoA!p"q@p~a::'Lm!+%hч$j+z9.R:+S͆z~ Z ` T5<  Rȫ}ڇ3O*&K01$Jp6۵ܛ+dvx E6VrIǩcÁ.vQƎsnBqQ9]Α|yS}) )pÄ'v텔9@D,&Q4 cSGG\`8_Z.}&L-w N7uVc#:po)Ź~8^e:OWeEWlp 'r q’A4&-$`ء,k*TbhR}T5uϕ>Pu'.(֜ވ-;MB9{fS (ȕejK0AIrѶ< #"~# %jA)WzRV}sjy<tQLW?4[˔Rެ=Lzo?9c2bQ _x;O-8]W/|$/yP7E{O2 fgt&>?0 殟C)c3&],u_PUw#zme6V4 f p$]3UhDX MqK8S<9I xqՙr1j鴝VGѓ]r]m W|P㟖@I*ZY=W̪1IfVq~M6/ԥo.Ypty7fs6Vj;2H}»TW_ AAJ8 8ȹ*f"enx3KSq2A/ɟ@{9&oWlx4vYM YPBJ~G"r".G},&Sl[_6{yS`&Cv\Tlz-Ml>Cc0j2XӋG D_z+}?`l/- mwOQd?:3Q(}; / #a<ͲR?kyuz~?\ t~1^t/x9}ic * oj7/,[VĚZ"4id ӿ+wIx_(kp D [6lٳk{/:AE<3vѤ;j/9ɭE!t"j`/)pjB$7pS~`(aAIAhL)G !i|[h{р![`蒈KyZAAE,7an'YV:ItB@&[8jB$7pS~`(a AIAlL!UĚ>^tT?2sM_+#|g#R6 %l< x T`& gp= Ƹ,R&[Lg1MSM>_fK+tB, ԥ>kEߦz<\ЏVD!u=z֌ႃ֫$۸p`o"g9H+6w$[E`pIl=r!hŭ#fOoVҸZ4 @"NM,$>z;A d~vA1#1_O`a^X2k1D5 tm[ֺ %O?,/7jªBS;yy DjCDP)/i6QR_Ѯ[[+װx&${(z60oQEqv9gc(A42R4"gƧ9cm?KvCX?r}w&8EsysQ 8(`T [,bpǶWL5`baP Ȁd$pAp+s;14<Կe(OC=cs3Ilp/cY.]("WBhHLO5P/uc}W'c bg/,]=S<`Zsr;xG YɖKe6I~څ]/?ȾeҮ6_u$ &ufBE4q9~y '1|xگ;badojq'։_MRR P<0~ɗO>;l\KT߽3Bl8I%;pW̚&TSo8yg:@: 1K/WW¿~ CeqG]1 _'{faX80j9nB2pwLjP!u9ȗحh80_%/x=wl.jfmݏuh v8@Ü7gQ 0RIQC%QhiRwYI>LsR餖p'_8z7VHXQ<)?KdF9,˩S@%(| *W8`}v[Dm*s-OXX@Y_"0! X"%&>00 /?w[kZPoa'[R"\ev'eC'Z!zxç L&3l'Gv5]JO7yI fSq#+6Vsuƴ)V ']]>͜l_Z+}@ :>oAoD*h5ߏItBtcQU =L}B7mS,Tq=jB |nHS+A"IAlL!UBAhJEqZz8!A@E,7an'YV:вÁmtB@&[8S+<[@ajB |nHSAfIAlL!UV AE,7an'YV:I+MtB@&[8 0qjB |nHSAIAlL!UW AE,7an'YV:I+MtB@&[8 0pjB |nHSAIAlL!UV A E,7an'YV:I+M+tB@&[8 0q-jB |nHSA2IAlL!UW APE,7an'YV:I+MotB@&[8 0pqjB |nHSAvIAlL!UV AE,7an'YV:I+MtB@&[8 0qjB |nHSAIAlL!UW AE,7an'YV:I+MtB@&[8 0pjB |nHSAIAlL!UV AE,7an'YV:I+M;tB@&[8 0q=jB |nHSA"IAlL!UV A@E,7an'YV:I+MtB@&[8 0pajB |nHSAfIAlL!UV AE,7an'YV:I+MtB@&[8 0qjB |nHSAIAlL!UW AE,7an'YV:I+MtB@&[8 0pjB |nHSGAIAlL!U"Ki o Il0 P<-*#$Ҫ%"A E,7anmud]'}j@+tB@&[8 0q-jB |nHS!A2IAlL&XG"APE,7anF ]_%N otB@&[8 0pqjB |nHS#AvIAlLe/|eـ AE,7an'YV:I+MtB@&[8 0qjB |nHS$AIAlLo]z4&仪bPe}SŖ)z;p\˦ަ|rUxÁ2ϼĖQAcɆXQl~|q0|o."N 1 !E[:h%٦@巆 E,;u}Ol! _^0 .5@*r2HO5P ({dMTa89wy,0m+ܫϒųvoRiH 7$;%Dt'Nj=4eMKFߘL4J'9;:Í*$wVjf6qpݺ3a;=H۷ ;Szї0껶S^x9LMA'q".3*7VҜFip!Dh)r&T3W8~;^-{fAyWY{mg wzlWd{ʑfe]P| \D4C'^(7/QTD8[](ӵuûb2lk,[vemrph0+[_bs},3o!`9!WV=j%lșeȯAn{6~`csJ;D;]5O/UCQ7l +vr=r a>Ռ/ x_QwJ[Cq\>相vp7@K,8oiPV(r |⋰:R}.jp $Ie]ﲗe<Qv2QS^ R7wf| #Ew۵~5xfa`z<[bw>g M˜[hn]& @.8P)M|A#6A%LO0}Bi.NE,/ k@0@&nStDğ`n󭆇@Į>GEی j[5ylf;,S;xpyZY r agYzgep cLeV.T.!Dkbi}M 3xRUʂU(I"򻋩kؐ[-w L 3)xL!!p0:w+0?CԻR@0{_-7;f+e˔))j!#Cblxд46[w!ޮ#-,:_۴MOgF.92\=#Y֍M CV z1o?c#pz'z/Q_rɭ֋;T_6g] Vr4@y]дNߗ;G'&I ]2oWAvFɩrX'/p?);dͩsا)a:E u^P=uZTWqλ@,?;Yv'y_5vѮUG -sz_cRqFZ!TguS/A &zgl,pߧ-,#!eԄ}QZ@q(3ҏ[Db:򉅖p(M=5`g:nŒJ)ti{ 8*"u6XynBTR3D@@@tgv__`<.S ,/c<#׍"ǎL5|- L?ľH6(KZFI UJo,^mN˜g㉃CS_t/*nsQciSʚU> XMѴf#Lԍ3.|$&Bbm|QeT9+C9ƷS}!ȥ)7U*&{G4Q ө7T4o<$|) ;q8@f9EE7ӣ%`-W lPVjRl^_$N]" QG3TzO(IeO5AE,7an'YV: $8% mOC`+tB@&[8 0p0jB |nHT6ߥx JxWAIAlL!UHma@Yq)cZ4[=FoYt_A1;Fn*I?/$B?nkN{uU?KWb}y y<XWuH)V^"+'ph9~Wgw 3C1jsN^*h1JHKTt{w )D (U l*'62Jѐt_BmUn?nu3h PBnPX׫XTG ^YCc ߺ{v|(0+?[9  %@m1e UlحM-ې4^6J> r HSC}TY+˷s8̽o~]ݰAM2H?|< #VtB. &g7=겫pmJ5C݈ODB"sZj8rLecąaY:YGl/fM4۔b3X.eV_(*O#tGs@7JD'1prK|IÊm?}kxI a~w`Z9ŲHm2"rbQ! g*ZYuN;:b=pYSVQ/½Û@̂Yqj%\^Nl<>:^8 9`VV0&` ֚2١8c tJ=@JbsҼT"032SݎL KWZvX"P6Qx~%ʠ¢e|%hi}$k O \>jpn{)WgC ~i\b/K}Ɖphbm E,=ZM0zZntk_b uCTeN uKsO\WۮyH3>l][BQ/,_;QE0JKOJ;A혬1^!ߍB!j_IuuUmaJl 6W.el1;cD"r+H|Zysmn)' L "6Y C=fhɐnMSF[SvC'{Ёw8xV ?l kTH=-ļGH I>kbyE JWJ$n i̘G1k~Ъxt]{ zy3]co׭msQۗq'M(jDžI  %Zsmhyl@N`EedB0mzi0|)MH'窪%b%bnL"WI j&=.3v1s4X.*^Սw6DX~$:t?1?F7>gLm2*j1=Mu -W:6).r :]>Q3 K~ \%}_&qZ@;R}Jh v4$}?%+- 309ΚH!M'Eh/Ҭ<z\8ly~x?,;mAE,7an'YV:SOC =3p{, jƢ*ZEK2ׂŊwcG;tB@&[8q@̙ l aBIoZ2.$[ ᬫ =jB |nHTjH~A"IAlL!U* ? dWr\'">&G-s lG;* /Q'Y2VpqyVLoRzW 4#FK} ژkxnC BE#14Lr0nrFsU-6K,yc##`@)%pCvmZ ̼C%zktYbr C^jh8/=ƶzuFnT9.V{6@xfu|[Hb~x[g yexPjgnfGQҸ!jxMA}Yт~ږz4;8hݾT(Gτ>L.VHN(Cb{2XXP۱{6q*AsqQT>'%r"Ú:A{ $;jwȒ~)rQI脐{g =6`AKiP֋=H!n8=钑C⬑pk m.lZ>Kf MUk(ZȄZq^/NANၟ˜~I//wFMi޻ #KėĶ/lM _!2 =ZQU _4+;'n4Z$v#c l&}-,!kYO#IB|w00F4YS eP^whx`5n^n<ӄP Ɍ_ƾǕ}9l"5oRӏs{ zj><}P>Y!n1éʣp,'-z膡0#E;A/ʉhu| 91Q_ Xv!OF|?ͱh=;|Y D(,[_y1_%Oq;2A@E,7an'YV:S b+n ItB@&[8q?,ajB |nHT *SJn =!+AfIAlL!UVk/kP V$Eɢj.!N&w99cЫLk3'z!63mg7=m`5mX/?,ብX,hʚ6`V+'[zrM!!%X%`S W8站&CmqJo+w;W w0J|_"اl.p "&VDU]-7.33jTP9,nƮ&tb$\SUB0DT_uWaaxyFfM|7Tm':v(+/2 k]8\qYF&C )§ `eu?G~!Zc2m:*U%6`^q5Y0s݌JH̘m1 (*mxOOBBvzMZG!"f>T_ZQI7bL,J{WPPQv;Zw棿/\7/SdB Z`('+yb,0]y2{h(`^ N8\P<b 3$ B?x8իUՀȁ󔳻ZYp1(Q1Mi:v OEG/>I,e =\tc] K#eƕ;:87[:ﲲ^f0H] xiX[": x~" =j+&l+M?aue5h2"3g7MF/ϵf Iɏ6dZ44-CaO􏈸'MeCT H}}S KGkZ(A1CР{ cJh}7oIec /SR_v0:a) X4n=2Z</j,2JOIU^Vn6*K3RPz43AE,7an'YV:SF(4wA1tB@&[8q8hmڇ{RnUjB |nHTj2AIAlL!Um?xh.'7!^+8+I!>_f#lӚl敁۷+{Mkd-#6׏{nzV 2uZt!LE.c3eȩ8Z[Gn޻aHziR<יKF/}%=}Xsp;఻quNS35&:BM7܌  CEɺ guP>gh”TJSDvf 2 gMB;q7l$Ozd}ھ%*W_azKy&ܞ~&DWh%k YBbސmgO(}c3SXEo')j!"e:6>xI<UEMR9x^V]ӵHpI"!5z#<'7dQ`$DQBzXEgE)q_fC5G?g Eū1 R5T7CE, Ob8!s;3NdoݝjEA#r %PL2UwXUrq/ ė t] -y-tP!XFRytʑkG%î}6AE,7an'YV:kG@xu` Bp4VtB@&[8q?0jB |nHTqE^@p#)AIAlL!U!`qͻO8@$A E,7an'YV:Su P+tB@&[8q?-jB |nHTjA2IAlL!UjuYB~Q.%3idm˃~ ݑ?8+%f<<Pno$WCm;8ZA~( 󗾍Zf--hL 3W7GW\ j^TE%1]Ƞʊ>I~Yy-U@Ӟ-?ld2^n ?M ,qFk\b ')זYmDr+Oӣ139CD]t7y,ůR'ay􋷟EK SJxfycfC yb`/%7CXQ s0gVw{g`c..nɼ\%jєj[&Ze{4ɟ{0c_HQ*V^ ($$%dHRXʪ{`Yқ]vh+ՂUDkh6owRf?D.̮TMNJ@lvÏFܒOxdFuE[mnb1 -PUǹ$&~Lܠj(񬺼 7t4}jj)A S .㷃xh5 FҞjTy+T {t ˑ/}\p902FD;fy 8y1a2}^[ t-:X8RNn5T+O@7APE,7an'YV:S dˀZs׾5otB@&[8qvx/qjB |nHTPsK"2l/AvIAlL!UBI Q'3; UH04/ $AE,7an'YV:StVtB@&[8q?jB |nHTjAIAlL!U]PPEf.Ħ Jb U_5iSc5/l}nx?/~V\!Z 03sK̶TT|hўhuY^p"`{<˲/ 1-(F^'+oPvzha%[(k;Afmkqa.M&DnꞑnVpI y> Tޏ ikϥZt#boɘő~Bf-o O`Y+ ² yAzƨ.y"gxX=N^POd-h⁉[l~'{5N XE=+:Yɑ&`8R"M #~ODpw]i?fBIu#Ma ;mouіDGyB18aϽN(˸mYvs} u*꫿̳GE: ,ɲWg(;y&eQ ONY`|pQmC8) 6Qb&禰We-\F!CԭVa(Z2XZà]W󡿇r+zђIvRd3 ׊6 G۫J]F7ɁMD!N_=̓{Ht).=tJ3mn~Ksv8MEĒGWq=[x o^*dycIg_I BZ$:ȠBSqY #XpтWm3b {L>d-(T29,& /_^""P^[yLQN*Bm{ϻy`gSZ6k}b\rELa#!̄ k'& ?~nZw,*%P„7AE,7an'YV:S <EqvMBtB@&[8q?/jB |nHTPEG U:p8(AIAlL!Uf8 ?:p#*$AE,7an'YV:S0p;tB@&[8q?=jB |nHTjA"IAlL]cf#唉|Ę35 ]Eqw:t*BAy_e|pZ#H IAϔٌʚ. ;_]-aMMQbsǰS='4Њ+'辻A:,Waپ#xQV1!ſ(mfE[d4|'~_ӟO(ƚ..Z Aw6i Ae9xqiDwqcuڇD'Bzy%c氄5%iW IH8cN}s]&WτU15SF\뱚\MX @-qn܀T_J?,ϋQOQ_HdVHL':C<̆?.-#c7?άn7K~_ DFO4W8%k9 X~E^YO-3'@|ss t#ׇ<̭xx~n s>^ixW3ϰvDأ=)b³$>G_yéF117tcr+8D/?XԶ :|"(8Nj^v`RYg}O3Xɨ'F J, Wkzקx&C7NBI \]c\qAe*\$w)Ͳ5`?_'Bհk (OڷZ8Eq{$`4(?Y@=]BVslӓߗOT(zYT}Il1$m/EG<6A|;n~K0&Ry\dcO^ :3'M|3q&՞ܤvTdo]< f?W>k:*miԄ?cv-BgJWꗰE/-}v=;jkMO6=$<))4_Cq%Gd6ʬ3zsn1mIf"yVK+d|HM.!< 7A@E,7an'YV:S c#o%~(@ztB@&[8q?/ajB |nHTPs&Y_5a#AfIAlLp+q.$AE,7an'YV:S{vtB@&[8q?jB |nHTjAIAlLw] @зܲkJq0tQQƖ43. 49$z;F:6և2WAjt=+d_̧SYPHHb_*f]"<fTNAGMo9άhbЛ@*&`{G0~r-]thq dpК&4?k/OȂ i\1% 'v瓮HLy>ٕMF}2?M>> h 9ߞ#evO0JZseNk$uͻxpTXn3Bik[FNiHU78GTa2BE %NFl@pH\u]lU@;ee',2p9^VHժD:sTRt ,?1apTL7 TFJ"99ie6^) %@݋S]6>8.~tXꩬ)‚96No78pKV2i&vL,=S kXsE`Ѣqp>5P0t{5TEt7]7:TVl0I^Tح5H@jWL9nxG`- a|fq鳴Bi xмTYFi4AE,7an'YV:SFP@o]  x/tB@&[8q:hlJkf&eLjB |nHTmÛAIAlL_e$A E,7an'YV:St<+tB@&[8qWG9 -jB |nHTmÛdA/IAlL_یJeRJ`Zcj#1#\&@#%n pPSPר-X/󛀘h}O*)00"_`Oæ{Jo b_݇HfeG67_>]*G8#m@rF< )wFP^U9cM. [aބ^fz2>n9.-ҳ )*DBtB;u,$NqifVI11L[}|ӄ& <"| 縰 j4vF>-K.)MݜVB =`(&XO~ˋw=J^I^[4Rt^=͙nnb6[S *UC оn!(& ਱HUXQ; 3.OdkXr'*(l +~Fcm2n9j ԙ],) MFA;6&HLlkâ\P/[|}?#`F BI5oHB~ϕgA0^E36Y~ñ\]L-ϯ,nUlo[r<P;.j'<{8ik~,.)c;}*cK)!x$b\~-YEcAK)dkUnqSlp'C4da>zpiLSݼIGƜr82= ݖǓg`|Xſ@Wfd1V Ih%0s(>>LjO/og1x^驟'jؤvNHΎx|m{Pnٗ(W#ΣapYLڐ"_6gbD/|:VbϘG1lQ"A^_l0a*TMK+M#Ne+?6lN4lJoNxDwvbfeaAAhÔ) ]mU /W T%:EO^ moovlmvhd%@ avcCd!gd@9#jh"colrnclxpaspbtrtsttsstssctts stscTstsz8(=9':">(%%" "6" K; /%" !$ !$ !$ !$ !$ !$ !$ !$ !$ !$ K& %& '$ (9 4[qK$6!0/75!6:!4-(!!;#33(!!;!3,(!!";!3'(!!83# ("#hstco0budtaZmeta!hdlrmdirappl-ilst%toodataLavf58.76.100ipyparallel-7.1.0/docs/source/api/000077500000000000000000000000001412533144600170415ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/api/ipyparallel.rst000066400000000000000000000021161412533144600221110ustar00rootroot00000000000000API Reference ============= .. module:: ipyparallel .. autodata:: version_info The IPython parallel version as a tuple of integers. There will always be 3 integers. Development releases will have 'dev' as a fourth element. Classes ------- .. autoclass:: Cluster :inherited-members: .. autoclass:: Client :inherited-members: .. autoclass:: DirectView :inherited-members: .. autoclass:: LoadBalancedView :inherited-members: .. autoclass:: BroadcastView :inherited-members: .. autoclass:: AsyncResult :inherited-members: .. autoclass:: ViewExecutor :inherited-members: Decorators ---------- IPython parallel provides some decorators to assist in using your functions as tasks. .. autodecorator:: interactive .. autodecorator:: require .. autodecorator:: depend .. autodecorator:: remote .. autodecorator:: parallel Exceptions ---------- .. autoexception:: RemoteError :no-members: .. autoexception:: CompositeError :no-members: .. autoexception:: NoEnginesRegistered .. autoexception:: ImpossibleDependency .. autoexception:: InvalidDependency ipyparallel-7.1.0/docs/source/changelog.md000066400000000000000000000235631412533144600205520ustar00rootroot00000000000000(changelog)= # Changelog Changes in IPython Parallel ## 7.1 New: - New {meth}`.Client.start_and_connect` method for starting a cluster and returning a connected client in one call. - Support [CurveZMQ][] for transport-level encryption and authentication. See [security docs][] for more info. - Define `_max_workers` attribute on {attr}`view.executor` for better consistency with standard library Executors. [security docs]: secure-network [curvezmq]: https://rfc.zeromq.org/spec/26/ Improvements: - {meth}`.Client.wait_for_engines` will raise an informative error if the parent Cluster object notices that its engines have halted while waiting, or any engine unregisters, rather than continuing to wait for engines that will never come - Show progress if `%px` is taking significant time - Improved support for streaming output, e.g. with `%px`, including support for updating output in-place with standard terminal carriage-return progress bars. Fixes: - Fix dropped IOPub messages when using large numbers of engines, causing {meth}`.AsyncResult.wait_for_output` to hang. - Use absolute paths for {attr}`.Cluster.profile_dir`, fixing issues with {meth}`.Cluster.from_file` when run against a profile created with a relative location, e.g. `Cluster(profile_dir="./profile")` - Fix error waiting for connection files when controller is started over ssh. ## 7.0 ### 7.0.1 - Fix missing setupbase.py in tarball ### 7.0.0 Compatibility changes: - **Require Python 3.6** - Fix compatibility issues with ipykernel 6 and jupyter-client 7 - Remove dependency on deprecated ipython-genutils - New dependencies on psutil, entrypoints, tqdm New features: - New {class}`.Cluster` API for managing clusters from Python, including support for signaling and restarting engines. See [docs](../examples/Cluster%20API.ipynb) for more. - New `ipcluster list` and `ipcluster clean` commands derived from the Cluster API. - New {meth}`.Client.send_signal` for sending signals to single engines. - New KernelNanny process for signaling and monitoring engines for improved responsiveness of handing engine crashes. - New prototype {class}`.BroadcastScheduler` with vastly improved scaling in 'do-on-all' operations on large numbers of engines, c/o Tom-Olav Bøyum's [Master's thesis][] at University of Oslo. [Broadcast view documentation][]. - New {meth}`.Client.wait_for_engines` method to wait for engines to be available. - Nicer progress bars for interactive waits, such as {meth}`.AsyncResult.wait_interactive`. - Add {meth}`.AsyncResult.stream_output` context manager for streaming output. Stream output by default in parallel magics. - Launchers registered via entrypoints for better support of third-party Launchers. - New JupyterLab extension (enabled by default) based on dask-labextension for managing clusters. - {meth}`.LoadBalancedView.imap` consumes inputs as-needed, producing a generator of results instead of an AsyncMapResult, allowing for consumption of very large or infinite mapping inputs. [broadcast view documentation]: ../examples/broadcast/Broadcast%20view.ipynb [master's thesis]: https://urn.nb.no/URN:NBN:no-84589 Improvements and other fixes: - Greatly improved performance of heartbeat and registration with large numbers of engines, tested with 5000 engines and default configuration. - Single `IPController.ports` configuration to specify the pool of ports for the controller to use, e.g. `ipcontroller --ports 10101-10120`. - Allow `f` as keyword-argument to `apply`, e.g. `view.apply(myfunc, f=5)`. - joblib backend will start and stop a cluster by default if the default cluster is not running. The repo has been updated to use pre-commit, black, myst, and friends and GitHub Actions for CI, but this should not affect users, only making it a bit nicer for contributors. ## 6.3.0 - **Require Python 3.5** - Fix compatibility with joblib 0.14 - Fix crash recovery test for Python 3.8 - Fix repeated name when cluster-id is set - Fix CSS for notebook extension - Fix KeyError handling heartbeat failures ## 6.2.5 - Fix compatibility with Python 3.8 - Fix compatibility with recent dask ## 6.2.4 - Improve compatibility with ipykernel 5 - Fix `%autopx` with IPython 7 - Fix non-local ip warning when using current hostname ## 6.2.3 - Fix compatibility for execute requests with ipykernel 5 (now require ipykernel >= 4.4) ## 6.2.2 - Fix compatibility with tornado 4, broken in 6.2.0 - Fix encoding of engine and controller logs in `ipcluster --debug` on Python 3 - Fix compatiblity with joblib 0.12 - Include LICENSE file in wheels ## 6.2.1 - Workaround a setuptools issue preventing installation from sdist on Windows ## 6.2.0 - Drop support for Python 3.3. IPython parallel now requires Python 2.7 or >= 3.4. - Further fixes for compatibility with tornado 5 when run with asyncio (Python 3) - Fix for enabling clusters tab via nbextension - Multiple fixes for handling when engines stop unexpectedly - Installing IPython Parallel enables the Clusters tab extension by default, without any additional commands. ## 6.1.1 - Fix regression in 6.1.0 preventing BatchSpawners (PBS, etc.) from launching with ipcluster. ## 6.1.0 Compatibility fixes with related packages: - Fix compatibility with pyzmq 17 and tornado 5. - Fix compatibility with IPython ≥ 6. - Improve compatibility with dask.distributed ≥ 1.18. New features: - Add {attr}`namespace` to BatchSpawners for easier extensibility. - Support serializing partial functions. - Support hostnames for machine location, not just ip addresses. - Add `--location` argument to ipcluster for setting the controller location. It can be a hostname or ip. - Engine rank matches MPI rank if engines are started with `--mpi`. - Avoid duplicate pickling of the same object in maps, etc. Documentation has been improved significantly. ## 6.0.2 Upload fixed sdist for 6.0.1. ## 6.0.1 Small encoding fix for Python 2. ## 6.0 Due to a compatibility change and semver, this is a major release. However, it is not a big release. The main compatibility change is that all timestamps are now timezone-aware UTC timestamps. This means you may see comparison errors if you have code that uses datetime objects without timezone info (so-called naïve datetime objects). Other fixes: - Rename {meth}`Client.become_distributed` to {meth}`Client.become_dask`. {meth}`become_distributed` remains as an alias. - import joblib from a public API instead of a private one when using IPython Parallel as a joblib backend. - Compatibility fix in extensions for security changes in notebook 4.3 ## 5.2 - Fix compatibility with changes in ipykernel 4.3, 4.4 - Improve inspection of `@remote` decorated functions - {meth}`Client.wait` accepts any Future. - Add `--user` flag to {command}`ipcluster nbextension` - Default to one core per worker in {meth}`Client.become_distributed`. Override by specifying `ncores` keyword-argument. - Subprocess logs are no longer sent to files by default in {command}`ipcluster`. ## 5.1 ### dask, joblib IPython Parallel 5.1 adds integration with other parallel computing tools, such as [dask.distributed](https://distributed.readthedocs.io) and [joblib](https://joblib.readthedocs.io). To turn an IPython cluster into a dask.distributed cluster, call {meth}`~.Client.become_distributed`: ``` executor = client.become_distributed(ncores=1) ``` which returns a distributed {class}`Executor` instance. To register IPython Parallel as the backend for joblib: ``` import ipyparallel as ipp ipp.register_joblib_backend() ``` ### nbextensions IPython parallel now supports the notebook-4.2 API for enabling server extensions, to provide the IPython clusters tab: ``` jupyter serverextension enable --py ipyparallel jupyter nbextension install --py ipyparallel jupyter nbextension enable --py ipyparallel ``` though you can still use the more convenient single-call: ``` ipcluster nbextension enable ``` which does all three steps above. ### Slurm support [Slurm](https://hpc.llnl.gov/training/tutorials/livermore-computing-linux-commodity-clusters-overview-part-one) support is added to ipcluster. ### 5.1.0 [5.1.0 on GitHub](https://github.com/ipython/ipyparallel/milestones/5.1) ## 5.0 ### 5.0.1 [5.0.1 on GitHub](https://github.com/ipython/ipyparallel/milestones/5.0.1) - Fix imports in {meth}`use_cloudpickle`, {meth}`use_dill`. - Various typos and documentation updates to catch up with 5.0. ### 5.0.0 [5.0 on GitHub](https://github.com/ipython/ipyparallel/milestones/5.0) The highlight of ipyparallel 5.0 is that the Client has been reorganized a bit to use Futures. AsyncResults are now a Future subclass, so they can be `yield` ed in coroutines, etc. Views have also received an Executor interface. This rewrite better connects results to their handles, so the Client.results cache should no longer grow unbounded. ```{seealso} - The Executor API {class}`ipyparallel.ViewExecutor` - Creating an Executor from a Client: {meth}`ipyparallel.Client.executor` - Each View has an {attr}`executor` attribute ``` Part of the Future refactor is that Client IO is now handled in a background thread, which means that {meth}`Client.spin_thread` is obsolete and deprecated. Other changes: - Add {command}`ipcluster nbextension enable|disable` to toggle the clusters tab in Jupyter notebook Less interesting development changes for users: Some IPython-parallel extensions to the IPython kernel have been moved to the ipyparallel package: - {mod}`ipykernel.datapub` is now {mod}`ipyparallel.datapub` - ipykernel Python serialization is now in {mod}`ipyparallel.serialize` - apply_request message handling is implememented in a Kernel subclass, rather than the base ipykernel Kernel. ## 4.1 [4.1 on GitHub](https://github.com/ipython/ipyparallel/milestones/4.1) - Add {meth}`.Client.wait_interactive` - Improvements for specifying engines with SSH launcher. ## 4.0 [4.0 on GitHub](https://github.com/ipython/ipyparallel/milestones/4.0) First release of `ipyparallel` as a standalone package. ipyparallel-7.1.0/docs/source/conf.py000066400000000000000000000272101412533144600175710ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # ipyparallel documentation build configuration file, created by # sphinx-quickstart on Mon Apr 13 12:53:58 2015. # NOTE: This file has been edited manually from the auto-generated one from # sphinx. Do NOT delete and re-generate. If any changes from sphinx are # needed, generate a scratch one and merge by hand any new fields needed. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # We load the ipython release info into a dict by explicit execution iprelease = {} exec( compile( open('../../ipyparallel/_version.py').read(), '../../ipyparallel/_version.py', 'exec', ), iprelease, ) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '1.3' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.doctest', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.intersphinx', 'sphinx.ext.napoleon', 'myst_parser', 'nbsphinx', 'IPython.sphinxext.ipython_console_highlighting', ] myst_enable_extensions = [ 'colon_fence', 'deflist', ] # Add any paths that contain templates here, relative to this directory. # templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: source_suffix = ['.md', '.rst'] from traitlets.config import LoggingConfigurable # exclude members inherited from HasTraits by default autodoc_default_options = { 'members': None, "exclude-members": ','.join(dir(LoggingConfigurable)), } # Add dev disclaimer. if iprelease['version_info'][-1] == 'dev': rst_prolog = """ .. note:: This documentation is for a development version of IPython. There may be significant differences from the latest stable release. """ # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'ipyparallel' copyright = u'2015, The IPython Development Team' author = u'The IPython Development Team' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '.'.join(map(str, iprelease['version_info'][:2])) # The full version, including alpha/beta/rc tags. release = iprelease['__version__'] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. default_role = 'literal' # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. # html_style = 'default.css' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'pydata_sphinx_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # https://pydata-sphinx-theme.readthedocs.io/en/latest/user_guide/configuring.html html_theme_options = { # "navbar_center": [], # disable navbar-nav because it's way too big right now "icon_links": [ { "name": "GitHub", "url": "https://github.com/ipyparallel", "icon": "fab fa-github-square", }, ], } html_context = { "github_url": "https://github.com/ipython/ipyparallel", } # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'ipythonparalleldoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ( master_doc, 'ipythonparallel.tex', u'IPython Parallel Documentation', u'The IPython Development Team', 'manual', ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'ipythonparallel', u'IPython Parallel Documentation', [author], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( master_doc, 'ipythonparallel', u'IPython Parallel Documentation', author, 'ipythonparallel', 'One line description of project.', 'Miscellaneous', ), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'python': ('https://docs.python.org/3/', None), 'ipython': ('https://ipython.readthedocs.io/en/stable/', None), 'pymongo': ('https://pymongo.readthedocs.io/en/stable/', None), 'distributed': ('https://distributed.readthedocs.io/en/stable/', None), 'jupyterclient': ('https://jupyter-client.readthedocs.io/en/stable/', None), } # nbsphinx config # https://nbsphinx.readthedocs.io/en/latest/usage.html nbsphinx_execute = 'auto' ipyparallel-7.1.0/docs/source/examples/000077500000000000000000000000001412533144600201065ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/examples/Cluster API.ipynb000066400000000000000000000635201412533144600231720ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "id": "80a38ff6-dfe6-4c7a-a378-d16a4fbb0786", "metadata": {}, "source": [ "# Cluster API\n", "\n", "IPython Parallel 7 adds a `Cluster` API for starting/stopping clusters.\n", "\n", "This is the new implementation of `ipcluster`,\n", "which can be more easily re-used in Python programs.\n", "The `ipcluster` script is\n", "\n", "Controllers and Engines are started with \"Launchers\",\n", "which are objects representing a running process.\n", "\n", "Each **Cluster** has:\n", "\n", "- a **cluster id**\n", "- a **profile directory**\n", "- one **controller**\n", "- zero or more **engine sets**\n", " - each of which has one or more **engines**\n", " \n", "The combination of `profile_dir` and `cluster_id` uniquely identifies a cluster.\n", "You can have many clusters in one profile, but each must have a distinct cluster id.\n", "\n", "To create a cluster, instantiate a Cluster object:" ] }, { "cell_type": "code", "execution_count": 5, "id": "ef0dff26-f6d2-4d79-901b-049debe0c0d1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import ipyparallel as ipp\n", "\n", "cluster = ipp.Cluster()\n", "cluster" ] }, { "cell_type": "markdown", "id": "db2688db-d6e6-4bb2-8fec-cb38fe4d8c59", "metadata": {}, "source": [ "To start the cluster:" ] }, { "cell_type": "code", "execution_count": 2, "id": "23e385fc-6bc2-44ad-b544-0f39182c1a76", "metadata": {}, "outputs": [ { "data": { "text/plain": [ ")>" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "await cluster.start_controller()\n", "cluster" ] }, { "cell_type": "code", "execution_count": 3, "id": "8f48261b-6787-424f-a9e7-b24fb04d076d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting 4 engines with \n" ] }, { "data": { "text/plain": [ ", engine_sets=['1623757384-b3pm'])>" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "engine_set_id = await cluster.start_engines(n=4)\n", "cluster" ] }, { "cell_type": "markdown", "id": "af046a0c-281c-4dad-9dc7-582a8cda6b06", "metadata": {}, "source": [ "As you can see, all methods on the Cluster object are async by default.\n", "Every async method also has a `_sync` variant, if you don't want to / can't use asyncio." ] }, { "cell_type": "code", "execution_count": 4, "id": "e239a78a-2907-43e4-80be-eaba13dfac0a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting 2 engines with \n" ] }, { "data": { "text/plain": [ "'1623757385-pe8h'" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "engine_set_2 = cluster.start_engines_sync(n=2)\n", "engine_set_2" ] }, { "cell_type": "markdown", "id": "f289aaba-7417-40d3-84ea-88bf83715140", "metadata": {}, "source": [ "At this point, we have a cluster with a controller and six engines in two groups.\n", "\n", "There is also a `start_cluster` method that starts the controller and one engine set, for convenience:\n", "\n", "```python\n", "engine_set_id = await cluster.start_cluster(n=4)\n", "```\n", "\n", "We can get a client object connected to the cluster with `connect_client()`" ] }, { "cell_type": "code", "execution_count": 5, "id": "fe2dbd68-1dcb-4de9-a45f-757314261b8c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 4, 5]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rc = await cluster.connect_client()\n", "rc.wait_for_engines(6)\n", "rc.ids" ] }, { "cell_type": "markdown", "id": "d9da9293-3a44-4f16-8f81-7faaf80f20cd", "metadata": {}, "source": [ "And we can use our classic `apply_async(...).get_dict()` pattern to get a dict by engine id of hostname, pid for each engine:" ] }, { "cell_type": "code", "execution_count": 6, "id": "a00c2116-269e-4016-a3d3-ec71ae1b093c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{0: {'host': 'touchy', 'pid': 24774},\n", " 1: {'host': 'touchy', 'pid': 24775},\n", " 2: {'host': 'touchy', 'pid': 24776},\n", " 3: {'host': 'touchy', 'pid': 24762},\n", " 4: {'host': 'touchy', 'pid': 24769},\n", " 5: {'host': 'touchy', 'pid': 24773}}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def identify():\n", " import os\n", " import socket\n", "\n", " return {\"host\": socket.gethostname(), \"pid\": os.getpid()}\n", "\n", "\n", "rc[:].apply_async(identify).get_dict()" ] }, { "cell_type": "markdown", "id": "1e71632f-d2d2-4c1b-b258-54ee639a8409", "metadata": {}, "source": [ "We can send signals to engine sets by id\n", "\n", "*(sending signals to just one engine is still a work in progress)*" ] }, { "cell_type": "code", "execution_count": 7, "id": "6ff6f7da-38e0-4fea-83af-ff875eebcc76", "metadata": {}, "outputs": [], "source": [ "import signal\n", "import time\n", "\n", "ar = rc[:].apply_async(time.sleep, 100)" ] }, { "cell_type": "code", "execution_count": 8, "id": "e898c30f-996b-44cd-bfad-84516d27a004", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sending signal 2 to engine(s) 1623757384-b3pm\n", "Sending signal 2 to engine(s) 1623757385-pe8h\n" ] }, { "ename": "CompositeError", "evalue": "one or more exceptions from call to method: sleep\n[0:apply]: KeyboardInterrupt: \n[1:apply]: KeyboardInterrupt: \n[2:apply]: KeyboardInterrupt: \n[3:apply]: KeyboardInterrupt: \n.... 2 more exceptions ...", "output_type": "error", "traceback": [ "[0:apply]: ", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m: ", "", "[1:apply]: ", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m: ", "", "[2:apply]: ", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m: ", "", "[3:apply]: ", "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m: ", "", "... 2 more exceptions ..." ] } ], "source": [ "# oops! I meant 1!\n", "\n", "await cluster.signal_engines(signal.SIGINT)\n", "ar.get()" ] }, { "cell_type": "markdown", "id": "ba286e10-0e9d-41af-9016-ceb8d7943cbe", "metadata": {}, "source": [ "Now it's time to cleanup. Every `start_` method has a correspinding `stop_method`.\n", "\n", "We can stop one engine set at a time with `stop_engines`:" ] }, { "cell_type": "code", "execution_count": 9, "id": "70574035-e97b-4a3b-acfd-ca404bab7e04", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stopping engine(s): 1623757385-pe8h\n" ] } ], "source": [ "await cluster.stop_engines(engine_set_2)" ] }, { "cell_type": "markdown", "id": "89026a15-60a2-40b5-beff-8b281ae624b3", "metadata": {}, "source": [ "Or stop the whole cluster" ] }, { "cell_type": "code", "execution_count": 10, "id": "81275363-a1db-4d6d-9330-514d2355a219", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stopping engine(s): 1623757384-b3pm\n", "Stopping controller\n", "Controller stopped: {'exit_code': 0, 'pid': 24758}\n" ] } ], "source": [ "await cluster.stop_cluster()" ] }, { "cell_type": "markdown", "id": "7835fa97-59dc-4146-9bdf-43640bbb1801", "metadata": {}, "source": [ "## Cluster as a context manager\n", "\n", "Cluster can also be used as a Context manager,\n", "in which case:\n", "\n", "1. entering the context manager starts the cluster\n", "2. the `as` returns a connected client\n", "3. the context is only entered when all the engines are fully registered and available\n", "4. when the context exits, the cluster is torn down\n", "\n", "This makes it a lot easier to scope an IPython cluster for the duration of a computation\n", "and ensure that it is cleaned up when you are done." ] }, { "cell_type": "code", "execution_count": 11, "id": "31bf1cfb-9349-46a1-b412-50678368a017", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting 4 engines with \n", "Stopping engine(s): 1623757397-ng0s\n", "Stopping controller\n" ] }, { "data": { "text/plain": [ "{0: 24989, 1: 24991, 2: 24990, 3: 24992}" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import os\n", "\n", "with Cluster(n=4) as rc:\n", " engine_pids = rc[:].apply_async(os.getpid).get_dict()\n", "engine_pids" ] }, { "cell_type": "markdown", "id": "5d2d5f22-8116-45e8-8c82-c24e19f7e0de", "metadata": {}, "source": [ "It can also be async" ] }, { "cell_type": "code", "execution_count": 12, "id": "3c0cdb81-4288-4e3a-b25a-7b0b0ce6016a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting 2 engines with \n", "Controller stopped: {'exit_code': 0, 'pid': 24988}\n", "Stopping engine(s): 1623757400-5fq1\n", "Stopping controller\n" ] }, { "data": { "text/plain": [ "{0: 25058, 1: 25059}" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "async with Cluster(n=2) as rc:\n", " engine_pids = rc[:].apply_async(os.getpid).get_dict()\n", "engine_pids" ] }, { "cell_type": "markdown", "id": "b7537226-dc39-4859-bcfb-16c79a16b951", "metadata": {}, "source": [ "## Launcher classes\n", "\n", "IPython's mechanism for launching controllers and engines is called `Launchers`.\n", "These are in `ipyparallel.cluster.launcher`.\n", "\n", "There are two kinds of Launcher:\n", "\n", "- ControllerLauncher, which starts a controller\n", "- EngineSetLauncher, which starts `n` engines\n", "\n", "You can use abbreviations to access the launchers that ship with IPython parallel,\n", "such as 'MPI', 'Local', or 'SGE',\n", "or you can pass classes themselves (or their import strings, such as 'mymodule.MyEngineSetLauncher').\n", "\n", "I'm going to start a cluster with engines using MPI:" ] }, { "cell_type": "code", "execution_count": 13, "id": "b85d2009-d60c-4642-a15c-84c80adcc361", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Controller stopped: {'exit_code': 0, 'pid': 25057}\n", "Starting 4 engines with \n" ] } ], "source": [ "import os\n", "os.environ[\"OMPI_MCA_rmaps_base_oversubscribe\"] = \"1\"\n", "\n", "cluster = Cluster(n=4, engine_launcher_class='MPI')\n", "await cluster.start_cluster()\n", "rc = await cluster.connect_client()" ] }, { "cell_type": "code", "execution_count": 14, "id": "91062c98-3ff2-494f-ae02-6cb96648b6ef", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rc.wait_for_engines(4)\n", "rc.ids" ] }, { "cell_type": "markdown", "id": "9274020a-a556-4d4c-a67b-170b448d1a91", "metadata": {}, "source": [ "Now I'm going to run a test with another new feature" ] }, { "cell_type": "code", "execution_count": 15, "id": "9a078e90-bdf8-4f8e-aedc-bcabf2c9ba87", "metadata": {}, "outputs": [ { "ename": "TimeoutError", "evalue": "Result not ready.", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTimeoutError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/qr/3vxfnp1x2t1fw55dr288mphc0000gn/T/ipykernel_24747/824703262.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0mar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_async\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0muhoh\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mar\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m~/dev/ip/parallel/ipyparallel/client/asyncresult.py\u001b[0m in \u001b[0;36mget\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 227\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexception\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 228\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 229\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0merror\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTimeoutError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Result not ready.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 230\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 231\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_check_ready\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTimeoutError\u001b[0m: Result not ready." ] } ], "source": [ "def uhoh():\n", " import time\n", " from mpi4py import MPI\n", "\n", " rank = MPI.COMM_WORLD.rank\n", " if rank == 0:\n", " print(\"rank 0: oh no.\")\n", " 1 / 0\n", " print(f\"rank {rank}: barrier\")\n", " MPI.COMM_WORLD.barrier()\n", "\n", "\n", "ar = rc[:].apply_async(uhoh)\n", "ar.get(timeout=2)" ] }, { "cell_type": "markdown", "id": "f69526d7-4c39-422e-8654-76fbac9ce14e", "metadata": {}, "source": [ "Uh oh! We are stuck in barrier because engine 0 failed.\n", "\n", "Let's try interrupting and getting the errors:" ] }, { "cell_type": "code", "execution_count": null, "id": "ebe3e6d1-10cf-49be-83b6-da8dbaf712b1", "metadata": {}, "outputs": [], "source": [ "import signal\n", "await cluster.signal_engines(signal.SIGINT)\n", "ar.get(timeout=2)" ] }, { "cell_type": "markdown", "id": "afd8854b-2138-458d-b2bb-69acdc009b7f", "metadata": {}, "source": [ "It didn't work! This is because MPI.barrier isn't actually interruptible 😢.\n", "\n", "We are going to have to resort to more drastic measures, and *restart* the engines:" ] }, { "cell_type": "code", "execution_count": 16, "id": "2ae1cb50-965b-400e-b39f-4567df667da2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stopping engine(s): 1623757404-oexv\n", "Starting 4 engines with \n" ] } ], "source": [ "await cluster.restart_engines()" ] }, { "cell_type": "code", "execution_count": 17, "id": "c676323b-1bf7-4983-8139-5b085c2fdf91", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "engine set stopped 1623757404-oexv: {'exit_code': -9, 'pid': 25078}\n" ] } ], "source": [ "rc.wait_for_engines(4)\n", "rc.ids" ] }, { "cell_type": "markdown", "id": "7c49fa83-087c-4237-acbe-7d6c8ca208c9", "metadata": {}, "source": [ "We are now back to having 4 responsive engines.\n", "Their IPP engine id may have changed, but I can get back to using them." ] }, { "cell_type": "code", "execution_count": 19, "id": "7e261d39-0793-4207-813a-24caa4ec9a92", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{4: 0, 5: 2, 6: 3, 7: 1}" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def get_rank():\n", " from mpi4py import MPI\n", "\n", " return MPI.COMM_WORLD.rank\n", "\n", "rank_map = rc[:].apply_async(get_rank).get_dict()\n", "rank_map" ] }, { "cell_type": "markdown", "id": "9a67ca12-67f5-4539-939f-80a6e61b5692", "metadata": {}, "source": [ "Finally, clean everything up" ] }, { "cell_type": "code", "execution_count": 20, "id": "060108bb-229b-4829-bf32-0dc7d0db0345", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stopping engine(s): 1623757404-oexv\n", "Stopping controller\n", "Controller stopped: {'exit_code': 0, 'pid': 25076}\n", "engine set stopped 1623757404-oexv: {'exit_code': -9, 'pid': 25154}\n" ] } ], "source": [ "await cluster.stop_cluster()" ] }, { "cell_type": "markdown", "id": "4607e5e6-e585-413f-85aa-281e606f7f82", "metadata": {}, "source": [ "## Connecting to existing clusters\n", "\n", "a Cluster object writes its state to disk,\n", "in a file accessible as `cluster.cluster_file`.\n", "By default, this willb e `$PROFILE_DIR/security/ipcluster-$cluster-id.json`.\n", "\n", "Cluster objects can load state from a dictionary with `Cluster.from_dict(d)`\n", "or from a JSON file containing that information with `Cluster.from_file()`.\n", "\n", "The default arguments for `from_file` are to use the current IPython profile (default: 'default')\n", "and empty cluster id,\n", "so if you start a cluster with `ipcluster start`, you can connect to it immediately with\n", "\n", "```python\n", "cluster = ipp.Cluster.from_file()\n", "```" ] }, { "cell_type": "code", "execution_count": 2, "id": "0da44ee0-2a3f-4b98-b31a-9c71621b9777", "metadata": {}, "outputs": [], "source": [ "import ipyparallel as ipp\n", "cluster = ipp.Cluster.from_file()" ] }, { "cell_type": "code", "execution_count": 3, "id": "0df1b2b2-0d35-45a7-8ad8-c7f55eb52a5f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ ", engine_sets=['1624884556-z9qr'])>" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cluster" ] }, { "cell_type": "markdown", "id": "947edd74-5b1c-42ca-a83a-1755a96c6469", "metadata": {}, "source": [ "`ipp.ClusterManager` provides an API for collecting/discovering/loading all the clusters on your system.\n", "\n", "By default, it finds loads clusters in all your IPython profiles,\n", "but can be confined to one profile or use explicit profile directories." ] }, { "cell_type": "code", "execution_count": 6, "id": "c8b30824-b833-449c-b1eb-71b56ff47ba6", "metadata": {}, "outputs": [], "source": [ "clusters = ipp.ClusterManager().load_clusters()" ] }, { "cell_type": "code", "execution_count": 8, "id": "6c5e2fe0-c45c-4528-8732-d6bd56580885", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'mpi:abc-123': , engine_sets=['1624884663-euj7'])>,\n", " 'default:': , engine_sets=['1624884556-z9qr'])>}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "clusters" ] }, { "cell_type": "markdown", "id": "b75d78c5-5f49-436f-b313-c56085f70e43", "metadata": {}, "source": [ "This is the class that powers the new `ipcluster list`" ] }, { "cell_type": "code", "execution_count": 10, "id": "cbf3d3f3-70e1-4c92-b6d5-bf17ea913253", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PROFILE CLUSTER ID RUNNING ENGINES LAUNCHER\n", "default '' True 4 Local\n", "mpi abc-123 True 4 MPI\n" ] } ], "source": [ "!ipcluster list" ] }, { "cell_type": "code", "execution_count": 11, "id": "08a6c241-8d05-45fa-bfe1-ba89f4257ade", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2021-06-28 14:53:00.591 [IPClusterStop] Stopping engine(s): 1624884663-euj7\n", "2021-06-28 14:53:00.592 [IPClusterStop] Stopping controller\n" ] } ], "source": [ "!ipcluster stop --profile mpi --cluster-id abc-123" ] }, { "cell_type": "code", "execution_count": 12, "id": "5ec2c3c2-153c-4fbf-b52d-567530533a3c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PROFILE CLUSTER ID RUNNING ENGINES LAUNCHER\n", "default '' True 4 Local\n" ] } ], "source": [ "!ipcluster list" ] }, { "cell_type": "markdown", "id": "3b3e53cf-4b30-4496-8b84-993daf4fc527", "metadata": {}, "source": [ "The same operation can be done from the Python API:" ] }, { "cell_type": "code", "execution_count": 13, "id": "7be40506-7108-4fbc-8b8f-16e4d478ed84", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stopping engine(s): 1624884556-z9qr\n", "Stopping controller\n" ] } ], "source": [ "cluster = ipp.Cluster.from_file(profile=\"default\", cluster_id=\"\")\n", "await cluster.stop_cluster()" ] }, { "cell_type": "code", "execution_count": 14, "id": "572b2e45-4b20-4f8d-81b0-9334a42e69e7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PROFILE CLUSTER ID RUNNING ENGINES LAUNCHER\n" ] } ], "source": [ "!ipcluster list" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 } ipyparallel-7.1.0/docs/source/examples/Data Publication API.ipynb000066400000000000000000010411141412533144600246500ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# IPython's Data Publication API" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "IPython has an API that allows IPython Engines to publish data back to the Client. This Notebook shows how this API works." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We begin by enabling matplotlib plotting and creating a `Client` object to work with an IPython cluster." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import ipyparallel as ipp" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = ipp.Client()\n", "dv = c[:]\n", "dv.block = False\n", "dv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simple publication" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a simple Python function we are going to run on the Engines. This function uses `publish_data` to publish a simple Python dictionary when it is run." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def publish_it():\n", " from ipyparallel.datapub import publish_data\n", " publish_data(dict(a='hi'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We run the function on the Engines using `apply_async` and save the returned `AsyncResult` object:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "ar = dv.apply_async(publish_it)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The published data from each engine is then available under the `.data` attribute of the `AsyncResult` object." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{}, {}, {}, {}]" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ar.data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each time `publish_data` is called, the `.data` attribute is updated with the most recently published data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulation loop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In many cases, the Engines will be running a simulation loop and we will want to publish data at each time step of the simulation. To show how this works, we create a mock simulation function that iterates over a loop and publishes a NumPy array and loop variable at each time step. By inserting a call to `time.sleep(1)`, we ensure that new data will be published every second." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "def simulation_loop():\n", " from ipyparallel.datapub import publish_data\n", " import time\n", " import numpy as np\n", " for i in range(10):\n", " publish_data(dict(a=np.random.rand(20), i=i))\n", " time.sleep(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, we run the `simulation_loop` function in parallel using `apply_async` and save the returned `AsyncResult` object." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "ar = dv.apply_async(simulation_loop)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "New data will be published by the Engines every second. Anytime we access `ar.data`, we will get the most recently published data." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\ntxG76/tLG3Nkcil6fAN0eiofcnPfoHr/bzSweTtJFql4HzSoEp9dDNO7rJo9t5YuG/KajVBI4+E2\nexKg4FX1l8GrEqp2IqCYAtiMCqCQMMlCUXOZVQCnRBWwgYMR9GRVfOf34+WVBL/0raucmYnic1r5\njU8e4G8+NoTtDrM1rCerjDWrJEniV5/dT8Br5/JKkq9/UD2iolS5jSiRs5eR7DZ6nn6Ip49/BoA3\nLxtTBbx8boE/+o/v8cHpaUNe716VUcmqPwD+AfAfgT8PfAX4GeBPg8Fg2T8jGAweAL4E/JuJiYnz\nBl1bVdKTVfcQZL2Ql5m8pp5OGWZW6ckq4yKhd0qkttL3EGBdURS+fVldLPyFRz6F0+7i+sIFVjbU\nlEyPnqwyz2K1EtUCWS+aVcYnqy7ozIWdpwCWyqtzq8xTyxDauKmmp6K9fTyoTQTyjNZeAzQqWSVq\ngJ5WSVZ1q3+70fXNmWsRUQNsTwNsqiL6JMDya1aiBri0dotTezrIy4qeYjKLpjfSLESz+F02xvsr\nPi/UP6PakwDrJ0mS9ImAm0HWq+VVCZ0YEBMBzXVY5TN4ImB6foXeFXWL0VJmlZZkCTR7EmAFcHUh\nfSJghckqsXZ64EDzzKp+kyarAE4ONt6scnvsIEEqlduSb6soCt/8aIUvfec6y/EsR/s8fO3zx7Zk\nIYa0ZJWYvmikOl02Xnp+BIsEf3humXMLdxv9lWr19fdAlul+9BQ2r4dHDz2Pw+bkytxZ/b+lFq1r\ne5juQOX34raKqtmsCgaDfx74aeDPT0xMfHliYuJ7ExMT/wb4OPAY8MUKXu63gXXg/6r1umqV6JGv\n3kNm1ezkGtlMgcBAhz7RqlbpzKqheiarVCMstbCCIpuvZlUPfbiUYHo9TY/bxscO7eHhQ2q66q0r\n3wdKzSpzLVbLVWmyqlJIbL1qgIl0jNc+/J8APHDg6YqeW1ykmy9ZtXB5CgDfgX14HOrETs8Bzay6\nVXuyKpIIIyvVvy+LyarWMKucLhtuj518Xr4rSZfLZ0lkYlgkKx3uylMvbRmj7OoG6bklrG4X3sMj\nZT8v0DmI2+ElklzjUe3g+MeT5qoCCvPsif2dVVWM2smqxkivAm4yxELnVVVpVo12u/E6rCzHszrI\n2wwyeiJgemGZwJJIVl0y5DUbIT1Z1cRJgJl0nvXVJBarRG9/+WslvQa4Ur5ZFUmscWv5Mnabk/F9\n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\nYII=\n", "text/plain": [ "" ] }, "metadata": { "image/png": { "height": 392, "width": 597 } }, "output_type": "display_data" } ], "source": [ "data = ar.data\n", "for i, d in enumerate(data):\n", " plt.plot(d['a'], label='engine: '+str(i))\n", "plt.title('Data published at time step: ' + str(data[0]['i']))\n", "plt.legend()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } ipyparallel-7.1.0/docs/source/examples/Futures.ipynb000066400000000000000000000454711412533144600226210ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Futures in IPython Parallel\n", "\n", "The IPython Parallel AsyncResult object extends `concurrent.futures.Future`,\n", "which makes it compatible with most async frameworks in Python.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Starting 4 engines with \n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "107792592f044155ab2d9dec950ca6e7", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/4 [00:00" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import ipyparallel as ipp\n", "rc = ipp.Cluster(n=4).start_and_connect_sync()\n", "\n", "dv = rc[:]\n", "dv.activate()\n", "dv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Do some imports everywhere" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "%%px --local --block\n", "import os\n", "import time\n", "import numpy\n", "from numpy.linalg import norm" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [] }, "outputs": [], "source": [ "def random_norm(n):\n", " \"\"\"Generates a 1xN array and computes its 2-norm\"\"\"\n", " A = numpy.random.random(n)\n", " return norm(A, 2)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The basic async API hasn't changed:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = rc[-1].apply(random_norm, 100)\n", "f" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.854015134508366" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f.get()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But the full Futures API is now available:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.854015134508366" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f.result()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The standard futures API has methods for registering callbackes, etc." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I got PID: 12509" ] }, { "data": { "text/plain": [ "12509" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" }, { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "import os\n", "f = rc[-1].apply(os.getpid)\n", "f.add_done_callback(lambda _: print(\"I got PID: %i\" % _.result()))\n", "f.result()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A more complex example shows us how AsyncResults can be integrated into existing async applications, now that they are Futures:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ". [12507, 12506, 12508, 12509]\n", ". . [12507, 12506, 12508, 12509]\n", ". . . [12507, 12506, 12508, 12509]\n", ". . . . [12507, 12506, 12508, 12509]\n" ] } ], "source": [ "import asyncio\n", "from tornado.ioloop import IOLoop\n", "import sys\n", "\n", "def sleep_task(t):\n", " time.sleep(t)\n", " return os.getpid()\n", "\n", "async def background():\n", " \"\"\"A backgorund coroutine to demonstrate that we aren't blocking\"\"\"\n", " while True:\n", " await asyncio.sleep(1)\n", " print('.', end=' ')\n", " sys.stdout.flush() # not needed after ipykernel 4.3\n", "\n", "async def work():\n", " \"\"\"Submit some work and print the results when complete\"\"\"\n", " for t in [ 1, 2, 3, 4 ]:\n", " ar = rc[:].apply(sleep_task, t)\n", " result = await asyncio.wrap_future(ar) # this waits\n", " print(result)\n", "\n", "bg = asyncio.Task(background())\n", "await work()\n", "bg.cancel();\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So if you have an existing async application using coroutines and/or Futures,\n", "you can now integrate IPython Parallel as a standard async component for submitting work and waiting for its results." ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Executors\n", "\n", "Executors are a standard Python API provided by various job-submission tools.\n", "A standard API such as Executor is useful for different libraries to expose this common API for asynchronous execution,\n", "because it means different implementations can be easily swapped out for each other and compared,\n", "or the best one for a given context can be used without having to change the code.\n", "\n", "With IPython Parallel, every View has an `.executor` property, to provide the Executor API for the given View.\n", "Just like Views, the assignment of work for an Executor depends on the View from which it was created.\n", "\n", "You can get an Executor for any View by accessing `View.executor`:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ex_all = rc[:].executor\n", "ex_all.view.targets" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "12507\n", "12508\n", "12507\n", "12508\n", "12507\n", "12508\n", "12507\n", "12508\n", "12507\n", "12508\n" ] } ], "source": [ "even_lbview = rc.load_balanced_view(targets=rc.ids[::2])\n", "ex_even = even_lbview.executor\n", "for pid in ex_even.map(lambda x: os.getpid(), range(10)):\n", " print(pid)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Typically, though, one will want an Executor for a LoadBalancedView on all the engines.\n", "This is what the top-level `Client.executor()` method will return:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ex = rc.executor()\n", "ex.view" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's create a few compatible Executor instances" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Comparing Executors\n", "\n", "Let's make a few Executors. Aside: [dask.distributed][] is a great library. Any IPython Parallel cluster can be bootstrapped into a dask cluster.\n", "\n", "[dask.distributed]: https://distributed.readthedocs.io\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There *can* be serialization differences, especially for interactively defined functions (i.e. those in defined in a notebook itself).\n", "That's why we define our task function in a local module,\n", "rather than here. ProcessPoolExecutor doesn't serialize interactively defined functions.\n", "But for the most part working with functions defined in modules works consistently across implementations." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[0;32mimport\u001b[0m \u001b[0mnumpy\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mnumpy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlinalg\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mnorm\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Generates a 1xN array and computes its 2-norm\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mA\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnumpy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mones\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnorm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%pycat task_mod.py\n", "from task_mod import task" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "tags": [] }, "outputs": [], "source": [ "def task(n):\n", " \"\"\"Generates a 1xN array and computes its 2-norm\"\"\"\n", " import numpy\n", " from numpy.linalg import norm\n", " A = numpy.ones(n)\n", " return norm(A, 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor\n", "import distributed\n", "\n", "distributed_client = rc.become_dask()\n", "dist_ex = distributed_client.get_executor()\n", "\n", "N = 4\n", "ip_ex = rc.executor(targets=range(N))\n", "thread_ex = ThreadPoolExecutor(N)\n", "process_ex = ProcessPoolExecutor(N)\n" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "tags": [] }, "outputs": [], "source": [ "executors = [process_ex, thread_ex, ip_ex, dist_ex]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can submit the same work with the same API,\n", "using four different mechanisms for distributing work.\n", "The results will be the same:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for executor in executors:\n", " print(executor.__class__.__name__)\n", " it = executor.map(str, range(5))\n", " print(list(it))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This makes it easy to compare the different implementations. We are going to submit some dummy work—allocate and compute 2-norms of arrays of various sizes." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1048576, 1261463, 1517571, 1825676, 2196334, 2642245,\n", " 3178688, 3824041, 4600417, 5534417, 6658042, 8009791,\n", " 9635980, 11592325, 13945857, 16777216])" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sizes = np.logspace(20, 24, 16, base=2, dtype=int)\n", "sizes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Run the work locally, to get a reference:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Local time:\n", "CPU times: user 765 ms, sys: 403 ms, total: 1.17 s\n", "Wall time: 874 ms\n" ] } ], "source": [ "print(\"Local time:\")\n", "%time ref = list(map(task, sizes))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then run again with the various Executors:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ProcessPoolExecutor\n", "246 ms ± 86 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", "ThreadPoolExecutor\n", "182 ms ± 32.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", "ViewExecutor\n", "228 ms ± 24.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", "ClientExecutor\n", "246 ms ± 27.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], "source": [ "for executor in executors:\n", " print(executor.__class__.__name__)\n", " result = executor.map(task, sizes)\n", " rlist = list(result)\n", " assert rlist == ref, \"%s != %s\" % (rlist, ref)\n", " # time the task assignment\n", " %timeit list(executor.map(task, sizes))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this toy work, the stdlib ThreadPoolExecutor appears to perform the best.\n", "That's useful info, and likely to be true for most workloads that release the GIL and fit comfortably into memory.\n", "When the GIL is involved, ProcessPoolExecutor is often best for simple workloads.\n", "\n", "One benefit of IPython Parallel or Distributed Executors over the stdlib Executors is that they do not have to be confined to a single machine.\n", "This means the standard Executor API lets you develop small-scale parallel tools that run locally in threads or processes,\n", "and then extend the *exact same code* to make use of multiple machines,\n", "just by selecting a different Executor.\n", "\n", "That seems pretty useful. [joblib][] is another package to implement standardized APIs for parallel backends,\n", "which IPython Parallel [also supports](joblib.ipynb).\n", "\n", "[joblib]: https://joblib.readthedocs.io" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "107792592f044155ab2d9dec950ca6e7": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_2d6218bb526d44f0acda723f0c6dc861", "IPY_MODEL_acda3a9b8ac74e12bfcc6408aa8b11ad", "IPY_MODEL_30dc8b7299de4c6f869fa41aebba7f45" ], "layout": "IPY_MODEL_412b13dee37849cd97baf8ab5f0552f3" } }, "2d6218bb526d44f0acda723f0c6dc861": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_a17b92b8857c4bd5b883c5615021c713", "style": "IPY_MODEL_fd97190cd7094a429ec44137db60db39", "value": "100%" } }, "30dc8b7299de4c6f869fa41aebba7f45": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_8783cf96737d4e03a7dd05208445afe1", "style": "IPY_MODEL_398a5bd036a643fc9be7e97200b091e3", "value": " 4/4 [00:04<00:00, 4.75s/engine]" } }, "398a5bd036a643fc9be7e97200b091e3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "412b13dee37849cd97baf8ab5f0552f3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8783cf96737d4e03a7dd05208445afe1": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "97f0bee4e8c64f608146df6c4b9e30c0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": { "description_width": "" } }, "a17b92b8857c4bd5b883c5615021c713": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "acda3a9b8ac74e12bfcc6408aa8b11ad": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": { "bar_style": "success", "layout": "IPY_MODEL_c80f0d14fe6542e0b25385efb22e54e5", "max": 4, "style": "IPY_MODEL_97f0bee4e8c64f608146df6c4b9e30c0", "value": 4 } }, "c80f0d14fe6542e0b25385efb22e54e5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fd97190cd7094a429ec44137db60db39": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/Index.ipynb000066400000000000000000000211211412533144600222150ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "# examples\n", "\n", "Here you will find example notebooks and scripts for working with IPython Parallel." ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Tutorials" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "nbsphinx-toctree" ] }, "source": [ "* [Creating Clusters from Python](Cluster%20API.ipynb)\n", "* [Data Publication API](Data%20Publication%20API.ipynb)\n", "* [Parallel Magics](Parallel%20Magics.ipynb)\n", "* [Visualizing AsyncResults](visualizing-tasks.ipynb)\n", "* [Interactive widgets in IPython Parallel](progress.ipynb)\n", "* [Broadcast View](broadcast/Broadcast%20view.ipynb)\n", "* [Broadcasting arrays with memmap](broadcast/memmap%20Broadcast.ipynb)\n", "* [Broadcasting arrays with MPI](broadcast/MPI%20Broadcast.ipynb)\n" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Examples" ] }, { "cell_type": "markdown", "metadata": { "tags": [ "nbsphinx-toctree" ] }, "source": [ "[Monitoring an MPI Simulation - 1](Monitoring%20an%20MPI%20Simulation%20-%201.ipynb)\n", "\n", "[Monitoring an MPI Simulation - 2](Monitoring%20an%20MPI%20Simulation%20-%202.ipynb)\n", "\n", "[Parallel decorator and map](Parallel%20Decorator%20and%20map.ipynb)\n", "\n", "[Using MPI with IPython Parallel](Using%20MPI%20with%20IPython%20Parallel.ipynb)\n", "\n", "[Monte Carlo Options](Monte%20Carlo%20Options.ipynb)\n", "\n", "[Random Matrices](rmt/rmt.ipynb)" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Integrating IPython Parallel with other tools\n", "\n", "There are lots of cool tools for working with asynchronous and parallel execution. IPython Parallel aims to be fairly compatible with these, both by implementing explicit support via methods such as `Client.become_dask`, and by using standards such as the `concurrent.futures.Future` API." ] }, { "cell_type": "markdown", "metadata": { "tags": [ "nbsphinx-toctree" ] }, "source": [ "* [Using Futures with IPython Parallel](Futures.ipynb)\n", "* [IPython Parallel as a joblib backend](joblib.ipynb)\n", "* [Working with IPython and dask.distributed](dask.ipynb)\n", "* [Using dill to serialize anything](Using%20Dill.ipynb)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Non-notebook examples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This directory also contains some examples that are scripts instead of notebooks." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "./
\n", "  dagdeps.py
\n", "  customresults.py
\n", "  throughput.py
\n", "  itermapresult.py
\n", "  task_profiler.py
\n", "  fetchparse.py
\n", "  iopubwatcher.py
\n", "  nwmerge.py
\n", "  phistogram.py
\n", "  dependencies.py
" ], "text/plain": [ "./\n", " dagdeps.py\n", " customresults.py\n", " throughput.py\n", " itermapresult.py\n", " task_profiler.py\n", " fetchparse.py\n", " iopubwatcher.py\n", " nwmerge.py\n", " phistogram.py\n", " dependencies.py" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import glob\n", "import os\n", "\n", "from IPython.display import FileLink, FileLinks, display\n", "\n", "FileLinks(\".\", included_suffixes=[\".py\"], recursive=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More substantial examples can be found in subdirectories:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "wave2D/
\n", "  wavesolver.py
\n", "  RectPartitioner.py
\n", "  parallelwave-mpi.py
\n", "  communicator.py
\n", "  parallelwave.py
" ], "text/plain": [ "wave2D/\n", " wavesolver.py\n", " RectPartitioner.py\n", " parallelwave-mpi.py\n", " communicator.py\n", " parallelwave.py" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "pi/
\n", "  pidigits.py
\n", "  parallelpi.py
" ], "text/plain": [ "pi/\n", " pidigits.py\n", " parallelpi.py" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "daVinci Word Count/
\n", "  wordfreq.py
\n", "  pwordfreq.py
" ], "text/plain": [ "daVinci Word Count/\n", " wordfreq.py\n", " pwordfreq.py" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "interengine/
\n", "  bintree_script.py
\n", "  bintree.py
\n", "  communicator.py
\n", "  interengine.py
" ], "text/plain": [ "interengine/\n", " bintree_script.py\n", " bintree.py\n", " communicator.py\n", " interengine.py" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "broadcast/
\n", "  test.py
" ], "text/plain": [ "broadcast/\n", " test.py" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "rmt/
\n", "  rmtkernel.py
\n", "  rmt.ipynb
" ], "text/plain": [ "rmt/\n", " rmtkernel.py\n", " rmt.ipynb" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "for path in os.listdir(\".\"):\n", " if os.path.isdir(path) and not path.startswith(\".\"):\n", " display(FileLinks(path, included_suffixes=['.py', '.ipynb']))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "nbsphinx": { "execute": "always" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/Monitoring an MPI Simulation - 1.ipynb000066400000000000000000002034471412533144600266400ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_start": false } }, "source": [ "# Interactive monitoring of a parallel MPI simulation from a notebook\n", "\n", "first, start a cluster with mpi.\n", "\n", "Or load an existing cluster with e.g.\n", "\n", "```python\n", "cluster = Cluster.from_file(profile=\"mpi\")\n", "```" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "slideshow": { "slide_start": false } }, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "from IPython.display import display\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Starting 4 engines with \n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "8b14b3b6f0cb4a67be4645c8274c4074", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/4 [00:00" ] }, "metadata": { "image/png": { "height": 365, "width": 572 } }, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Monitoring interrupted, simulation is ongoing!\n", "Monitored for: 0:00:19.277042.\n" ] } ], "source": [ "monitor_simulation(refresh=1);" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_start": false } }, "source": [ "If you execute the following cell before the MPI code is finished running, it will stop the simulation at that point, which you can verify by calling the monitoring again:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "slideshow": { "slide_start": false } }, "outputs": [], "source": [ "view['stop'] = True" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[stdout:0] \n", "{\n", " \"shell_port\": 57579,\n", " \"iopub_port\": 57580,\n", " \"stdin_port\": 57581,\n", " \"control_port\": 57583,\n", " \"hb_port\": 57582,\n", " \"ip\": \"127.0.0.1\",\n", " \"key\": \"944dac79-816e4a12e48b45baf11ab5e8\",\n", " \"transport\": \"tcp\",\n", " \"signature_scheme\": \"hmac-sha256\",\n", " \"kernel_name\": \"\"\n", "}\n", "\n", "Paste the above JSON into a file, and connect with:\n", " $> jupyter --existing \n", "or, if you are local, you can connect with just:\n", " $> jupyter --existing /Users/minrk/.ipython/profile_default/security/kernel-46128.json\n", "or even just:\n", " $> jupyter --existing\n", "if this is the most recent Jupyter kernel you have started.\n" ] } ], "source": [ "%%px --target 0 --block\n", "from ipyparallel import bind_kernel; bind_kernel()\n", "%connect_info" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "%%px --target 0 --block\n", "%qtconsole" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "02f2d94254d542bdbd3cff7f9bc035e7": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": { "bar_style": "success", "layout": "IPY_MODEL_4cdefd8ae5764be8a8bf1f3c374492f0", "max": 4, "style": "IPY_MODEL_d101b978f358462fbcc0ad51c4be1ee1", "value": 4 } }, "0ab41c84c30445dfa2c77e0e2014dd46": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "11097de7cc114cc88d99cd17a693d510": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "20d1375897eb4fdcb87fda73573ad678": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_7a50e71a222340a4ba51e70ef3abce47", "IPY_MODEL_02f2d94254d542bdbd3cff7f9bc035e7", "IPY_MODEL_876517d46fa6488f8fb61db3ed689b96" ], "layout": "IPY_MODEL_0ab41c84c30445dfa2c77e0e2014dd46" } }, "212854fc12af402eb3a57438a851dfac": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "2231573544774560984193c60e6370cb": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "4205fec62aea485c8ffaf31ca98859aa": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "4cdefd8ae5764be8a8bf1f3c374492f0": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "5143e78c3dcc439a95b7ec24ea37c2be": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "6817e30d6c824bd194e102e20a9e7cda": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "6be3837161754e08a715ac0c78fa1080": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7a50e71a222340a4ba51e70ef3abce47": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_2231573544774560984193c60e6370cb", "style": "IPY_MODEL_6817e30d6c824bd194e102e20a9e7cda", "value": "100%" } }, "7afda4d37ef841c684bb624ff6b19267": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "811e5930f3bc42899a5e700d9b275bdb": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "876517d46fa6488f8fb61db3ed689b96": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_11097de7cc114cc88d99cd17a693d510", "style": "IPY_MODEL_7afda4d37ef841c684bb624ff6b19267", "value": " 4/4 [00:05<00:00, 5.54s/engine]" } }, "88e7ff230d284b368ace25166967a93d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_6be3837161754e08a715ac0c78fa1080", "style": "IPY_MODEL_5143e78c3dcc439a95b7ec24ea37c2be", "value": "100%" } }, "8b14b3b6f0cb4a67be4645c8274c4074": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_88e7ff230d284b368ace25166967a93d", "IPY_MODEL_eee11ad7360e4cc2b6b2f196ff4a1d45", "IPY_MODEL_9d0e878210c24c93bc764ec9ec53b84e" ], "layout": "IPY_MODEL_a15b2b7fec55486689859c321b2ca7e3" } }, "9d0e878210c24c93bc764ec9ec53b84e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_811e5930f3bc42899a5e700d9b275bdb", "style": "IPY_MODEL_212854fc12af402eb3a57438a851dfac", "value": " 4/4 [00:06<00:00, 6.12s/engine]" } }, "a15b2b7fec55486689859c321b2ca7e3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "afa1055878c444c49c4db6767994e04b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": { "description_width": "" } }, "d101b978f358462fbcc0ad51c4be1ee1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": { "description_width": "" } }, "eee11ad7360e4cc2b6b2f196ff4a1d45": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": { "bar_style": "success", "layout": "IPY_MODEL_4205fec62aea485c8ffaf31ca98859aa", "max": 4, "style": "IPY_MODEL_afa1055878c444c49c4db6767994e04b", "value": 4 } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/Monitoring an MPI Simulation - 2.ipynb000066400000000000000000001722161412533144600266400ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Interactive visualization of MPI simulaitons\n", "\n", "In this example, which builds on our previous one of interactive MPI monitoring, we now demonstrate how to use the IPython data publication APIs.\n", "\n", "\n", "\n", "## Load IPython support for working with MPI tasks\n", "\n", "First, we create a cluster using `mpi` to launch the engines.\n", "This launches the engines using `mpiexec` so they are in the same mpi universe.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Starting 4 engines with \n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "da09992ca3fd438eadf96a6599d5384e", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/4 [00:00" ] }, "metadata": { "image/png": { "height": 365, "width": 572 } }, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Simulation completed!\n", "Monitored for: 0:00:11.457755.\n" ] } ], "source": [ "monitor_simulation(ar, refresh=1)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "31b6b4a0105148e187aadfc70e0fe1c7": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_c872eb3aedc4492eb5a7d3b3c24249bb", "style": "IPY_MODEL_7b50ee6a201f48f796c337a68ba8730c", "value": " 4/4 [00:05<00:00, 5.23s/engine]" } }, "401e2a9344964c7082435def275db2c8": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "650e652f16044e4f816006c8f334eadb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_9a56d508e10842299d798de5d18aa84a", "style": "IPY_MODEL_fe5f5baeb0b24e13bfb0aa8e3d411b0b", "value": "100%" } }, "7b50ee6a201f48f796c337a68ba8730c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "7d5e98d4de4b472080ce751a65546968": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": { "bar_style": "success", "layout": "IPY_MODEL_401e2a9344964c7082435def275db2c8", "max": 4, "style": "IPY_MODEL_a577270a23bf46fa933a41e32d1a5a79", "value": 4 } }, "9a56d508e10842299d798de5d18aa84a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "a577270a23bf46fa933a41e32d1a5a79": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": { "description_width": "" } }, "bba28e827ace46e9b233476330b76938": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c872eb3aedc4492eb5a7d3b3c24249bb": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "da09992ca3fd438eadf96a6599d5384e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_650e652f16044e4f816006c8f334eadb", "IPY_MODEL_7d5e98d4de4b472080ce751a65546968", "IPY_MODEL_31b6b4a0105148e187aadfc70e0fe1c7" ], "layout": "IPY_MODEL_bba28e827ace46e9b233476330b76938" } }, "fe5f5baeb0b24e13bfb0aa8e3d411b0b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/Monte Carlo Options.ipynb000066400000000000000000005032141412533144600246750ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Parallel Monto-Carlo options pricing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook shows how to use `ipyparallel` to do Monte-Carlo options pricing in parallel. We will compute the price of a large number of options for different strike prices and volatilities." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import sys\n", "import time\n", "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here are the basic parameters for our computation." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": true }, "outputs": [], "source": [ "price = 100.0 # Initial price\n", "rate = 0.05 # Interest rate\n", "days = 260 # Days to expiration\n", "paths = 10000 # Number of MC paths\n", "n_strikes = 6 # Number of strike values\n", "min_strike = 90.0 # Min strike price\n", "max_strike = 110.0 # Max strike price\n", "n_sigmas = 5 # Number of volatility values\n", "min_sigma = 0.1 # Min volatility\n", "max_sigma = 0.4 # Max volatility" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "strike_vals = np.linspace(min_strike, max_strike, n_strikes)\n", "sigma_vals = np.linspace(min_sigma, max_sigma, n_sigmas)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Strike prices: [ 90. 94. 98. 102. 106. 110.]\n", "Volatilities: [ 0.1 0.175 0.25 0.325 0.4 ]\n" ] } ], "source": [ "from __future__ import print_function # legacy Python support\n", "print(\"Strike prices: \", strike_vals)\n", "print(\"Volatilities: \", sigma_vals)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Monte-Carlo option pricing function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following function computes the price of a single option. It returns the call and put prices for both European and Asian style options." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def price_option(S=100.0, K=100.0, sigma=0.25, r=0.05, days=260, paths=10000):\n", " \"\"\"\n", " Price European and Asian options using a Monte Carlo method.\n", "\n", " Parameters\n", " ----------\n", " S : float\n", " The initial price of the stock.\n", " K : float\n", " The strike price of the option.\n", " sigma : float\n", " The volatility of the stock.\n", " r : float\n", " The risk free interest rate.\n", " days : int\n", " The number of days until the option expires.\n", " paths : int\n", " The number of Monte Carlo paths used to price the option.\n", "\n", " Returns\n", " -------\n", " A tuple of (E. call, E. put, A. call, A. put) option prices.\n", " \"\"\"\n", " import numpy as np\n", " from math import exp,sqrt\n", " \n", " h = 1.0/days\n", " const1 = exp((r-0.5*sigma**2)*h)\n", " const2 = sigma*sqrt(h)\n", " stock_price = S*np.ones(paths, dtype='float64')\n", " stock_price_sum = np.zeros(paths, dtype='float64')\n", " for j in range(days):\n", " growth_factor = const1*np.exp(const2*np.random.standard_normal(paths))\n", " stock_price = stock_price*growth_factor\n", " stock_price_sum = stock_price_sum + stock_price\n", " stock_price_avg = stock_price_sum/days\n", " zeros = np.zeros(paths, dtype='float64')\n", " r_factor = exp(-r*h*days)\n", " euro_put = r_factor*np.mean(np.maximum(zeros, K-stock_price))\n", " asian_put = r_factor*np.mean(np.maximum(zeros, K-stock_price_avg))\n", " euro_call = r_factor*np.mean(np.maximum(zeros, stock_price-K))\n", " asian_call = r_factor*np.mean(np.maximum(zeros, stock_price_avg-K))\n", " return (euro_call, euro_put, asian_call, asian_put)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can time a single call of this function using the `%timeit` magic:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 174 ms, sys: 5.96 ms, total: 180 ms\n", "Wall time: 211 ms\n" ] }, { "data": { "text/plain": [ "(12.166236181100073,\n", " 7.6440122060909745,\n", " 6.8409606562778666,\n", " 4.4639448434953621)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%time result = price_option(S=100.0, K=100.0, sigma=0.25, r=0.05, days=260, paths=10000)\n", "result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parallel computation across strike prices and volatilities" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Client is used to setup the calculation and works with all engines." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import ipyparallel as ipp\n", "rc = ipp.Client()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `LoadBalancedView` is an interface to the engines that provides dynamic load\n", "balancing at the expense of not knowing which engine will execute the code." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": true }, "outputs": [], "source": [ "view = rc.load_balanced_view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Submit tasks for each (strike, sigma) pair. Again, we use the `%%timeit` magic to time the entire computation." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "async_results = []" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 127 ms, sys: 15.7 ms, total: 143 ms\n", "Wall time: 1.97 s\n" ] } ], "source": [ "%%time\n", "\n", "for strike in strike_vals:\n", " for sigma in sigma_vals:\n", " # This line submits the tasks for parallel computation.\n", " ar = view.apply_async(price_option, price, strike, sigma, rate, days, paths)\n", " async_results.append(ar)\n", "\n", "rc.wait(async_results) # Wait until all tasks are done." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "30" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(async_results)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Process and visualize results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Retrieve the results using the `get` method:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": true }, "outputs": [], "source": [ "results = [ar.get() for ar in async_results]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Assemble the result into a structured NumPy array." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "prices = np.empty(n_strikes*n_sigmas,\n", " dtype=[('ecall',float),('eput',float),('acall',float),('aput',float)]\n", ")\n", "\n", "for i, price in enumerate(results):\n", " prices[i] = tuple(price)\n", "\n", "prices.shape = (n_strikes, n_sigmas)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot the value of the European call in (volatility, strike) space." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABGcAAAM0CAYAAAAWak+/AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAWJQAAFiUBSVIk8AAAIABJREFUeJzs3Xv8fvlc7//nMqLRdsghOWyDIbKRnW455dT0q5AOm94k\nh2rnkNrYEqrtnDZhG1KSH0VS3uTYDInMhFCjjSKHGjOEnMdxNJpZ+491ffn4zOdwfa7rWte11rru\n99vtc/v6rrWutd6fz2Dm+5jXWqtp2zYAAAAAbMZFNr0AAAAAgG0mzgAAAABskDgDAAAAsEHiDAAA\nAMAGiTMAAAAAGyTOAAAAAGyQOAMAAACwQeIMAAAAwAaJMwAAAAAbJM4AAAAAbJA4AwAAALBB4gwA\nAADABokzAAAAABt00U0vAABgbJqmuWKSmye5UpJLJflEkvcneUvbtuf3dM2zkvzntm2P22PfBUnO\natv2mn1cGwDolzgDADNN09w6yRuO+LGfadv2+X2sh+FpmuZOSR6S5HuTNEnaY7tmv36uaZo/SvLY\ntm0/teLLt4cfAgCMkTgDABf2pSR/N8dxbZJ/63ktDEDTNFdK8uJ00zJtkrcmeUWSDyb5TJITkpQk\nP5Dkl5Lcq2maO7Vt+7rNrBgAGBNxBgAu7INt237/phfBMDRNc70kr013C9O7kty3bdu37XHoc5um\n+a4kf5zkekle0TTNDdq2PXN9qwUAxsgDgQEA9tE0zaWTvDJdmHl9kpvtE2aSJG3bvjPJzZK8LcnP\nCTMAwDxMzgAA7O/pSa6Z5ANJfrxt23MP+0Dbtl9IF2gAAOZicgYAYA9N01w3yU+ne8bMvdu2/fKG\nlwQATJQ4AwCwt19I989KZ7Rt+9ebXgwAMF3iDACsUNM0ZzVNc/4hx9y6aZoLmqZ57h77Tpvtu3LT\nNN/WNM3Tm6Z5X9M05zVN89Y9jr9S0zSPb5rm75um+WzTNF9umuZfmqb5g6ZpbnHAGk6YXefU2e8v\n2TTNrzVN89bZeb7YNM17m6b5naZprj/n9/6DTdPUpmn+tWmarzRN89GmaV7eNM0dDvncxZqm+fnZ\nsWfNrv3l2ff0kKZpvmmfz7109j386Oz3P9U0zemz9X+haZp3NE3z603TfPM869/D7dJNzfzJgp/f\nU9M0P9w0zfNmP99zmqb596Zp/rlpmqc1TXPFVV4LABgHcQYAVqtdwefbdM85OSPJL6Z7Rtzbkpy6\n88Cmae6T5F+SPDzJdZKcmeQdSf5TknsmeWPTNC9omub4gy7YNM3Nk7wvyeOSXDfd81XeneSKSe6X\n5J1N0zy5aZo9/7mhaZrjm6apSV6T5E5JLkj3KvLPJvmRJK+aRZsLRZamaW40u/bvJ7ljkq/OvoeP\nJLl+kt9K8tp9As0XZj+r/9Q0zcvSvSXpO2dr/3iSG8y+p9c2TXOk5+w1TXPZJNea/fb0o3z2oHM2\nTfO6dH8d757kUknek+T96R44/D+S/H3TNFdfxfUAgPHwQGAAuLBrNE3zhjmOe1Pbto/oaQ1PTxc5\nbtO27Rt372ya5v5JnpHk35P8SpJn7nxYbdM0t0/y20nuluQy6SLJXq6V5JQkX5od+5K2bc+fneO4\nJHdJcnKSBye5RJL771pHk+TFSW6fLur8Ytu2r9ux/z8n+Z0kd05ybpJ77f5WklwyyZOTnNy27Ud3\nfPba6aZWbjW7/hN3ffaC2ecfneSq6d6O9Ic7Pn/TJK9KcovZup++z89gLzsnWM4+wucOckGSKyR5\nRZLHtG37jmM7Zm+Felq6qPbMdFM7AMCWaNp22X/BBwDT0DTNrZO8IfNPv7y8bds77TrHB5NcrW3b\n4+a4zh+2bftzu/a9Icmtk3w5yY3atv3nPT5/tXTTJt+U5Efatn3NPte5YroJlqskuV/bts/ese+E\nJB+c/fasJDdv2/bf9jnPdyZ5a7qJnB9u2/Yvd+y7V5I/mF3ntns9NHcWed6Y5CZJbtW27Zt37b/E\nfg/bbZrmvyT5hyT/2LbtDXft+4N0sadNcre2bV+0x+fvneRZSd7atu3N97rGPte9WZI3z859sWPB\nalmHfK8XT/LP6aZovr1t20/t2r/vf7eaprkgyVlt215zFesEANbLbU0AcGHvbtv2uDm+7nT4qRbS\nJnnuXmFm5oFJLp7kT/YLM0nStu3Hkzwo3XTJQw+41s/vF2Zm5/mnJI/Z5zz/c3aO++wXHWZh43/P\nPv/ze+zf9y1Ibdu+O8nnk5y43zFJ3rlXmJn5s9mv33nA5/dyzo7/fOkjfnZfh3yv/57k7el+TtdY\n1TUBgOFzWxMADNNbDtj3Q+mCyB/NcZ5XpIsb12ya5tpt235g1/4z27b9qznO80dJnpTkVk3THN+2\n7blN03xbkhsm+de2bd95yOffPvv1lnvtnD1T5pZJbpYuTFwuybek+2eViye52D7nbdPdlrX3zrb9\nTNM0n0l3a9dR7IxVJyT5zBE/f6DZs3Zuk+TaSb4t3a1dF8vXI9KBzwkCAKZFnAGAYfrIAftOmP36\nnsNO0rbt+U3TvD/JjZNcPd1zYXZ61zyLadv2k03TfDpdNLnq7DxXm+2+yuy2mnlcefeG2a1Rv5nu\ndp69bilr9tl+zEcP2JckX0zyrXOuL0nStu1nm6b5QLpn8twqyf89yuf30zTNTdI9g+e7s/zDowGA\niRBnAGD9vmXTC1iRY3HhC0n+/oifSZI0TfPAJE9N95amZ6R7APA/tm37hR3HfDBfD0F7+dJRrnkE\nr07ygCR3Tfew3qXMwswb0k0C/Xm6B//+3c5ny8yeo3PPZa8FAIyLOAMAq9UmSdM039y27Vf2OeZC\n0yNHdHa621+ul+RfDzpw9vrra89+e9Yeh9xgngs2TXP5dFMz/7Hjmh+a/fqltm2/f57z7DrnNyd5\nbLqf2R3btn3tPode4qjnXpHfS/JLSb63aZpbtW3710ue7/HpwswT2rb99X2O2dT3CgBskAcCA8Bq\nHZv4OOitOXfIcre0vCbdrT53n+PYH0v3QNt/2eN5M0lyYtM0t5njPPeYXfP0Y6/sbtv2k+kmZr69\naZrbzrPwXa6d7lkrH94vzDRNc5V0r59eu7Zt35vk+em+799vmmbu58A0ne/Ztfm/zn599u7j9zgG\nANgi4gwArNZ7Z7/++F47m6a5RZI7LnmNpyf59yQ/1TTND+530OxV2ienC0G/dcD5ntM0zbcfcJ7r\nJnnkPud5crp48YymaS51wDl+smmapzVNs3Nq99h/vtSu7Ts9+IB1r8OD0j1f5zuSvPwIgeaxSd7a\nNM2v7dh27Hu8/F4faJrmjumecQMAbBlxBgBWq6aLFb/aNM0ddu5omuZHkrwyyb8sc4G2bT+ULloc\nl+QVTdP8ctM0X7sdZja1cYd0b3y6apJT27b9//c53YfTxYIzmqa5685I0jTNRZum+ekkb0xyqSTP\natv2dbvW8qdJXpbuNqu3NU1z0q7v+fJN0/zvdM+S+e9JbrRj9z8k+US6yZ7nN01zmR2fu3jTNI9O\ncu/Mbp9qmua4uX5AK9S27eeT/OhsDf9fuuBys/2On637yUl+Pd0tYG/dsfv16f678ZymaU7c9bmf\nTPdGrGNx75tW9k0AAIPnmTMAcGHXbJrmDXMe+6a2bR9x7Ddt2/5Z0zSvSPcH+lc1TfPhdAHkaulC\nyWnpXkl9Srq4spC2bZ/ZNM1/pHtQ7ZOSPK5pmn9Kcl6SE9MFlzbJC9MFjv2cmeRxSf50duyzmqZ5\n3+yz10kXZS5I8pQkD9vnHD+V7laduyf5y6ZpPj4772XSTYJcNN3bp+7etu0ZO76H/2ia5r5JXpzk\nLkl+rGma96aLGtdLFyjukS7q/Od0D1L+/Jw/opVp2/Z9TdN8b5IXpXtz05ubpnlrkpcn+WCSc9I9\nj+dmSe6c7q1TH03ys7teU/6Q2THXT/L+2V+vz6X763WFdH8tP5HuzVVTeWg0ADAHcQYAvlGb5Ph0\nfwifx2f22HanJP8jyU8nuW66SPGeJL+RLmIce43ypQ9Yw+ELbdtnN01zyuxaP5zuD/kXS/KxJKcm\neU7btm+c4zxvmN269IB0Uek66aZrP5Lkj5P8Xtu2/3DA589Lcq+maZ6T5D5Jvm/2PX4hyd+lixi/\n17btF/f47Ctmt3o9fPaZ6yX5VLoJoye1bfuOpml+LN3P5MpZPM4s9drqtm0/keS2TdP8RLrIcpPZ\nV7PrGh9Pd+vXE9u2/eyuc5zZNM13J3lEktskuUa613y/M8kz27Z9WdM0P5+vf69H/T68mhsARqpp\nW38fB4Bt0zTNCemmPk5b5E1L265pmm9LcvN0EeXS6SZg/iHJm9u2vWCTawMAxmeykzOllCunGxk/\nodZ62QOOu2SSlyY5Kcllaq17/hu5Usqdkzw03b8B/VKS05P8Wq31zFWvHQAYttkkzcs3vQ4AYDml\nlIsleWCSe6abQj4nyWuTPLrWetYhn/3zJLdP8qBa69OXWcckHwhcSrl+uocg3uCQ466a5M1JbpsD\nRoFLKfdPd5/5GUnulm6c+cQkby6lXG1FywYAAADWpJTSpHv23WPTDXfcOcmj0z0j7m9LKScc8Nk7\n5uu3qi9tcpMzpZSTkrwk3dsOXp7uQYJ7HXfDdPfjfzndX4hH7XPcldM9aPHxtdZH7tj+snSx5v+k\n+wsIAAAAjMcdk/xIknvVWl9wbGMp5aVJ3p3kkeleTPANSikXT/LUdC9LeN4qFjLFyZl7Jnl7kh/I\n3g9pPOZOST6d5JZJzj7guHule2vEE3ZurLV+ebbtR0spV1xmwQAAAMDafTnJk9O9AOFraq2fSndr\n0032+dzDk3yi1vpHq1rIFOPMfZPcvtb6pYMOqrU+Kskta60fP+R8t05y+izG7HZKuumjWy60UgAA\n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\nc1xqi+cvPft+3pzGyg5jHZrTWCxPn/tUqurQFk89bvb9/vMYh6Wa6z41u73jnya5ZZIHVtWpUNfT\n0AAAC5NJREFUG8bKDmM5Tq22Pvcnx6hpmNs+VVUXVNX7quq1VXW7JK9P8up1F291jJqGPvcpx6lp\nONA+Nfuz7g+THFtVn9vFWNlhLMepkRNn9ufc2ffLb/H8WtH86oqNxfIM4r9zVf1juvOld3MbboZt\n3vvUq5LcOcmvVNUbFjwWw9Pn/rQlx6hRWeRx49nprhVxux7GYjj63Ke25Dg1Kgfdpx6c5CpJXtla\nu+ra17r3W3vssDmMxQiIM/vz5XRLzm6wxfM3TFc257GU8dPpiu12Y33DhaFWXp/71E6+leTCHsZh\nsea2T7XWXpzufPrHVtUrNtnEcWr8+tyfduIYNQ6L/HPv87Pv1559d4yahj73qZ04To3DQfepq6W7\nRsynZ++19vWC2fN/n+RLs+0cpxBn9qOqLkxySra+avvdk5xZVWfPYawvJ/mHbca6W5LNLlDGCulz\nn0qS1tqlW2tX2+Txyya5cZKPzmMclmde+1Rr7elJHpnkiVX1ki3GcpwauT73p9l2jlEjd9B9qrV2\nZGvtAa21zf6W+fqz72fPxnKMmoA+96nZ9o5TIzeHP/teneSuSX5uw9fzZ88fM3v+bMcpEnHmIF6W\n5Hazizx9V2vtRknul+Sl6x677OzChwcZ6/6tte9bHtlau1265ZUv3fRVrJpe9qnZ+dIfS/La1toR\nG57+vXTV/rX7eW8G50D7VGvtV5M8Jckzqur3dzGW49S49bI/OUZNykH2qZukOz3u8Zu87xPTXb9h\n/cVbHaOmoZd9ynFqUva9T1XVWVX1vzZ+5Xvh7l2zx769bizHqQk77NChra5lxU5aaycluWOS5yb5\ncJIfS3fwPivJbavqwtbapdMthfxKVW15le7W2ruT/HNVHbPJcz+Q5L3pllI+O11VvWmSJyR5R1Xd\nd56/L5anx33qV5K8JMn7krw4yb8leVi6mv/gqnr1XH9jLM1+96nW2k2SnJbu2PO0Ld7+a1V1+mx7\nx6kJ6HF/coyaiIP8uddae1G62xy/PMnbk/y7JA9KcpckD6mqE9Zt6xg1ET3uU45TEzHPn89n7/eg\nJK9IcoWqOn/d445TE2flzMHcN8nz0l3s6U+T/EaSE5P83GwZXJJ8J925hDtdoTvpzlm8mKr6TroL\n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\nluj/AnN6nnqCXKW/AAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "metadata": { "image/png": { "height": 410, "width": 563 } }, "output_type": "display_data" } ], "source": [ "plt.figure()\n", "plt.contourf(sigma_vals, strike_vals, prices['ecall'])\n", "plt.axis('tight')\n", "plt.colorbar()\n", "plt.title('European Call')\n", "plt.xlabel(\"Volatility\")\n", "plt.ylabel(\"Strike Price\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot the value of the Asian call in (volatility, strike) space." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\nXbq3Q302XQy6XpJD2rY9uG3bg9OtMNlpM+GNOe32WNR+A8iHknxpcu9eNuhtmub5SX4v3R46z0zy\nE0kO3fhdJ78vAMBgxBkAWJyN/WO+ke61zdN8tekeLdp2c9q2bT/Ttu0ftG1793RvRrpTko+ne0zn\npVPO65eS3DTdW55u3LbtH7dt+29bHpG69JTX6lXbthel+z2aJL/RNM1M4aRpmqun+8/h60l+qm3b\nJ7Vt+9HNe9BsDWEAAH0TZwBgAZqmuVqSu6eLLb/Wtu0PTvOV5K3pwsQjD3SPtvPudG9EapLcdsqY\ncZPJvOouj1rddIrrDOX3062euU6S39nLB5umuVbTNIdvOvRj6faU+fAumybfZD+TBACYljgDAItx\nbLoo8MUkr9nD5543+X7jpmluvXGwaZqbN03z4B0+s7EKZNpVJpdIF3OusN3JyRumfiUL2ptlss/M\nAyY/PrFpmodO87mmaS6bLm59ommajeCysf/eEbt89LH7migAwJTEGQCYs6ZpLpnvxo2Xtm37jWk/\n27bt+5N8ZPLjoybX++Ek703yiqZpntg0zaU23etKSV49+fFv2ra9cIrbvGfy/VebprnflrlfO91r\nvTf2k/m+aefep7Ztj0/y6Ez+DJumeVnTNJffaXzTNNdJclKSG6R749OnJqfen24/nx9tmuYPNj/C\n1DTNZZumeWmSn0z3Cu+F/K4AwOoTZwBg/u6b5IpJvpXkJfv4/PPSrWy5Z9M0P9S27X8meVi6yPDM\nJGc2TfOhpmn+Ocl/Jrljvrux7wG1bfu2JG9KFyNe0zTN55qm+UDTNJ9MFzWuOPkdmiSX2cf8e9G2\n7YvTPbJ1dro3VP1H0zSvbZrmIU3T3LVpmrs0TfPQpmnemuQT6R5hOiHJndq2/erkGmclOW5yyccm\nOWPyZ3dykv9K93uWyf+9sN8VAFht4gwAzN8j0634eEPbtl/Yx+ffmORz6R7JeXiStG376iRHJ/nj\ndK/cvmGSKyf55yS/m+Totm1P3eZabbZ/POk+6Vbm/EOSSyW5UZKLkjwjyc2S/L/Jz1feYY7TPPK0\n072n1rbtO5Icle53/FK6mPLSJG9P8o50fx53S/LRJA9o2/ZukyCz+RovSnLnJO9Mcl663/XwJK9K\n8uOT1UpnJ7ls0zTfv8ffY+bfEQBYfU3bruY/L5RSrpLuNaJH1loP32XcoUnenOSYJIfVWr+yw7h7\n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\nFf+d/zHJS5PcN93V589Pcnpr7TELHIPVWcqaaq1dPcl/S/LBqnrtMsdiUPpcTxvso8ZtYWuqtfbv\nknw53Zp5RJK7V9U7lzEWg9bnmtpgPzVui1pTz03yxl2uTWQ/hSNnDugq6crmJTu8vnE70L3cLWcv\nY2WXsY5a0FisTp9rKkk+XlVbLxhcrbVXJnlaa+2MqjpvQWOxGgtfU7O/RXxdku9KcpstY2WXseyn\n1luf6ymxj5qCRa6pf0x3uP8N0t1d502ttZ/cdOtj+6hp6HNNJfZTUzD3mmqt/Xi6o65uvIexsstY\n9lMj58iZg/lSuv85rrzD68fMHi9a0FjZZaxDCxqL1elzTaWqDu3w0qNmj/dfxDis1ELX1Oz2jn+U\n5BZJHlhV790yVnYZy35qvfW5nuyjpmFha6qqLqmqd1fVq6rqDkleneQVmy7eah81DX2uKfupaZhr\nTc3+rPvdJM+pqo/vYazsMpb91MiJMwfzhdnj1XZ4faNofn7NxmJ1BvHfuar+X7rzpfdyG26GbdFr\n6g+S3DnJL1bVa5Y8FsPT53rakX3UqCxzv/GMdNeKuEMPYzEcfa6pHdlPjcq8a+pBSY5L8vLW2rU3\nvjZ93sZzRy1gLEZAnDmYz6Q75OzEHV7/gXRlcxGHMn4kXbE90lhfdmGotdfnmtrN15Jc2sM4LNfC\n1lRr7QXpzqc/tapets0m9lPj1+d62o191Dgs88+9T8webzB7tI+ahj7X1G7sp8Zh3jV1fLprxHxk\n9lkbX8+bvf4PST49285+CnHmIKrq0iTvys5Xbb9Hkg9V1fkLGOszSf7pCGPdPcl2FyhjjfS5ppKk\ntXZMa+34bZ6/apIfTHLOIsZhdRa1plprT0ny8CSPraoX7jCW/dTI9bmeZtvZR43cvGuqtXZsa+0B\nrbXt/pb5+2eP58/Gso+agD7X1Gx7+6mRW8Cffa9Icrckd93y9dzZ6yfNXj/ffopEnJnHi5LcYXaR\np29qrd04yc8k+b1Nz111duHDeca6f2vtcodHttbukO7wyt/b9l2sm17W1Ox86b9P8qrW2tFbXv7N\ndNX+VQf5bAZnrjXVWvulJE9I8tSqevYexrKfGrde1pN91KTMs6Zuku70uEdv87mPTXf9hs0Xb7WP\nmoZe1pT91KQceE1V1XlV9eatX/lWuHvb7LmvbxrLfmrCjjp0aKdrWbGb1toZSe6Y5FlJ3pfk+9Lt\nvM9LcvuqurS1dky6QyE/V1U7XqW7tfaXSb5YVSdt89q3JXlHukMpn5Guqt40yWOSvKmq7rfI3xer\n0+Oa+sUkL0zy7iQvSPIvSf5Tupr/oKp6xUJ/Y6zMQddUa+0mSc5Kt+958g4ff2FVnT3b3n5qAnpc\nT/ZREzHPn3utteenu83xS5O8Mcm/SnJykrskeXBVnbZpW/uoiehxTdlPTcQivz+ffd7JSV6W5OpV\ndfGm5+2nJs6RM/O5X5LfSnexpz9K8itJTk9y19lhcEnyjXTnEu52he6kO2fxMFX1jXQXTjx9NsYf\nzcZ8TpKfnWP+DE9fa+pFSX4yyWVJXpzub4qumOTHfTMxOgddU1efPf77dH9TuN3XxjnT9lPT0dd6\nso+ajgP/uVdVp6S7zfFNkrw83Vq5YpI7bf4heratfdR09LWm7KemY9Hfn2/LfgpHzgAAAACskCNn\nAAAAAFZInAEAAABYIXEGAAAAYIXEGQAAAIAVEmcAAAAAVkicAQAAAFghcQYAAABghcQZAAAAgBUS\nZwAAAABWSJwBAAAAWCFxBgAAAGCFxBkAAACAFRJnAAAAAFZInAEAAABYIXEGAAAAYIW+bdUTAADW\nR2vt6CRfT/KSqnroiubwySTnVtWdVzE+AMCiiTMAsIZaa0cl+XiS70xy7ar65z2856eSnJHkOVX1\n6CVPcW6ttTcnuWGSW1TVFze9dGibbX8wyduSnF5V/6WnKQIALITTmgBgDVXVoSSnJ/n2JPfZ49tO\nThc2TlvWvBbs+kmuleTKe9j2akmunuS7lzojAIAlEGcAYH29MslRSR6w24atteOS3DXJ+6rqA8ue\n2F601o5qrT2ptfaTO2xykyTfXVWf3u2zqupdSY7PNqGqtXbv1toT5pstAMDyiDMAsKaq6pwk5yS5\nXWvtOrts/rPpTmf+g6VPbO+ukORJSbaNM1X1L1V10V4/rKouqKrLtnnppCTiDAAwWOIMAKy3VyY5\nOl18OZKT013I9/SlzwgAgH0RZwBgvZ2e5LIcIc601n4oyb9L8qaqOr+vie3BUSMbBwDgQNytCQDW\nWFV9qrX29iS3b6390OxUp61+Pt2FgA87pam1dsMkj05y5yTXSfLlJO9N8tKq+p/7mcvsjkm/lOR2\nSa6b7oie96e7O9RrNm334CQv3vTWh7TWHjL75w9X1ffPtntlkvtW1RX3MPblbvG96debt9k45elQ\nkh9L8rtJfjjJv6mqD23zmbdK8r+TVFXdb7c5AAAclCNnAGD97Xhh4FmkuH+Si5L82ZbXfjrJogtZ\nqAAABV9JREFUB9LFm/cleW6SVye5UZI/bK29rrX27XuZQGvt3knOStJmj89PF4NOSFKb4kvSxZ8n\nJ3nqll8/efa+DYeyzW2z9+iyTZ95zuzXT9r03MfzrUD0wB0+4wGz8V9ywDkAAOyJI2cAYP29JskL\n00WYx2x57a5Jrp3kf1TV1zaebK3dIl3U+XCSe1bV/9302tFJnp7k15K8KN31anbzl0mekeTZVfWl\nTZ/1qCRnJ3l6a+1lVXVZVZ2V5KzZOE9M8t6qeuq2n3pAs1uNP3U2h+9LcuOqetrmbVprpyd5TrpT\nwh635bWjk/x0kk9U1VsWOTcAgK0cOQMAa24WQ/48yXVaaz++5eWTs/0pTc+aPf7HzWFm9nmXVtVj\nkrw+yQNaazfZwxwurKonbg4zs+f/eTb2NZL8wF5/T32oqouTVJITWmu33/LyXZIcl+T3+54XADA9\njpwBgHF4ZbojPR6Q5G1J0lq7WrrbVJ9XVf97Y8PW2jWT/Pskf15VHz7CZz43yT1mn/v+vUxidsTJ\nLZKcmORqSY5NcrPZy9+xj99PX16c7rSun0ty5qbnfzbdqVAv731GAMDkOHIGAMbhjUk+n+Sk1tqV\nZs/dL8mVcvhRMzdKd42a9+3ymX83ezxxLxNorf1ykv+X5N1JXprkt9KdWnTP2SaDu2tSVf1VknOT\n/NTG9XVaa8ck+Q9J3lZVH1vl/ACAaRBnAGAEquob6U7RuUqSe82e3jil6RXLHr+19uR0MebsJHdL\nckJVHV1VRyd56LLHn9NLkvzrJPee/freSY5J8rKVzQgAmBSnNQHAeLwyycPTXSfmvUlumeQdVfXR\nLdt9OF20uekun3fz2eM/HGmj1toV0l08+Owkd5pdjHez43ef+kr9QbqLGT8wyR+mO6XpwiR/vMpJ\nAQDT4cgZABiJ2Sk65yW5c7pYst2FgFNVFyR5R5K7tdZudISP/OXZZ7xml6GPT3LlJB/YJswkydaL\nFG/Y2HbZf1l0KEf4nqeqvpDkT5LcsbX2Q0numOT0zXe3AgBYJnEGAMblVUmumOQhSb6a5Iwdttu4\n5fbrtt6NqbV2pdbaC9NdK+aVVXX2LmN+fjbWHVpr19ryWQ9LcpvZLy/3fUdVXZbkkiz/Lk5fSnJU\na+37j7DNi9NFolcnOTpOaQIAeuS0JgAYl1cmeUK6o0X+bOutrTdU1d+01h6Y7lbR/6e19rok/5ju\njkp3S3KDJG9I8rDdBqyqr7bWnpbkvyY5p7X2R+lOC7rF7OvXkzwn3XVctnpjknu31irJR5Ncv6ru\nu+ff7d68Md3pXn/SWvuzdBdEflZV/e2m38NfttY+kuTGSc6uqrMWPAcAgB05cgYARqSq/inJe9LF\nmdN22baS/GC6o0R+OMmvpLvD00eS/ExV3bOqLtnmrYfyrVOSNj7rGemO1vl0uttSPyTJRenizJmz\n7Y/b5rMemS6e3D3Jg5JcvM1Yu45/pOer6nVJHp/kO5OckuSGSb64zftfNXv/S7Z5DQBgaY46dGi7\n720AAKaltfbaJHdJcp2q2i7eAAAshSNnAIDJa61dM93pXH8izAAAfRNnAACSh6a7Fp9TmgCA3jmt\nCQCYpNbatdNduPg2SV6b5ENVdYvVzgoAmCJHzgAAU/W8dLfyfku6W4GfvNrpAABT5VbaAMBUnZ7k\n3CSfTHfb8S+seD4AwEQ5rQkAAABghZzWBAAAALBC4gwAAADACokzAAAAACskzgAAAACskDgDAAAA\nsELiDAAAAMAKiTMAAAAAKyTOAAAAAKyQOAMAAACwQuIMAAAAwAqJMwAAAAArJM4AAAAArJA4AwAA\nALBC4gwAAADACv1/Z26P1/NPQ6AAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "metadata": { "image/png": { "height": 410, "width": 563 } }, "output_type": "display_data" } ], "source": [ "plt.figure()\n", "plt.contourf(sigma_vals, strike_vals, prices['acall'])\n", "plt.axis('tight')\n", "plt.colorbar()\n", "plt.title(\"Asian Call\")\n", "plt.xlabel(\"Volatility\")\n", "plt.ylabel(\"Strike Price\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot the value of the European put in (volatility, strike) space." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\nrSS/kuTly+LOkjck+eLk/Rdi7IHnJkk+P+W5z6X553Ojye/vmuTdU7Z9Z5pid9uZrg6go1yqBSwn\n8kD3iDywvsm9d45K8zP9at6Z5JallEOnPH/7JAekuTfv1dRa96RpCEdvf6UbM/bAc1GSG0957vDJ\n129Pvt4syZlTtv33JDsm2wCMgsgDLCfyQPeIPLCuw5Lsl63/rH/zyde1Xr+wTjD2wPOeJA+Y3BTp\n+0opO5I8Ksl5tdbzJ5dp7ZNk1YtZa63fSXJFkuvNeb0AAJ0l8kD3iDywputOvk67cdXS49N+1r9u\nkstrrf+9xuuvVUrZb4vr25Sx3zPm+CS/kOZTsJ6Y5PQkRyb5gyR3SPK0yXZL361ctsZ7XZbkoDWe\nBxgcN1wGVnLjZegeN16GqQ5IsifTf9ZfCjfTftY/IMl313j/5a+fFoFmZtQTPLXWs9PcW+fyJB9K\ncznWP6W5Bu+CJH822XTpu5Sda7zdziTfmstCATrMpVrASiZ5oHtM8sCqLklzCda0n/WXJm+m/ax/\nSZLrrPH+671+psY+wZNa6xlJ7lhKuUmaGyXvn+TDSZ60NGZVa/12KeWKyfN7KaXsn+YSrm9sZQ1n\n/NNHt/IyAIDOOnD9TYAFO3DabWJhG3r+8+xFk6/TCujS5M60n/UvSrJvKWW/KZdpHZTke2tcwjVT\no57gWa7W+uVa62eS/FGS02utb1yxydlJbjHl5buXbQMAAAB031fSXJ611s/6ezL9Z/2zJl/Xev3C\nOsHoJ3iWK6U8MslPTH6t9KEk907ypFWeu0+amyd9ajP7u/vd775jk0sEAACA1g3h59la6xWllI+m\n+Zn+patscp8kZ9ZaL5jyFp9M8p3Jdqev8vy90ny400KY4JkopVwvzc2VT6q1/ssqm5yY5IdKKQ9f\n8bobJHlckjfUWv93/isFAAAAZuTVSY4ppdxt+YOllFsmeXCSVy177MBSyvfv11Nr/Z8kJyV5Qinl\n+ite//A0H+J04hzXfjU79uzZs6h9dVop5Q1pPlHrFrXWr0/Z5oQkj09T9j6a5LA0n7S1J8mdaq3f\nXtByAQAAgBkopZyW5KeTvCjNlTk/nOQZaS6vOnoy6bNfki8n+Xqt9ZbLXntwkn9Oc7PmFyU5P80H\nNz05yctqrc9Y1J/DBE+SUspRSR6W5LnT4k6S1FqfmuQpSX4uSU3y3DQ3ZL6LuAMAAAC99KAkL07y\niCRvSxNnTk1yr1rrFZNtLk8Tb760/IW11ouT3CVNG3huktOS3C/JkxcZdxITPAAAAAC9Z4IHAAAA\noOcEHgAAAICeE3gAAAAAek7gAQAAAOg5gQcAAACg5wQeAAAAgJ4TeAAAAAB6bt+2F9BnpZR9kjw9\nya8mOTzJ15KcluS5tdZLN/E+RyX56yQfrrUeO2Wb/ZM8J8kDktwgyXlJ3pDkD2utV2z9T0GXLOqY\nKqUckeScKS/fk+R2tdZPb271dNF2j6lSyvXTnHvumeTGSb6UpCZ5Qa31v1ds6zw1cIs6npyjxmMG\nx9T9kjwlyW2TfC/J6Un+oNb6gVW2dY4agUUdU85T4zGr788n73WTJGcmuU6SQ2qt317xvPMU22KC\nZ3tOTfN/9tcnOTbJi5M8LMm7Sikb+mdbSilJ/i7JQWtss2+S9yR56GQfx072+YwkJ29j/XTPQo6p\nZX43ydErfh2T5AubWjVdtuVjqpRyaJJ/SvILSf4kyf2TnJTk8UneN/mGZ2lb56lxWMjxtIxz1PBt\n55j6jSRvT/ND9sOSPCrJF5P8XSnl2BXbOkeNx0KOqWWcp4Zv29+fL/PHSS5e7QnnKWbBBM8WTU7y\nD0hyj1rr+5c9/v4kn0zyuCSvWOc9fivJHyR5bZLda2z6xCR3SHL7WuvnJ4/9n1LKx9N8U3xqrfUd\nW/7D0AkLPqaWfLbW+pEtL5pOm8Ex9Ttp/vbotrXW/5w89u5Syt8n+VCSh+SqbzicpwZuwcfTEueo\nAZvBMfXlJI+rtb5q2WN/XUrZL01E/MtljztHjcCCj6klzlMDNovvz5e95h5JfirJC9IEnJWcp9g2\nEzxb9+gkH1r+f/Qkmfyf8c1JHrPWi0sp10nyiCR/VGv99SRXrrOvNy37P/rSvj6Q5pviNfdFbyzy\nmGIctnVMJflckt9a9sP40us/kuS/kvz4in05Tw3bIo8nxmFbx1St9a9X/CC+5KNJDiulXHPFvpyj\nhm+RxxTjsN3/9iVJJsfOnyR5ZpJvrrEv5ym2ReDZgskY+VFJ3jllk3cmueVkHH1VtdbvJvmJWusz\n1tnXDZP8SJJ3rbGvn1p30XTaIo8pxmFGx9Rra61/OuXpayf538m+nKcGbpHHE+Mwi2NqDbdPM1Xh\nHDUiizymGIcZH1NPSfLdJK+Zsi/nKWbCJVpbc1iS/dLcIGs1/55kR5KbJblw2pvUWle9/nKFm6e5\nUdta+/qBUsoNaq1f28D70U2LPKaWe2Ep5Y1pbvT2b0leUmt9yybfg26ayTG1mlLKvZMcmuTDk4ec\np4ZvkcfTcs5RwzXTY6qUsjPNjbsfnuRBSe6z7GnnqHFY5DG1nPPUcM3kmCqlHJ7mXk33qbXuaW6X\nuRfnKWbCBM/WXHfyddoP00uPX69n+6I9bfx7/o8kr0vywDSfCnBBklNLKU+f4T5oz1yOqVLKIUn+\nNMnna61vn+e+6JRFHk9LnKOGbWbHVCnlNkkuTXPMPC7Jz9Za/34e+6LTFnlMLXGeGrZZHVMvSfLu\nde7V5DzFTJjg2ZoD0hTWy6Y8v/RRrxv5FKON7Cvr7GvHjPZFexZ5TCXJl2qtK2/CXEsppyR5Xinl\ntFrr2TPaF+2Y+TE1+dvMdyS5YZKfXLGvrLMv56l+W+TxlDhHjcEsj6n/SHPpwk3TfOrRe0opP7fs\nY62do8ZhkcdU4jw1Bts+pkopd0sz/XXLDewr6+zLeYp1meDZmkvS/B9s55Tn95t8/daM9pV19rVn\nRvuiPYs8plJr3TPlqadOvj5kFvuhVTM9piYf3fm2JHdK8rBa6ydX7Cvr7Mt5qt8WeTw5R43DzI6p\nWutltdaP1VrfVGs9Jslbkpy87Ia4zlHjsMhjynlqHLZ1TE3+W/fyJCfUWr+0gX1lnX05T7EugWdr\nLpp8PXjK80tl9Rs92xft6cS/51rrV9NcP76Rj1in22Z9TP15knsk+fVa61vnvC+6Z5HH01TOUYMy\nz/PGC9PcO+OYBeyL7ljkMTWV89SgbPeYekSSXUneWEq5wdKvZe+39NiOGewLkgg8W/WVNONzt5jy\n/O40hXUWY5lnpSnHa+3rUjfb6r1FHlPr+V6SKxawH+ZrZsdUKeUVae4v8KRa6+tX2cR5avgWeTyt\nxzlqGOb5370vT77edPLVOWocFnlMrcd5ahi2e0wdluaeOWdN3mvp10snz/97kvMn2zlPMRMCzxbU\nWq9I8tFMv5v+fZKcWWu9YAb7+kqS/1xjXz+bZLWbvtEjizymkqSUsl8p5bBVHj8wyY8m+cws9kN7\nZnVMlVKem+SxSZ5Ra33llH05Tw3cIo+nyXbOUQO33WOqlLJ/KeWhpZTV/rb7RyZfL5jsyzlqBBZ5\nTE22d54auBn8t+/kJPdOcq8Vv14yef7YyfMXOE8xKwLP1r06yTGTG2d9XynllkkenORVyx47cHIz\nye3s6yGllKuNepZSjkkzKvqqVV9F3yzkmJpcP/5vSd5UStlnxdN/mOZvD960lfemc7Z1TJVSnpDk\nmUmOr7X+0Qb25Tw1bAs5npyjRmU7x9St01zq97RV3vcZae5nsfyGuM5R47CQY8p5alS2fEzVWs+u\ntb535a9cFf8+MHnsf5fty3mKbdmxZ8+0+4OxnlLKaUl+OsmLknwqyQ+n+Q/A2UmOrrVeUUrZL81Y\n59drrVPvnl5K+WCSb9Zaj13luX2TfCTNWOgL09Td2yZ5epL31FofNMs/F+1Z4DH160lemeRjSV6R\n5H+S/Fqav1V4RK315Jn+wWjNVo+pUsqtk5ye5tzznClvf3Gt9YzJ9s5TI7DA48k5aiS289+9Usqf\npPkI69cleXeSayU5Lsk9kzyy1nrSsm2do0ZigceU89RIzPL788n7HZfk9UkOqbV+e9njzlNsmwme\n7XlQkhenuYHW25I8OcmpSe41GelLksvTXFu53p3Tk+Yazr3UWi9PczPKUyf7eNtknyck+eVtrJ/u\nWdQx9eokP5fkyiQnpvkbq2smuZtvSAZnq8fUIZOvd0nzN5ar/Vq6htx5ajwWdTw5R43Hlv+7V2t9\nYpqPsL51kjemOVaumeRnlv8gPtnWOWo8FnVMOU+Nx6y/P1+V8xSzYIIHAAAAoOdM8AAAAAD0nMAD\nAAAA0HMCDwAAAEDPCTwAAAAAPSfwAAAAAPScwAMAAADQcwIPAAAAQM8JPAAAAAA9J/AAAAAA9JzA\nAwAAANBzAg8AAABAzwk8AAAAAD0n8AAAAAD0nMADAAAA0HMCDwAAAEDP7dv2AgCA/iil7JPkf5O8\nttb66JbWcF6Sz9Va79HG/gEAukjgAYAeKqXsSPKlJNdNcoNa63c28JpfTHJakhNqrU+b8xK3rZTy\n3iRHJrlTrfWby57as8q2P5rkA0lOrbX+/wtaIgBAZ7hECwB6qNa6J8mpSa6T5AEbfNlxaeLISfNa\n14wdkeT6SXZuYNuDkxyS5AfnuiIAgI4SeACgv05JsiPJQ9fbsJSyK8m9knyq1vrZeS9sI0opO0op\nzy6l/NyUTW6d5Adrreev91611o8mOSyrxK5Syv1LKc/c3moBALpN4AGAnqq1fibJZ5LctZRyo3U2\n/+U0l2b/+dwXtnHXSPLsJKsGnlrr/9Rav7XRN6u1XlhrvXKVp45NIvAAAIMm8ABAv52SZJ80AWct\nx6W5OfKpc18RAAALJ/AAQL+dmuTKrBF4Sik/luQ2Sd5Ta71gUQvbgB0D2w8AQGt8ihYA9Fit9b9K\nKR9OcnQp5ccml22t9Ktpbq681+VZpZQjkzwtyT2S3CjJpUk+meR1tda/2MxaJp9k9YQkd01y4zST\nRZ9O86ldb1223SOTnLjspY8qpTxq8r+/UGv9kcl2pyR5YK31mhvY99U+vn3Z75dvs3T51p4kP5Xk\n5Ulul+T/qbWeucp73jnJPySptdYHrbcGAIA2meABgP6berPlSeh4SJJvJfmbFc/9UpLPpglAn0ry\nkiRvSXLzJG8upbyjlHKdjSyglHL/JKcnKZOvf5ImKN0kSV0WcJImID0nyfErfv+cyeuW7MkqH4m+\nQVcue8/PTH7/7GWPfSlXRaaHTXmPh072/9otrgEAYGFM8ABA/701ySvThJynr3juXklukOQ1tdbv\nLT1YSrlTmjD0hST3rbWes+y5fZK8IMlvJXl1mvv3rOeDSV6Y5I9qrZcse6+nJjkjyQtKKa+vtV5Z\naz09yemT/TwrySdrrcev+q5bNPkY+eMna/jhJLestT5v+TallFOTnJDm8rbfXfHcPkl+KcmXa63v\nm+XaAADmwQQPAPTcJKj8bZIblVLutuLp47L65Vkvmnz9+eVxZ/J+V9Ran57knUkeWkq59QbWcHGt\n9VnL487k8e9M9n29JLs3+mdahFrrt5PUJDcppRy94ul7JtmV5A2LXhcAwFaY4AGAYTglzcTJQ5N8\nIElKKQen+Qjys2ut/7C0YSnl0CR3SfK3tdYvrPGeL0lyn8n7fnoji5hMvtwpyS2SHJxk/yS3nzx9\n0Cb+PItyYppL1H4lyYeWPf7LaS7reuPCVwQAsAUmeABgGN6d5BtJji2lXHvy2IOSXDt7T+/cPM09\nez61znt+YvL1FhtZQCnlN5N8NcnHkrwuyYvTXCZ138kmnfs0q1rrPyb5XJJfXLrfUCllvyT3S/KB\nWuu5ba4PAGCjBB4AGIBa6+VpLjc6IMkvTB5eujzr5Hnvv5TynDRB54wk905yk1rrPrXWfZI8et77\n36bXJvmBJPef/P7+SfZL8vrWVgQAsEku0QKA4TglyWPT3Dfnk0l+PMlHaq1fXLHdF9KEn9uu8353\nmHz997U2KqVcI80Nmc9I8jOTGxwvd9j6S2/Vn6e5QfTDkrw5zeVZFyf5yzYXBQCwGSZ4AGAgJpcb\nnZ3kHmmCy2o3V06t9cIkH0ly71LKzdd4y9+cvMdb19n1YUl2JvnsKnEnSVbe+HnJ0rbz/gunPVnj\ne55a60VJ/irJT5dSfizJTyc5dfmnjgEAdJ3AAwDD8qYk10zyqCTfTXLalO2WPk79HSs/JauUcu1S\nyivT3DvnlFrrGevs8xuTfR1TSrn+ivd6TJKfnPz2at931FqvTHJZ5v/pWpck2VFK+ZE1tjkxTWh6\nS5J94vIsAKBnXKIFAMNySpJnppla+ZuVH1u+pNb68VLKw9J8DPg/l1LekeQ/0nzS1b2T3DTJu5I8\nZr0d1lq/W0p5XpLnJ/lMKeVtaS5xutPk128nOSHNfW1WeneS+5dSapIvJjmi1vrADf9pN+bdaS5d\n+6tSyt+kucn0i2qt/7rsz/DBUspZSW6Z5Ixa6+kzXgMAwFyZ4AGAAam1/meSf0kTeE5aZ9ua5EfT\nTKvcLsmT03zy1llJHlxrvW+t9bJVXronV11etfReL0wzNXR+mo8cf1SSb6UJPB+abL9rlfd6fJoA\n87NJHpHk26vsa939r/V4rfUdSX4vyXWTPDHJkUm+ucrr3zR5/WtXeQ4AoNN27Nmz2vdHAADjUkp5\ne5J7JrlRrXW1AAQA0FkmeACA0SulHJrm0rS/EncAgD4SeAAAkkenuTehy7MAgF5yiRYAMEqllBuk\nuRn0TyZ5e5Iza613andVAABbY4IHABirl6b5mPb3pfmY9+PaXQ4AwNb5mHQAYKxOTfK5JOel+Uj5\ni1peDwDAlrlECwAAAKDnXKIFAAAA0HMCDwAAAEDPCTwAAAAAPSfwAAAAAPScwAMAAADQcwIPAAAA\nQM8JPAAAAAA9J/AAAAAA9JzAAwAAANBzAg8AAABAzwk8AAAAAD0n8AAAAAD0nMADAAAA0HMCDwAA\nAEDP/V96nEbDO4df8wAAAABJRU5ErkJggg==\n", "text/plain": [ "" ] }, "metadata": { "image/png": { "height": 410, "width": 572 } }, "output_type": "display_data" } ], "source": [ "plt.figure()\n", "plt.contourf(sigma_vals, strike_vals, prices['eput'])\n", "plt.axis('tight')\n", "plt.colorbar()\n", "plt.title(\"European Put\")\n", "plt.xlabel(\"Volatility\")\n", "plt.ylabel(\"Strike Price\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot the value of the Asian put in (volatility, strike) space." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\nSc6oqgu27Ct77Mt1atz6PJ9co+ZhaedUVX2mqt5YVS+tqjsn+a0kL9l081bXqHno85xynZqHI51T\ni7/rfinJM6vqffvYV/bYl+vUxIkzh/PRxedr7/D8RtH855Hti+Gsxf/PVfXBdOul9/M23Ky3ZZ9T\nv5Hkrkl+uKpetuJ9sX76PJ925Bo1Kau8bjw13b0i7tzDvlgffZ5TO3KdmpSjnlMPSnL9JC9urZ28\n8bHp9TYeO7aEfTEB4szh/GO6kbNb7PD8LdOVzWWMMv5DumK7274+5cZQo9fnObWXzyW5rIf9sFpL\nO6daa89Lt57+UVX1om02cZ2avj7Pp724Rk3DKv/ee//i81csPrtGzUOf59ReXKem4ajn1A3T3SPm\nHxavtfHx7MXzf5fkA4vtXKcQZw6jqi5L8obsfNf2eyV5R1V9eAn7+sckf7/Lvu6ZZLsblDEifZ5T\nSdJaO7G1dsNtHj8pydckecsy9sNwlnVOtdaelORHkjyuqp6/w75cpyauz/NpsZ1r1MQd9ZxqrV2z\ntfaA1tp2/8p888XnDy/25Ro1A32eU4vtXacmbgl/970kyT2S3H3Lx7MWz5++eP7DrlMk4sxRvCDJ\nnRc3efo3rbVbJfmeJL+66bGTFjc+PMq+vre1doXxyNbandONV/7qtl/F2PRyTi3WS/9tkpe21k7Y\n8vTPp6v2Lz3Ma7N2jnROtdZ+NMnjkzy5qp6xj325Tk1bL+eTa9SsHOWcunW65XGP2eZ1H5fu/g2b\nb97qGjUPvZxTrlOzcuhzqqourqo/2vqRy8PdaxePfX7TvlynZuzY8eM73cuKvbTWzk3y7UmenuRN\nSb463cX74iR3qqrLWmsnphuF/FBV7XiX7tbanyb5WFWdvs1zV0ny+nSjlE9NV1Vvk+SxSV5dVfdf\n5u+L4fR4Tv1wkucneWOS5yX51yQ/lK7mP6iqXrLU3xiDOew51Vq7dZIL0117nrjDy3+8qi5abO86\nNQM9nk+uUTNxlL/3WmvPTfc2x7+W5FVJ/l2SBya5W5IHV9VZm7Z1jZqJHs8p16mZWOb354vXe2CS\nFyW5TlVduulx16mZMzlzNPdP8gvpbvb0O0n+R5Jzktx9MQaXJF9It5Zwrzt0J92axSupqi+ku3Hi\nOYt9/M5in89M8n1HOH7WT1/n1AuSfGeSLyY5M92/FF01yV18MzE5hz2nrrP4/C3p/qVwu4+NNdOu\n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\n", "text/plain": [ "" ] }, "metadata": { "image/png": { "height": 410, "width": 563 } }, "output_type": "display_data" } ], "source": [ "plt.figure()\n", "plt.contourf(sigma_vals, strike_vals, prices['aput'])\n", "plt.axis('tight')\n", "plt.colorbar()\n", "plt.title(\"Asian Put\")\n", "plt.xlabel(\"Volatility\")\n", "plt.ylabel(\"Strike Price\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } ipyparallel-7.1.0/docs/source/examples/Parallel Decorator and map.ipynb000066400000000000000000000051651412533144600261000ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Load balanced map and parallel function decorator" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from __future__ import print_function\n", "import ipyparallel as ipp" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "rc = ipp.Client()\n", "v = rc.load_balanced_view()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Simple, default map: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]\n" ] } ], "source": [ "result = v.map(lambda x: 2*x, range(10))\n", "print(\"Simple, default map: \", list(result))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Submitted tasks, got ids: ['b21595ef-61f4-4ec3-ac7a-7888c025e492', '5e29335b-526d-4516-b22f-0a4b358fa242', '18cd0bd2-7aad-4340-8c81-9b2e384d73d9', '1ef7ccbc-6790-4479-aa90-c4acb5fc8cc4', '8d2c6d43-6e59-4dcf-9511-70707871aeb1', '58042f85-a7c1-492e-a698-d2655c095424', 'd629bf13-4d8b-4a54-996e-d531306293ea', '79039685-1b02-4aa5-a259-4eb9b8d8a65a', '16ffe6f3-fe82-4610-9ec9-a0a3138313a9', 'a3d9050b-faf2-4fa4-873a-65c81cab4c56']\n", "Using a mapper: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]\n" ] } ], "source": [ "ar = v.map_async(lambda x: 2*x, range(10))\n", "print(\"Submitted tasks, got ids: \", ar.msg_ids)\n", "result = ar.get()\n", "print(\"Using a mapper: \", result)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using a parallel function: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]\n" ] } ], "source": [ "@v.parallel(block=True)\n", "def f(x): return 2*x\n", "\n", "result = f.map(range(10))\n", "print(\"Using a parallel function: \", result)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 } ipyparallel-7.1.0/docs/source/examples/Parallel Magics.ipynb000066400000000000000000000252141412533144600240750ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Using Parallel Magics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "IPython has a few magics for working with your engines.\n", "\n", "This assumes you have started an IPython cluster, either with the notebook interface,\n", "or the `ipcluster/controller/engine` commands." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import ipyparallel as ipp\n", "\n", "rc = ipp.Cluster(n=4).start_and_connect_sync()\n", "dv = rc[:]\n", "rc.ids" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Creating a Client registers the parallel magics `%px`, `%%px`, `%pxresult`, `pxconfig`, and `%autopx`. \n", "These magics are initially associated with a DirectView always associated with all currently registered engines." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can execute code remotely with `%px`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%px a=5" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%px print(a)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%px a" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with dv.sync_imports():\n", " import sys" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%px from __future__ import print_function\n", "%px print(\"ERROR\", file=sys.stderr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You don't have to wait for results. The `%pxconfig` magic lets you change the default blocking/targets for the `%px` magics:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pxconfig --noblock" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%px import time\n", "%px time.sleep(1)\n", "%px time.time()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But you will notice that this didn't output the result of the last command.\n", "For this, we have `%pxresult`, which displays the output of the latest request:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pxresult" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, an IPython engine is IPython, so you can do magics remotely as well!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pxconfig --block\n", "%px %matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`%%px` can be used to lower the priority of the engines to improve system performance under heavy CPU load." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%px\n", "import psutil\n", "psutil.Process().nice(20 if psutil.POSIX else psutil.IDLE_PRIORITY_CLASS)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%px\n", "import numpy as np\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`%%px` can also be used as a cell magic, for submitting whole blocks.\n", "This one acceps `--block` and `--noblock` flags to specify\n", "the blocking behavior, though the default is unchanged.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dv.scatter('id', dv.targets, flatten=True)\n", "dv['stride'] = len(dv)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%px --noblock\n", "x = np.linspace(0,np.pi,1000)\n", "for n in range(id,12, stride):\n", " print(n)\n", " plt.plot(x,np.sin(n*x))\n", "plt.title(\"Plot %i\" % id)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pxresult" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It also lets you choose some amount of the grouping of the outputs with `--group-outputs`:\n", "\n", "The choices are:\n", "\n", "* `engine` - all of an engine's output is collected together\n", "* `type` - where stdout of each engine is grouped, etc. (the default)\n", "* `order` - same as `type`, but individual displaypub outputs are interleaved.\n", " That is, it will output the first plot from each engine, then the second from each,\n", " etc." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%px --group-outputs=engine\n", "x = np.linspace(0,np.pi,1000)\n", "for n in range(id+1,12, stride):\n", " print(n)\n", " plt.figure()\n", " plt.plot(x,np.sin(n*x))\n", " plt.title(\"Plot %i\" % n)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When you specify 'order', then individual display outputs (e.g. plots) will be interleaved.\n", "\n", "`%pxresult` takes the same output-ordering arguments as `%%px`, \n", "so you can view the previous result in a variety of different ways with a few sequential calls to `%pxresult`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pxresult --group-outputs=order" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Single-engine views" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When a DirectView has a single target, the output is a bit simpler (no prefixes on stdout/err, etc.):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "from __future__ import print_function\n", "\n", "def generate_output():\n", " \"\"\"function for testing output\n", " \n", " publishes two outputs of each type, and returns something\n", " \"\"\"\n", " \n", " import sys,os\n", " from IPython.display import display, HTML, Math\n", " \n", " print(\"stdout\")\n", " print(\"stderr\", file=sys.stderr)\n", " \n", " display(HTML(\"HTML\"))\n", " \n", " print(\"stdout2\")\n", " print(\"stderr2\", file=sys.stderr)\n", " \n", " display(Math(r\"\\alpha=\\beta\"))\n", " \n", " return os.getpid()\n", "\n", "dv['generate_output'] = generate_output" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also have more than one set of parallel magics registered at a time.\n", "\n", "The `View.activate()` method takes a suffix argument, which is added to `'px'`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "e0 = rc[-1]\n", "e0.block = True\n", "e0.activate('0')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%px0 generate_output()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%px generate_output()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As mentioned above, we can redisplay those same results with various grouping:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pxresult --group-outputs order" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pxresult --group-outputs engine" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parallel Exceptions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When you raise exceptions with the parallel exception,\n", "the CompositeError raised locally will display your remote traceback." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [], "source": [ "%%px\n", "from numpy.random import random\n", "A = random((100, 100, 'invalid shape'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Remote Cell Magics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, Engines are IPython too, so the cell that is run remotely by %%px can in turn use a cell magic." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%px\n", "%%timeit\n", "from numpy.random import random\n", "from numpy.linalg import norm\n", "A = random((100, 100))\n", "norm(A, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Local Execution" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can instruct `%%px` to also execute the cell locally.\n", "This is useful for interactive definitions,\n", "or if you want to load a data source everywhere,\n", "not just on the engines." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%px --local\n", "import os\n", "thispid = os.getpid()\n", "print(thispid)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/Using Dill.ipynb000066400000000000000000000664171412533144600231210ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Using dill to pickle anything" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ipyparallel doesn't do much in the way of serialization.\n", "It has custom zero-copy handling of numpy arrays,\n", "but other than that, it doesn't do anything other than the bare minimum to make basic interactively defined functions and classes sendable.\n", "\n", "There are a few projects that extend pickle to make just about anything sendable, and one of these is [dill](https://dill.readthedocs.io).\n", "Another is [cloudpickle](https://cloudpickle.readthedocs.io).\n", "\n", "To install dill:\n", " \n", " pip install dill" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, as always, we create a task function, this time with a closure" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "def make_closure(a):\n", " \"\"\"make a weird function with a closure on an open file, and return it\"\"\"\n", " import os\n", " f = open('/tmp/dilltest', 'a')\n", " def has_closure(b):\n", " product = a * b\n", " f.write(\"%i: %g\\n\" % (os.getpid(), product))\n", " f.flush()\n", " return product\n", " return has_closure" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "!rm -f /tmp/dilltest\n", "closed = make_closure(5)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "closed(2)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "33018: 10\n" ] } ], "source": [ "cat /tmp/dilltest" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "import pickle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Without help, pickle can't deal with closures" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "ename": "AttributeError", "evalue": "Can't pickle local object 'make_closure..has_closure'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/qr/3vxfnp1x2t1fw55dr288mphc0000gn/T/ipykernel_33018/935663835.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mpickle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mclosed\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m: Can't pickle local object 'make_closure..has_closure'" ] } ], "source": [ "pickle.dumps(closed)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But after we import dill, magic happens" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "import dill" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "b'\\x80\\x04\\x95\\xea\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x8c\\ndill._dill\\x94\\x8c\\x10_create_function\\x94\\x93\\x94(h\\x00\\x8c\\x0c_create_code\\x94\\x93...'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dill.dumps(closed)[:64] + b'...'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So from now on, pretty much everything is pickleable." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using dill in IPython Parallel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As usual, we start by creating our Client and View" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Starting 2 engines with \n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e05992ecd0ec4d2bab41ef48182f33bf", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/2 [00:00\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mview\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_sync\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mclosed\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m~/dev/ip/parallel/ipyparallel/client/view.py\u001b[0m in \u001b[0;36mapply_sync\u001b[0;34m(self, _View__ipp_f, *args, **kwargs)\u001b[0m\n\u001b[1;32m 233\u001b[0m \u001b[0mreturning\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 234\u001b[0m \"\"\"\n\u001b[0;32m--> 235\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_really_apply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m__ipp_f\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mblock\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 236\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0;31m# ----------------------------------------------------------------\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/conda/lib/python3.9/site-packages/decorator.py\u001b[0m in \u001b[0;36mfun\u001b[0;34m(*args, **kw)\u001b[0m\n\u001b[1;32m 230\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mkwsyntax\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 231\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msig\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 232\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mcaller\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mextras\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 233\u001b[0m \u001b[0mfun\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 234\u001b[0m \u001b[0mfun\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/dev/ip/parallel/ipyparallel/client/view.py\u001b[0m in \u001b[0;36msync_results\u001b[0;34m(f, self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 60\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_in_sync_results\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 62\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 63\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 64\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_in_sync_results\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/conda/lib/python3.9/site-packages/decorator.py\u001b[0m in \u001b[0;36mfun\u001b[0;34m(*args, **kw)\u001b[0m\n\u001b[1;32m 230\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mkwsyntax\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 231\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msig\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 232\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mcaller\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mextras\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 233\u001b[0m \u001b[0mfun\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 234\u001b[0m \u001b[0mfun\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__doc__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/dev/ip/parallel/ipyparallel/client/view.py\u001b[0m in \u001b[0;36msave_ids\u001b[0;34m(f, self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 44\u001b[0m \u001b[0mn_previous\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclient\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhistory\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 46\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 47\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 48\u001b[0m \u001b[0mnmsgs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclient\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhistory\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mn_previous\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/dev/ip/parallel/ipyparallel/client/view.py\u001b[0m in \u001b[0;36m_really_apply\u001b[0;34m(self, f, args, kwargs, block, track, after, follow, timeout, targets, retries)\u001b[0m\n\u001b[1;32m 1215\u001b[0m )\n\u001b[1;32m 1216\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1217\u001b[0;31m future = self.client.send_apply_request(\n\u001b[0m\u001b[1;32m 1218\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_socket\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtrack\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtrack\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmetadata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmetadata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1219\u001b[0m )\n", "\u001b[0;32m~/dev/ip/parallel/ipyparallel/client/client.py\u001b[0m in \u001b[0;36msend_apply_request\u001b[0;34m(self, socket, f, args, kwargs, metadata, track, ident, message_future_hook)\u001b[0m\n\u001b[1;32m 1771\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"metadata must be dict, not %s\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmetadata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1772\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1773\u001b[0;31m bufs = serialize.pack_apply_message(\n\u001b[0m\u001b[1;32m 1774\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1775\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/dev/ip/parallel/ipyparallel/serialize/serialize.py\u001b[0m in \u001b[0;36mpack_apply_message\u001b[0;34m(f, args, kwargs, buffer_threshold, item_threshold)\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 187\u001b[0m \u001b[0minfo\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnargs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnarg_bufs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg_bufs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkw_keys\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mkw_keys\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 188\u001b[0;31m \u001b[0mmsg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mserialize_object\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 189\u001b[0m \u001b[0mmsg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpickle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mPICKLE_PROTOCOL\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 190\u001b[0m \u001b[0mmsg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mextend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg_bufs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/dev/ip/parallel/ipyparallel/serialize/serialize.py\u001b[0m in \u001b[0;36mserialize_object\u001b[0;34m(obj, buffer_threshold, item_threshold)\u001b[0m\n\u001b[1;32m 117\u001b[0m \u001b[0mbuffers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mextend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_extract_buffers\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbuffer_threshold\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 118\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 119\u001b[0;31m \u001b[0mbuffers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minsert\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpickle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mPICKLE_PROTOCOL\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 120\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mbuffers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 121\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: cannot pickle '_io.TextIOWrapper' object" ] } ], "source": [ "view.apply_sync(closed, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oops, no dice. For IPython to work with dill,\n", "there are one or two more steps. IPython will do these for you if you call `DirectView.use_dill`:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rc[:].use_dill()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's try again" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "view.apply_sync(closed, 3)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "33018: 10\n", "33046: 15\n" ] } ], "source": [ "cat /tmp/dilltest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yay! Now we can use dill to allow ipyparallel to send anything.\n", "\n", "And that's it! We can send closures, open file handles, and other previously non-pickleables to our engines.\n", "\n", "Let's give it a try now:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "20" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "remote_closure = view.apply_sync(make_closure, 4)\n", "remote_closure(5)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "33018: 10\n", "33046: 15\n", "33018: 20\n" ] } ], "source": [ "cat /tmp/dilltest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But wait, there's more!\n", "\n", "At this point, we can send/recv all kinds of stuff" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def outer(a):\n", " def inner(b):\n", " def inner_again(c):\n", " return c * b * a\n", " return inner_again\n", " return inner" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So outer returns a function with a closure, which returns a function with a closure.\n", "\n", "Now, we can resolve the first closure on the engine, the second here, and the third on a different engine,\n", "after passing through a lambda we define here and call there, just for good measure." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "view.apply_sync(lambda f: f(3),view.apply_sync(outer, 1)(2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And for good measure, let's test that normal execution still works:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[5, 5]\n" ] }, { "data": { "text/plain": [ "[10, 10]" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%px foo = 5\n", "\n", "print(rc[:]['foo'])\n", "rc[:]['bar'] = lambda : 2 * foo\n", "rc[:].apply_sync(ipp.Reference('bar'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And test that the `@interactive` decorator works" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting testdill.py\n" ] } ], "source": [ "%%file testdill.py\n", "import ipyparallel as ipp\n", "\n", "@ipp.interactive\n", "class C:\n", " a = 5\n", "\n", "@ipp.interactive\n", "class D(C):\n", " b = 10\n", "\n", "@ipp.interactive\n", "def foo(a):\n", " return a * b\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "import testdill" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5 10\n" ] } ], "source": [ "v = rc[-1]\n", "v['D'] = testdill.D\n", "d = v.apply_sync(lambda : D())\n", "print(d.a, d.b)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "50" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v['b'] = 10\n", "v.apply_sync(testdill.foo, 5)" ] } ], "metadata": { "gist_id": "5241793", "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "482a88416c6f4f32b272165a7ff27da3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_64224d022bd94ac1806ed8f787780365", "style": "IPY_MODEL_fe9adbdc93c3486b94e22c87738e2b20", "value": "100%" } }, "4cdffdec17de419181264bbe14be7b02": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "64224d022bd94ac1806ed8f787780365": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "6e7318da3dd140288206e903e20e942f": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": { "bar_style": "success", "layout": "IPY_MODEL_ea1e89f575bd4bc9bd38fb49807fcaf0", "max": 2, "style": "IPY_MODEL_d05bdbbd4a9041ce8e1f92c25bc3c326", "value": 2 } }, "d05bdbbd4a9041ce8e1f92c25bc3c326": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": { "description_width": "" } }, "de9d64c64a4e4c39b0ad9b219949f1d5": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_e481dce1de5e46d0914b7c67836b8660", "style": "IPY_MODEL_f0ead7090f60468998a77b838c00f76c", "value": " 2/2 [00:03<00:00, 3.64s/engine]" } }, "e05992ecd0ec4d2bab41ef48182f33bf": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_482a88416c6f4f32b272165a7ff27da3", "IPY_MODEL_6e7318da3dd140288206e903e20e942f", "IPY_MODEL_de9d64c64a4e4c39b0ad9b219949f1d5" ], "layout": "IPY_MODEL_4cdffdec17de419181264bbe14be7b02" } }, "e481dce1de5e46d0914b7c67836b8660": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "ea1e89f575bd4bc9bd38fb49807fcaf0": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "f0ead7090f60468998a77b838c00f76c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "fe9adbdc93c3486b94e22c87738e2b20": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/Using MPI with IPython Parallel.ipynb000066400000000000000000000113161412533144600266720ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Simple usage of a set of MPI engines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example assumes you've started a cluster of N engines (4 in this example) as part\n", "of an MPI world. \n", "\n", "Our documentation describes [how to create an MPI profile](https://ipyparallel.readthedocs.io/en/stable/process.html#using-ipcluster-in-mpiexec-mpirun-mode)\n", "and explains [basic MPI usage of the IPython cluster](https://ipyparallel.readthedocs.io/en/stable/mpi.html).\n", "\n", "\n", "For the simplest possible way to start 4 engines that belong to the same MPI world, \n", "you can run this in a terminal:\n", "\n", "
\n",
    "ipcluster start --engines=MPI -n 4\n",
    "
\n", "\n", "or start an MPI cluster from the cluster tab if you have one configured.\n", "\n", "Once the cluster is running, we can connect to it and open a view into it:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "import ipyparallel as ipp\n", "rc = ipp.Client()\n", "view = rc[:]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's define a simple function that gets the MPI rank from each engine." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "@view.remote(block=True)\n", "def mpi_rank():\n", " from mpi4py import MPI\n", " comm = MPI.COMM_WORLD\n", " return comm.Get_rank()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 3, 2]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mpi_rank()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get a mapping of IPython IDs and MPI rank (these do not always match),\n", "you can use the get_dict method on AsyncResults." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{0: 0, 1: 1, 2: 3, 3: 2}" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mpi_rank.block = False\n", "ar = mpi_rank()\n", "ar.get_dict()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With %%px cell magic, the next cell will actually execute *entirely on each engine*:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[0;31mOut[0:8]: \u001b[0m{'data': 1, 'rank': 0}" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\u001b[0;31mOut[1:8]: \u001b[0m{'data': 4, 'rank': 1}" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\u001b[0;31mOut[2:8]: \u001b[0m{'data': 16, 'rank': 3}" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\u001b[0;31mOut[3:8]: \u001b[0m{'data': 9, 'rank': 2}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%%px\n", "from mpi4py import MPI\n", "\n", "comm = MPI.COMM_WORLD\n", "size = comm.Get_size()\n", "rank = comm.Get_rank()\n", "\n", "if rank == 0:\n", " data = [(i+1)**2 for i in range(size)]\n", "else:\n", " data = None\n", "data = comm.scatter(data, root=0)\n", "\n", "assert data == (rank+1)**2, 'data=%s, rank=%s' % (data, rank)\n", "{\n", " 'data': data,\n", " 'rank': rank,\n", "}" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/broadcast/000077500000000000000000000000001412533144600220505ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/examples/broadcast/Broadcast view.ipynb000066400000000000000000015332531412533144600257640ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "id": "b9dd40a2-26ae-48b7-8dc7-3b4995b2b6e6", "metadata": {}, "source": [ "# Broadcast View\n", "\n", "IPython Parallel is built on the idea of working with N remote IPython instances.\n", "This means that most of the 'parallelism' when working with N engines\n", "via the DirectView is really implemented in the client.\n", "\n", "DirectView methods are all implemented as:\n", "\n", "```python\n", "for engine in engines:\n", " do_something_on(engine)\n", "```\n", "\n", "which means that all operations are at least O(N) *at the client*.\n", "This can be really inefficient when the client is telling all the engines to do the same thing,\n", "*especially* when the client is across the Internet from the cluster.\n", "\n", "IPython 7 includes a new, experimental *broadcast* scheduler that implements the same message pattern,\n", "but sending a request to any number of engines is O(1) in the client.\n", "It is the *scheduler* that implements the fan-out to engines.\n", "\n", "With the broadcast view, a client always sends one message to the root node of the scheduler.\n", "The scheduler then sends one message to each of its two branches,\n", "and so on until it reaches the leaves of the tree,\n", "at which point each leaf sends $\\frac{N}{2^{depth}}$ messages to its assigned engines.\n", "\n", "The performance should be strictly better with a depth of 0 (one scheduler node),\n", "network-wise,\n", "though the Python implementation of the scheduler may have a cost relative to the pure-C default DirectView scheduler. \n", "\n", "\n", "This approach allows deplyments to exchange single-message latency (due to increased depth) for parallelism (due to concurrent sends at the leaves).\n", "\n", "This trade-off increases the baseline cost for small numbers of engines,\n", "while dramatically improving the total time to deliver messages to large numbers of engines,\n", "especially when they contain a lot of data.\n", "\n", "These tests were run with a depth of 3 (8 leaf nodes)" ] }, { "cell_type": "code", "execution_count": 1, "id": "0e46054d-5082-40d9-ace3-413cef646622", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/minrk/dev/ip/parallel/ipyparallel/util.py:205: RuntimeWarning: IPython could not determine IPs for ip-172-31-2-77: [Errno 8] nodename nor servname provided, or not known\n", " warnings.warn(\n" ] } ], "source": [ "import ipyparallel as ipp\n", "\n", "rc = ipp.Client(profile=\"mpi\")\n", "rc.wait_for_engines(100)\n", "direct_view = rc.direct_view()\n", "bcast_view = rc.broadcast_view()" ] }, { "cell_type": "code", "execution_count": 2, "id": "339a80ab-bbeb-423c-abb3-6d429377017d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I have 100 engines\n" ] } ], "source": [ "print(f\"I have {len(rc)} engines\")" ] }, { "cell_type": "markdown", "id": "ff036951-029a-486e-ada9-ec0ee5edd3bc", "metadata": {}, "source": [ "To test baseline latency, we can run an empty `apply` task with both views:" ] }, { "cell_type": "code", "execution_count": 3, "id": "9c30c899-68fd-470d-80ec-c10e724bd27a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "311 ms ± 67.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], "source": [ "%timeit direct_view.apply_sync(lambda: None)" ] }, { "cell_type": "code", "execution_count": 4, "id": "ee82f5fa-a2a1-4b72-ad1f-f36427168317", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "259 ms ± 14.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], "source": [ "%timeit bcast_view.apply_sync(lambda: None)" ] }, { "cell_type": "markdown", "id": "b4f994af-21b5-440e-b0b3-97e3abd73493", "metadata": {}, "source": [ "With 100 engines, it takes noticeably longer to complete on the direct view as the new broadcast view.\n", "\n", "Sending the same data to engines is probably the pattern that most suffers from this design,\n", "because identical data is duplicated *once per engine* in the client.\n", "\n", "We can compare `view.push` with increasing array sizes:" ] }, { "cell_type": "code", "execution_count": 5, "id": "e5f49dd7-e903-4126-888c-0130261e5fd7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 16 x 16: 2 kB\n", "Direct view\n", "287 ms ± 72 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", "Broadcast view\n", "266 ms ± 10.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", " 64 x 64: 32 kB\n", "Direct view\n", "598 ms ± 62.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", "Broadcast view\n", "274 ms ± 30.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", " 256 x 256: 512 kB\n", "Direct view\n", "5.99 s ± 199 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", "Broadcast view\n", "297 ms ± 43.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", " 512 x 512: 2 MB\n", "Direct view\n", "22.9 s ± 428 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", "Broadcast view\n", "559 ms ± 107 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], "source": [ "import numpy as np\n", "\n", "dview_times = []\n", "bcast_times = []\n", "\n", "n_list = [16, 64, 256, 512]\n", "for n in n_list:\n", " chunk = np.random.random((n, n))\n", " b = chunk.nbytes\n", " unit = 'B'\n", " if b > 1024:\n", " b //= 1024\n", " unit = 'kB'\n", " if b > 1024:\n", " b //= 1024\n", " unit = 'MB'\n", " print(f\"{n:4} x {n:4}: {b:4} {unit}\")\n", " print(\"Direct view\")\n", " tr = %timeit -o -n 1 direct_view.push({\"chunk\": chunk}, block=True)\n", " dview_times.append(tr.average)\n", " print(\"Broadcast view\")\n", " tr = %timeit -o -n 1 bcast_view.push({\"chunk\": chunk}, block=True)\n", " bcast_times.append(tr.average)" ] }, { "cell_type": "code", "execution_count": 7, "id": "8926cf3e-a247-413b-be61-7a7db73437e9", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ndirect viewbroadcastbroadcast speedupMB
0160.2874950.2657861.0816790.195312
1640.5980650.2738352.1840313.125000
22565.9862740.29703420.15352150.000000
351222.9468060.55865341.075245200.000000
\n", "
" ], "text/plain": [ " n direct view broadcast broadcast speedup MB\n", "0 16 0.287495 0.265786 1.081679 0.195312\n", "1 64 0.598065 0.273835 2.184031 3.125000\n", "2 256 5.986274 0.297034 20.153521 50.000000\n", "3 512 22.946806 0.558653 41.075245 200.000000" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "df = pd.DataFrame({\"n\": n_list, \"direct view\": dview_times, \"broadcast\": bcast_times})\n", "df[\"broadcast speedup\"] = df[\"direct view\"] / df.broadcast\n", "# MB is the total number of megabytes delivered (n * n * 8 * number of engines bytes)\n", "df[\"MB\"] = df[\"n\"] ** 2 * 8 * len(rc) / (1024 * 1024) \n", "df" ] }, { "cell_type": "code", "execution_count": 8, "id": "5a875b84-0d26-4e9d-94f6-23732095359e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'seconds')" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABMUAAAL7CAYAAAD9DjKGAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAABYlAAAWJQFJUiTwAAC36klEQVR4nOzdeXgV9d3+8c93cpac7GSDJKxRCCQBbUUCKiJBqIKACmERtFattS5FsE9b7dOquFSsGy7V4tZfBZUAAuICqChLqEVtCYSwBwhbCJAEcrKd7fv7g4xPRGTzhMnyfl1XrmQ+M3PmPopHvTOL0loLAAAAAAAA0JoYVgcAAAAAAAAAzjVKMQAAAAAAALQ6lGIAAAAAAABodSjFAAAAAAAA0OpQigEAAAAAAKDVoRQDAAAAAABAq0MpBgAAAAAAgFaHUgwAAAAAAACtDqUYAAAAAAAAWh1KMQAAAAAAALQ6lGIAAAAAAABodSjFAAAAAAAA0OrYrA7QWiildohIlIjstDgKAAAAAABAS9FZRI5qrbuc6Y6UYudOlM1mi0xMTEyyOgiA1snpdMaLiNTV1R2yOgsANDd8hgLA2eMzFI2ptLQ00ufzndW+lGLnzs7ExMSkTZs2zbA6CIDWKS8v73YRkUsvvZTPIQA4Q3yGAsDZ4zMUjal79+6379u3b//Z7Ms9xQAAAAAAANDqUIoBAAAAAACg1aEUAwAAAAAAQKtDKQYAAAAAAIBWh1IMAAAAAAAArQ6lGAAAAAAAAFodSjEAAAAAAAC0OjarA+D7/H6/raysLLOmpibV5/O1EZEQqzMBZ8Fvs9nKXS5XUWxsbEFISIjP6kAAAAAAAJgoxZoYv99v27dv39U+n6+TUsoVEhJitzoTcLYCgUCs2+1Orq2t7ZCcnPwxxRgAAAAAoKmgFGtiysrKMn0+XyebzRYRGxvrdrlcRw3D0FbnAs5UIBBQNTU19rKysgifz9eprKwsMyEhYa3VuQAAAAAAEOGeYk1OTU1NqlLKFRsb6w4PD/dQiKG5MgxDh4eHe2JjY91KKVdNTU2q1ZkAAAAAADBRijUxPp+vjVLK7nK5vFZnAYLB5XJ5lVL2+vvjAQAAAADQJFCKNT0hIsfOsrE6CBAMSinzzzIPjAAAAAAANBmUYgAalVLK6ggAAAAAAHwPpRgAAAAAAABaHUoxAAAAAAAAtDqUYmiWevTokdijR4/EhrM333zTFRUVlfTmm2+6rMpllYceeigyKioqadmyZQ6rswAAAAAA0BxQirVimw+4bW/+a3f4i8t3Rrz5r93hmw+4bVZnamluueWWmKioqKSioiJuMg8AAAAAQBNCCdIKrdpe5nhlZXFk/t6j3zmr6JllO+SClCjPHf07Vl52XqzHqnxn67rrrqvNyso6mJyc7Lc6y7l21113VY0ZM6amU6dOre69AwAAAABwNijFWpl3v9kX9pcl26ID+sTr8/ceddw1uyDugZ+dXzH2ouSac5vux4mJidExMTE+q3NYISEhIZCQkBCwOgcAAAAAAM0Fl0+2Iqu2lzlOVoiZAlrk8SXbYlZtL7P0/lSBQEBeeOGFsIsuuighPj4+qWvXrm3vueee6IqKCnWi7X/onmLm/ceOHDmi7rvvvqgePXokxsbGJj300EOR5jaFhYW2W265JaZbt25t4+Likrp06dL2xhtvjNm4ceMJL3usqqpS06ZNi7j00kvjk5KS2rVr167dT37yk4RJkyZFlZSUGCIiUVFRSXPnznWJiFx44YWJUVFRSVFRUUnH3wvteH/5y18ioqKikqZPnx5+ovV79uwxYmJiki699NJ4c3aye4qd7nu7//77I6OiopKWLFnibDj/85//HBkVFZWUmZn5vdypqalt09LSTvp+AAAAAABoijhTrBV5ZWVx5KkKMVNAi/x9VXHkZefFHm7cVD9sypQpUW+88UZ4YmJiYMKECVV2u12WLFkSOmzYsDiv16vsdvtpvhsRr9erhg4dGldRUWEMGDCgLjIyUnfu3NknIvLRRx85b7755jY+n08NGjSoNjU11b9v3z7j448/dn322Weh77///uHevXt7zdcqKytTV199dfzGjRttqampvrFjx1Y7HA7ZsWNHyOzZs8NGjhxZ265dO8+UKVPcH3/8cejGjRttt956a1V0dLQWEYmOjj7pGV0TJ06snjZtWuTs2bNdkyZNqjp+/axZs1yBQEDGjRtXfar3fSbvbeDAgXUvvfRSxBdffOH42c9+Vme+xqpVqxwiIsXFxSFFRUUhqampfhGRdevW2Q4dOmTk5OQ0qzMKAQAAAKA1W7mtLGHZlkOpVXV+Z7gzpC67W3xR//NjD1qdywqUYq3E5gNu2/H3EDuVtXuOOjYfcNvS2kac80sSV61aZX/jjTfCO3Xq5P/iiy8OxsXFaRGRRx555OhVV10VX1paaqSkpJz2/bNKS0uNrl27+pYsWXIwIiLi2zKtrKxM3X777W1cLpf+8MMPD2VmZn77XtevX+8ePHhw/D333BP9r3/965A5nzRpUvTGjRttN954Y/Xzzz9/JCTk/064Onr0qAoEjnVeDz30UGVxcXHIxo0bbffcc0+VWSadSocOHQKXXXZZ3YoVK5zr1q2z9erV6zt//XNzc8PsdruMHz/+pGXUmb63yy67zONwOGTFihVOEakUEamsrFT5+fmO/v37161cudK5bNkyZ2pqarWIyLJly5wiIgMGDKg7weEBAAAAAE3Iu1/v6/LPNXsG7C6v7dRwPve/JdKhTeium/q0Xz6ud/IOq/JZgVKsmen52Iqkc3m80a/9J+Fs913/x8v3n+2+b731VpiIyOTJkyvNQkxExOVyyYMPPnj02muvjTvT13z88cePNCzERET++c9/hh09elQ9/vjjRxuWRiIiPXv29N1www3Vr732WnhBQYEtMzPTd+DAAWPRokWuxMTEwLRp0442LMRERKKiok777LWTGTduXM2KFSucM2fODHvyySePmvM1a9bYt27davvZz35WGx8ff9Jjnel7CwsLk5/+9KeeNWvWOMrKylRsbKxevny5w+v1yq9//euqDRs22JcvX+647bbbqkXELM9k4MCBlGIAAAAA0IQ9+cn2n8xcs3e4Fjnh7Yh2l9d2enzJtht3l9cs+p/B5/33XOezCqUYmqT169fbRUQuv/zy7z0F8/LLL/fYbGf2R9fpdMoFF1zwvTPevvrqK4eISEFBgb3hPcZM27dvt4mIbNy40ZaZmelbs2aNPRAISFZWluf4gi2Yrr/++prf//730fPmzXM9/vjjR833O3PmTJeIyIQJE0556eSZvjcRkf79+9d9+eWXjs8//9w5atSo2uXLlzvtdrsMGDDAc8kll3jy8vKcIiI+n0/+/e9/O1JTU30dOnTgBv8AAAAA0ES9+/W+LicrxExaRL21Zu/wDm1cFa3ljDFKMTRJlZWVhohIu3btvle42Gw2iYmJOaMiJi4uzm8Y33+uRHl5uRIRefvtt8NOtn9VVZUSEamoqDBERJKSkk770s2zERYWJsOHD695++23w5YsWeIcNmxYncfjkYULF7piY2MDQ4cOPeXZWWf63kSOnfX117/+NfKLL75wjho1qnblypXOCy+80BMZGakHDBhQ98EHH4SuW7fOVlVVpSorK9V11133vdISAAAAANB0/HPNngGnKsRMx4qxPQMoxdAkne0liZsPuG1ncynk3Nt+etCKe4pFRkYGRCSkpKTEOP/8879TQPl8PqmoqDDatm172sWUUif+5z8yMlKLiKxYseLghRdeeMr3aZZx+/fvP+FTKYNpwoQJ1W+//XbY22+/HTZs2LC6RYsWhZaXlxu33nprlcNx6tvDnel7ExHJysryhoeH65UrVzoOHTqkNm7caJs8ebJb5P8uk/zss8+cNTU1SoT7iQEAAABAU7ZyW1nC8fcQO5Xi8tpOK7eVJbSGm+9//9QZtEhpbSN8F6REndFZPRe2j/JYUYiJiPTs2dMrIrJixYrvtT8rVqxw+HzBidW7d2+PyP89YfFUsrKyvIZhyL///W+H2+0+ZdMeEhKiRUT8/jM/sax///7ezp07+5cuXRpaUVGh3nnnHZfIsadTns7+Z/reRETsdrv06dPHU1RUZHvrrbfCtNbflmHdunXzJycn+1esWOFcuXKl0zAMyc7OphQDAAAAgCZq2ZZDqedyv+aGUqwVuaN/x0rjtE6YFDGUyK8u61jZuIl+mFn8PPvss5GHDx/+NnVNTY08/PDDUcE6zi9+8YvqqKgo/dRTT0V++eWX9uPX+/1+WbZs2belUmJiYmDEiBE1paWlxu9///uo48uuyspKVVFR8W3e2NhYLSJSXFx8VmeWjR07trqurk5efvnl8M8//zy0e/fuvosuuui0GsEzfW+m/v3714mIPP/88xFhYWG6b9++35apl156qefLL790fP31144ePXp4Gz4EAQAAAADQtFTV+Z3ncr/mhssnW5HLzov13P+z84/8Zcm26MBJqgxDiTzws/MrLjsv1rL7RfXv39/7i1/8ourNN98Mz8rKShw2bFiN3W6XJUuWhEZFRQUSExODcnP3+Ph4/eabb5bddNNNsT/72c/iL7300rq0tDSfYRiyZ8+ekP/85z+O8vJy49ChQ99etvrcc88d2bRpk/2tt94KW716teOKK66oczgcsmvXrpDly5c7Z82aVZadne0REbniiivqXn755fB777035pprrqkJDw/X0dHRgXvuuee0zvaaOHFi9V//+tfIp556KtLr9crYsWNPa7+zfW8iItnZ2XVT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y5c7r7jiirqbbrqpqri42CYiUl5erq688sr4rVu32nr16uX95S9/WXP48GFj0aJFrpycnLhp06YdueOOO6rN1ykrKzP+9Kc/RV900UWeyy+/vC4uLi5w4MCBkE8//TR0/PjxsU8//fSRX/7yl9UNj52bmxt6++23t7Hb7TJ8+PCatm3b+tesWeMYMmRIfPfu3b0nen8zZswI+/3vfx9tGIYMHjy4NjU11Xfo0CEjPz/f8frrr4ePGzfu2/f3u9/9LqZr167erKwsT9u2bf3l5eXGsmXLQu++++6YrVu32h555JFKc9u9e/ca2dnZCW63Ww0cOLB22LBhNbW1taq4uNg2b9481x133FEVHx/vu+GGG6qjo6MDS5cuDR0yZEhtZmamz3yNNm3anN4fGAAAAADAjxLQWv5n/sYrlm48NMCctQmzH34hJ2PmBe2jKiyM1mJRirUSRmmhzbxk8nRuxXfsUsqvHUZpoS2QmO475Q6NaM2aNY477rij6sknnzzaYFZ11VVXxf/P//xPzNChQw9ER0d/exbe559/7nznnXfKhg4dWtfwdR544IGorVu32iZMmFD90ksvHTGMYydKbt682T1w4MCEP/7xj9FDhgypS01N9YscK4TWrVt3oGPHjt8phioqKtSVV14ZP3Xq1KgJEyZUh4WFiYhIZWWl+u1vfxtjGIZ8+OGHh/r06fNtCTZlypSo1157Lfz491ZQUGD7wx/+EB0REaE/+uijQz179vzOX+vi4uLvnM25evXq0q5du/obzurq6o6MHDky7qWXXoq4/fbbqzp06BAQEZk3b56roqJCTZ069ei9995b1XAft9utDMPQIiK/+MUvakREli5dGjps2LBacxkAAAAAcG54/QF1xzvrh67ZdaS3OWsX5dz36g09Z3WOC6s+2b44e5RizUzk0+2TTr3VDzvdZ1OY24W/NSThbI9Ved+e/We7b0ORkZH6f//3fysbzvr06eO99tpra+bOneuaP39+6M033/xtkTNkyJDa4wsxj8cj7733nis8PFw/+uijR81CTEQkLS3Nf+utt1ZNnz49YubMma4///nPbhGR0NBQOb4QExGJiYnR48ePr3744Yej1qxZ47jiiis8IiILFiwIraioUKNGjappWIiJiPz5z3+unD17dlhlZeV3/hbMmDEjzOfzyeTJkyuPL8RE5HvHP74QExFxOp1y2223Va1evdqxbNky589//vPvlFoul+t7l+1GRETw2F4AAAAAaAIqa322W2bmX7/pQFUPc9YlzrX9zYkX5MZFOHgIXiPinmJo8jIzM71RUVHfK3H69+9fJyKSn59vbzj/6U9/+r3LFDdu3GirqalRPXr08MbFxX3vta644oo6EZH169d/57XWr19vu+WWW2LMe3iZ99p6+OGHo0RE9u7d++09t8wcl1122XcKOZFjRVp6evr3cn3zzbEngF511VXf2+dEdu7cGXL33XdHX3jhhQmJiYntzDy33HJLGxGRffv2fZtn+PDhteHh4fr++++PHjNmTJu///3vYevXr7cFAlwRCQAAAABNQcnROue4N/47oWEh1jM5cv27t/z0HQqxxseZYmjyEhISvnd2lIhI27ZtAyIiR48eNY6bf2/7I0eOGCIiiYmJJ2yEkpKSAg23ExFZvXq1/dprr43z+/3qkksuqRsyZEhtZGSkNgxDCgoKbEuXLg31eP7vM+ro0aPqZMc40dzM3r59+xO+x4a2bdsWMmjQoPgjR44YF198sWfAgAF1UVFROiQkRIqLi0Pmzp3rqqur+/ZMtC5duvg//fTTQ4899ljkF1984Vy8eHFo/Xv133nnnVWTJk2q+uGjAQAAAAAa05ZSd8Qd7xRMPOj2tDVnl53X5ssXxmQutRnWPqivtaAUa2bO9pJEo7TQFv7WkIQzuaeYEpGqG5cetPqeYgcPHjzhExAPHDhgiIhERUV9p2xS6vvvMDo6OlD/Wic8O3L//v3fe60nn3wysra2Vi1YsOBwdnb2dxr6v/zlLxFLly79zpMhzbPZSktLT3iME83rjxeyZ8+ekPT0k/91nj59ekR5ebkxffr0iuPv+zVr1izX3Llzv/cEyoyMDN/bb79d7vV6Ze3atfZly5Y5X3/99fA//elPUeHh4fq2227j2nQAAAAAOMfW7KyInTKv8MYjtb4Ycza8Z+Knjw5PyzNO8P+0aBxcPtlKBBLTff7kizxnck8xf3Jvj9WFmIhIQUGB3TwLq6GVK1c6RUQuuOCCEz7VsaHu3bv7XC6XLiwstJeVlX3vtZYvX+4UEenVq9e3r7Vz586QmJgYfXwhJiKSl5fnOH5m5li1apXz+HUVFRWqsLDQfvz8oosu8oiILF68+Hv7HG/Hjh0hIiKjRo2qPX7difI0ZLfb5eKLL/b+/ve/d7/66qvlIiIffvjht6VeSMix3tHv9/PpCwAAAACNaMnGg0l35xbcahZiSkT/PKv9wsdHdKcQO8coxVqRur73Vmp1en/LtTKkru+kylNv2fgqKyvVo48+GtlwtmbNGvuCBQtckZGR+rrrrvteSXQ8p9Mp1113XU1VVZV66KGHvvNaW7duDXn99dfD7Xa7TJgw4dszsNq3b++vqKhQa9eu/c4Zla+99lrYihUrvldiXXvttbXR0dF64cKFrjVr1nynAJs6dWrk8TfZFxG5/fbbq202mzz77LORBQUF3ztzs+HTJzt27OgXEfn888+/U4B9/PHHznfeeSfs+H2//PJLe0lJyff+hptn2DW8AX9sbGxARGT37t0nPCsPAAAAAPDjvfP13tT7F266ucYbCBMRsRnKNzm7y7u/vTJ1rcXRWiUun2xF/F0GeuqyHzniXPanaKUDcvyllOayVobUDXq0wt9lYJO4qV+fPn08b7/9dtg333xj79Onj+fAgQMhixYtcgUCAXnqqacqoqOjT+ta68cee+zov//9b8c//vGP8LVr1zouvfTSurKyMmPRokWuqqoq9fjjjx8577zzvr2315133lm1YsUK59VXXx0/fPjwmqioKL127Vr7119/7Rg6dGjtRx999J3LJyMjI/XTTz9dcfvtt7cZNmxY/PDhw2vatm3rX7NmjWPz5s32Pn36eNasWfOdQiszM9P3xBNPHPnd734XPWDAgIQhQ4bUpqam+srLy438/Hx7eHi4Xrp06WERkdtvv71q9uzZYbfeemvsvHnzapKSkgIbN260LV++3HnNNdfULlq06Dt5Zs+e7fp//+//hWdlZXm6dOnii46ODuzcudP26aefhjocDrnrrrvc5raXXHKJx+Vy6VdffTW8vLxcmfc/u/vuu6tiYmK4lh0AAAAAfqQXl+/MeDWv+PqAPnaCktNm1P756q5vj+jVdrfV2VorSrFWxnvhz6sD0R19zi+nR4bs+/o7BY15yWRd30mVTaUQEzl2htT06dOP/PnPf4586623wj0ej2RmZnp/97vfVV599dWn9dRGEZG4uDi9bNmyQ9OmTYv48MMPXTNmzIgIDQ3VF154oec3v/lN1fFPgBw6dGjdzJkzy5566qnIRYsWuQzDkAsvvNCzYMGCwzt27Ag5vhQTERkzZkxtdHR02bRp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\n", "text/plain": [ "
" ] }, "metadata": { "image/png": { "height": 381, "width": 610 }, "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "df.plot(x='MB', y=['direct view', 'broadcast'], style='-o', loglog=True)\n", "plt.ylabel(\"seconds\")\n" ] }, { "cell_type": "markdown", "id": "c7bee30d-84c8-4391-9d26-66517858e297", "metadata": {}, "source": [ "# Tuning broadcast views\n", "\n", "As mentioned above, `depth` is a tuning parameter to exchange latency for parallelism.\n", "\n", "This can be set with `IPController.broadcast_scheduler_depth` configuration (default: 1).\n", "The number of leaf nodes in the tree is `2^d` where `d` is the depth of the tree ($d=0$ means only a root node).\n", "\n", "With a DirectView, the total number of request messages is $ 2 N $ and the number of requests to deliver the first message is 2 (one client->scheduler, one scheduler->engine).\n", "\n", "With a Broadcast View, the total number of request messages is\n", "\n", "$$\n", "N + 1 + \\sum_{i=1}^{d} 2^i\n", "$$\n", "\n", "\n", "Since we are talking about delivering messages to all engines,\n", "the metric of interest for a 'cold start' is how many messages must the last engine wait for before its message is delivered,\n", "because until that point, at least some of the cluster is idle, waiting for a task.\n", "This is where the parallelism comes into play.\n", "\n", "In the DirectView, the message destined for the last engine N must wait for N sends from the client to the scheduler, then again for N sends from the scheduler to each engines, for a total of 2 N sends before the engines gets its message.\n", "\n", "In the BroadcastView, we only have to count sends along one path down the tree:\n", "\n", "- one send at the client\n", "- two sends at each level of the scheduler\n", "- $\\frac{N}/{2^d}$ sends at the leaf of the scheduler\n", "\n", "for a total of:\n", "\n", "$$\n", "1 + 2^d + \\frac{N}{2^d}\n", "$$\n", "\n", "sends to get the first message to the last engine (and therefore all engines). This is a good approximation of how deep your scheduler should be based on the number of engines. It is an approximation because it doesn't take into account the difference in cost between a local zmq send call vs a send actually traversing the network and being received." ] }, { "cell_type": "code", "execution_count": 9, "id": "680d971e-2a13-472c-9b08-c1be00b8304b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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1217dI0bN6588eLF8o0bN0pnzJihcV7/ww8/SC0WC40aNar8Ttddm2vr06eP/osvvvDcu3evaMCAAXr7MQ4cOCAiIsrLy+Pn5ubyw8PDzUREJ06cEBQUFPCeffZZbc2eBQAAAAAAAID6A0kxqJcOHDgg/O6772RNmzY1792795afnx9HRDR//vzSgQMH+t+8eZPXuHHjGo+fdfPmTV5ERIRp586dtzw9PSuSaUVFRSwpKclHKpVyqampBVFRURXllydPniyLi4vznz59utevv/5aYG+fMWOG16lTpwTPP/98+X/+858SPv/vDlelpaXMYrHmvObMmaPOy8vjnzp1SjB9+nSNPZl0J2FhYZbu3bvr9+3bJz5x4oSgffv2lUpCf/zxRw+hUEijR4++bTKqttfWvXt3g0gkon379omJSE1EpFar2fHjx0U9evTQ79+/X5yeni4ODw8vJyJKT08XExH16tVL7+L0AAAAAAAAAPUakmIN0L/3XJCvPHTZsybbDmkXVP7RkFYljm3v/98Zr/87me9Rk/0ndwsre7VPc7Vj2xvbzgkOXigJqc0+tbVmzRoPIqLXXntNbU+IERFJpVL68MMPS4cOHepX22N+/PHHJY4JMSKi1atXe5SWlrKPP/641DFpRETUrl0705gxY8q//fZbWVZWliAqKsqUn5/PS0lJkQYGBloWL15c6pgQIyJSKBQ17r12O6NGjdLu27dPvHbtWo8lS5aU2tszMzOFOTk5ggEDBuj8/f1ve67aXpuHhwc98cQThszMTFFRURHz9fXlMjIyREajkV5++WXNX3/9JczIyBC9+OKL5URkT55Rnz59kBQDAAAAAACABgdJMaiXTp48KSQi6tmzp8F5Xc+ePQ0CQe1eumKxmDp06FBlEP7ffvtNRESUlZUldBxjzO78+fMCIqJTp04JoqKiTJmZmUKLxUIxMTEG5wRbXXrmmWe0b7/9ttfmzZulH3/8can9eteuXSslIho7duwdSydre21ERD169NAfPnxYtGfPHvHw4cN1GRkZYqFQSL169TJ069bNcPDgQTERkclkoiNHjojCw8NNYWFhGOAfAAAAAAAAGhwkxaBeUqvVPCKi4ODgKgkXgUBA3t7etUrE+Pn5mXm8qvNKqFQqRkS0bt262/ac02g0jIiouLiYR0QUEhJS49LNu+Hh4UGDBw/Wrlu3zmPnzp3ixMREvcFgoG3btkl9fX0tCQkJd+ydVdtrI7L2+vrXv/4l37t3r3j48OG6/fv3izt27GiQy+Vcr1699D///LPkxIkTAo1Gw9RqNRs2bFiVpCUAAAAAAABAQ4CkWAP0ap/m6nspT/xoSKsS55LK2vjk6cdMMpms4M5b3j25XG4hIv6NGzd4jz32WKUElMlkouLiYl5QUFCNE1OMuZywkuRyOUdEtG/fvlsdO3as0pPMmT0Zd/36dZezUtalsWPHlq9bt85j3bp1HomJifqUlBSJSqXiTZ48WSMSVZlksoraXhsRUUxMjFEmk3H79+8XFRQUsFOnTglee+21MqK/yyR/+eUXsVarZUQYTwwAAAAAAAAarqpdZwDqgXbt2hmJiPbt21cl+7Nv3z6RyVSjHM8dde7c2UD09wyLdxITE2Pk8Xh05MgRUVlZmetMmwM+n88REZnNte9Y1qNHD2OzZs3MSqVSUlxczNavXy8lss5OWZP9a3ttRERCoZCio6MNubm5gjVr1nhwHFeRDGvZsqW5UaNG5n379on3798v5vF41LdvXyTFAAAAAAAAGogijUE4439/9Xvjp+ze7o6lPkBSDOole+Jn2bJl8sLCworkk1arpblz5yrq6jwTJ04sVygU3NKlS+WHDx8WOq83m82Unp5ekVQKDAy0DBkyRHvz5k3e22+/rXBOdqnValZcXFwRr6+vL0dElJeXd1c9y5577rlyvV5PX375pWzPnj2SyMhIU6dOnWqUEazttdn16NFDT0T0n//8x9PDw4N78sknK0okY2NjDYcPHxb9/vvvotatWxsdJ0EAAAAAAACA+snCcfT1gUutn/ryt2npZwu77z5d0ONQrsrf3XG5G8on7yPGmJyI7AOcCzmOu2PPIrDq0aOHceLEiZrk5GRZTExMYGJiolYoFNLOnTslCoXCEhgYWCeDu/v7+3PJyclF48eP9x0wYIB/bGysvlWrViYej0dXrlzh//HHHyKVSsUrKCi4bt/n3//+d8np06eFa9as8Th06JCod+/eepFIRJcuXeJnZGSIf/jhh6K+ffsaiIh69+6t//LLL2Wvvvqq91NPPaWVyWScl5eXZfr06TXq7TVu3Ljyf/3rX/KlS5fKjUYjPffcczXa726vjYiob9+++nnz5lFhYSGvV69eerFYXLGuV69e+v/9739SIqIe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z7iW0d3MmSu+DO24vCP25aiXjwt4gkdy+JgGlxSrpb62RyXHcZUSGxzHqRljB8maNHuSiH65zXF4RFRtuZwjxtjRalZFisVi4cGDB5Nut7+Pj4+/VCoVaDQa/5qcrz67l2vQ6635LIVCQW+88YbI8Vht27alYcOGWf73v//xNm3aFDBq1CgLEZHFYuEREcXHx3O9evWSazQauX0fo9FIP/30k1Amk9H7778v1Gq1FccLDQ2lyZMnc8uXL2dr1qzxe/PNNyteK35+fqTRVB47XigU0qhRo9i8efPYoUOHArp27coREf3000+84uJiNmLECEvbtm29HPd766236Mcff6TS0lLS6/U+9nXffPMN32Qy0auvvmoJDw/3dj6X8/kbNWpUJR4iookTJ7JDhw7Rrl27/J977jkLERHHcTwiIrFYLNNoNJXKARljxHFcxbHs985iscg1Gk2V5F11cnNzyWw2U3BwMN/V871+/XoBEdF7773HLBaLv/18EomEXnvtNXrttdeIMSZ03PfcuXPs008/rdVnU7du3SRBQUFVPsTT09P5//3vfyv+JQ0MDKRly5aZ+vbt66nRaDxdHSswMJCdO3eOFRYW+kskElebVLBYLAKdTud/p/c11D8Gg8GfiAjPHQA0VPgcA6g7HMdR5k1O8uctTvqPtjyVY+eLICJaGMOpRfxrXX89dK2r+6J8iHAcNb61V9oyb52iiam04v/qFuJRXnC85lzocxoT5xFLh36NdWeYD5MTuhPiFHWKV7GluFLPgE6STuVPyZ8yH/n1yEvuiq0uiMVif7KOHV9rD3tSrJXt8Ww163PImhRrSbakGGNsERGlEtFlsg7iP4aIehNR4v0MFFyLioriPD2r5i66devG/e9//6OsrKwqf6p5/PHHq5TF5eTkMK1WS126dOF8fHyqHK979+6W5cuX87KysnhEf/+Z4vTp0+y///0v78iRI7z8/PyKZJ3d9evXGRFxREQnT55kRET2JJkjhUJBbdq04Q4fPlwp3qNHjzIion79+t2xTJGI6MqVK/T555/zDxw4wLt69So5j8t148aNip8HDBhgWbRoEf/999/n7927l9e7d29LdHQ016pVK+5OvSxrqqioiBEROffWszt58iTj8XgUExNTZX23bt0sRFSlu1ZsbCx3/fp1l6WmtTVr1izzrFmzzBqNhs6fP8++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\n", "text/plain": [ "
" ] }, "metadata": { "image/png": { "height": 397, "width": 610 }, "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "import numpy as np\n", "\n", "N = np.logspace(0, 11, 12, base=2, dtype=int)\n", "N\n", "\n", "dview_sends = 2 * N\n", "\n", "def bcast_sends(N, depth):\n", " return 1 + (2**depth) + (N / (2**depth))\n", "\n", "plt.loglog(N, dview_sends, '--', label=\"direct view\")\n", "for depth in range(5):\n", " plt.loglog(N, bcast_sends(N, depth), label=f\"broadcast (d={depth})\")\n", "\n", "plt.xlabel(\"N engines\")\n", "plt.ylabel(\"messages\")\n", "plt.title(\"Serial messages for last engine\")\n", "plt.grid(True)\n", "plt.legend(loc=0)" ] }, { "cell_type": "code", "execution_count": 10, "id": "688f856f-c2b4-47ef-93cf-16a5f5abc496", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "image/png": { "height": 397, "width": 616 }, "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# \n", "plt.plot(N, [1] * len(N), '--k')\n", "for depth in range(7):\n", " plt.loglog(N, bcast_sends(N, depth) / dview_sends, label=f\"depth = {depth}\")\n", "\n", "plt.xlabel(\"N engines\")\n", "plt.ylabel(\"relative to dview\")\n", "plt.title(\"Serial messages for last engine\")\n", "\n", "plt.grid(True)\n", "plt.legend(loc=0)" ] }, { "cell_type": "markdown", "id": "5f065a9b-195c-49a7-92d7-670a39d81860", "metadata": {}, "source": [ "## Coalescing replies\n", "\n", "The broadcast view changes the number of requests a client sends from one per engine to just one,\n", "however each engine still has its own *reply* to that request.\n", "\n", "One of the sources of performance in the client, especially when sending many small tasks,\n", "is contention receiving replies.\n", "\n", "The IPython Parallel client receives replies in a background thread.\n", "If replies start arriving before the client is done sending requests,\n", "this can result in a lot of contention and context switching,\n", "slowing down task submission.\n", "\n", "Plus, deserializing all these replies can be a lot of work, especially if the only useful information is that it's done.\n", "\n", "For this, the broadcast view has a tuning parameter to 'coalesce' replies.\n", "Unlike depth, this parameter is set per-request instead of for the cluster as a whole.\n", "\n", "Coalescing reduces the number of replies in the same way that requests are reduced on the way to engines -\n", "by collecting replies at each node, and only sending them along to the next layer up when all engines have replied.\n", "\n", "As a result, when coalescing is enabled,\n", "a BroadcastView sends exactly one request and receives exactly one reply,\n", "dramatically reducing the workload and number of events on the client." ] }, { "cell_type": "code", "execution_count": 11, "id": "b5ecaea7-f8aa-4b93-9403-96a57bb2fa29", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "288 ms ± 44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], "source": [ "bcast_view.is_coalescing = False\n", "%timeit bcast_view.apply_sync(lambda: None)" ] }, { "cell_type": "code", "execution_count": 12, "id": "aa4dadbe-2a76-4163-b8ae-83613fb1ff33", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "216 ms ± 26.4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "bcast_view.is_coalescing = True\n", "%timeit bcast_view.apply_sync(lambda: None)" ] }, { "cell_type": "markdown", "id": "1a597b4f-64b7-4f31-855e-3c8d44a8e371", "metadata": {}, "source": [ "Coalescing has its own trade-offs.\n", "Each send and receive at each level has a cost, but so does waiting to send messages.\n", "Coalescing behaves best when lots of (especially small) replies are going to come at the same time.\n", "If responses are large and/or spread out, the cost of leaving network bandwidth idle will outweigh the savings of reduced message deserialization." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 } ipyparallel-7.1.0/docs/source/examples/broadcast/Dockerfile000066400000000000000000000002431412533144600240410ustar00rootroot00000000000000FROM jupyter/scipy-notebook:4a6f5b7e5db1 RUN mamba install -yq openmpi mpi4py RUN pip install --upgrade https://github.com/ipython/ipyparallel/archive/HEAD.tar.gz ipyparallel-7.1.0/docs/source/examples/broadcast/MPI Broadcast.ipynb000066400000000000000000002076761412533144600254450ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# More efficient data movement with MPI" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just like [we did](memmap%20Broadcast.ipynb) manually with memmap,\n", "you can move data more efficiently with MPI by sending it to just one engine,\n", "and using MPI to broadcast it to the rest of the engines.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import socket\n", "import os, sys, re\n", "\n", "import numpy as np\n", "\n", "import ipyparallel as ipp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For this demo, I will connect to a cluster with engines started with MPI.\n", "\n", "One way to do so would be:\n", "\n", " ipcluster start -n 64 --engines=MPI --profile mpi\n", " \n", "In this directory is a docker-compose file to simulate multiple engine sets launched with MPI.\n", "\n", "I ran this example with a cluster on a 64-core remote VM,\n", "so communication between the client and controller is over the public internet,\n", "while communication between the controller and engines is local." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "rc = ipp.Client(profile=\"mpi\")\n", "rc.wait_for_engines(64)\n", "eall = rc.broadcast_view(coalescing=True)\n", "root = rc[0]" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "64" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(rc)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "root['a'] = 5" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "%px from mpi4py.MPI import COMM_WORLD as MPI" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We cn get a mapping of IPP rank to MPI rank, in case they mismatch.\n", "\n", "In recent-enough IPython Parallel,\n", "they usually don't because IPython engines request their MPI rank as their engine id." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{0: 0,\n", " 1: 1,\n", " 2: 2,\n", " 3: 3,\n", " 4: 4,\n", " 5: 5,\n", " 6: 6,\n", " 7: 7,\n", " 8: 8,\n", " 9: 9,\n", " 10: 10,\n", " 11: 11,\n", " 12: 12,\n", " 13: 13,\n", " 14: 14,\n", " 15: 15,\n", " 16: 16,\n", " 17: 17,\n", " 18: 18,\n", " 19: 19,\n", " 20: 20,\n", " 21: 21,\n", " 22: 22,\n", " 23: 23,\n", " 24: 24,\n", " 25: 25,\n", " 26: 26,\n", " 27: 27,\n", " 28: 28,\n", " 29: 29,\n", " 30: 30,\n", " 31: 31,\n", " 32: 32,\n", " 33: 33,\n", " 34: 34,\n", " 35: 35,\n", " 36: 36,\n", " 37: 37,\n", " 38: 38,\n", " 39: 39,\n", " 40: 40,\n", " 41: 41,\n", " 42: 42,\n", " 43: 43,\n", " 44: 44,\n", " 45: 45,\n", " 46: 46,\n", " 47: 47,\n", " 48: 48,\n", " 49: 49,\n", " 50: 50,\n", " 51: 51,\n", " 52: 52,\n", " 53: 53,\n", " 54: 54,\n", " 55: 55,\n", " 56: 56,\n", " 57: 57,\n", " 58: 58,\n", " 59: 59,\n", " 60: 60,\n", " 61: 61,\n", " 62: 62,\n", " 63: 63}" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mpi_ranks = eall.apply_async(lambda : MPI.Get_rank()).get_dict()\n", "root_rank = root.apply_sync(lambda : MPI.Get_rank())\n", "mpi_ranks" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "32" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sz = 512\n", "data = np.random.random((sz, sz))\n", "megabytes = data.nbytes // (1024 * 1024)\n", "megabytes" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e3943864933941bbb65e6ad17e3c589f", "version_major": 2, "version_minor": 0 }, "text/plain": [ "_push: 0%| | 0/64 [00:00 0: a = randint(0, nodes - 1) b = a while b == a: b = randint(0, nodes - 1) G.add_edge(a, b) if nx.is_directed_acyclic_graph(G): edges -= 1 else: # we closed a loop! G.remove_edge(a, b) return G def add_children(G, parent, level, n=2): """Add children recursively to a binary tree.""" if level == 0: return for i in range(n): child = parent + str(i) G.add_node(child) G.add_edge(parent, child) add_children(G, child, level - 1, n) def make_bintree(levels): """Make a symmetrical binary tree with @levels""" G = nx.DiGraph() root = '0' G.add_node(root) add_children(G, root, levels, 2) return G def submit_jobs(view, G, jobs): """Submit jobs via client where G describes the time dependencies.""" results = {} for node in nx.topological_sort(G): with view.temp_flags(after=[results[n] for n in G.predecessors(node)]): results[node] = view.apply(jobs[node]) return results def validate_tree(G, results): """Validate that jobs executed after their dependencies.""" for node in G: started = results[node].metadata.started for parent in G.predecessors(node): finished = results[parent].metadata.completed assert started > finished, "%s should have happened after %s" % ( node, parent, ) def main(nodes, edges): """Generate a random graph, submit jobs, then validate that the dependency order was enforced. Finally, plot the graph, with time on the x-axis, and in-degree on the y (just for spread). All arrows must point at least slightly to the right if the graph is valid. """ from matplotlib import pyplot as plt from matplotlib.dates import date2num from matplotlib.cm import gist_rainbow print("building DAG") G = random_dag(nodes, edges) jobs = {} pos = {} colors = {} for node in G: jobs[node] = randomwait client = parallel.Client() view = client.load_balanced_view() print("submitting %i tasks with %i dependencies" % (nodes, edges)) results = submit_jobs(view, G, jobs) print("waiting for results") client.wait_interactive() for node in G: md = results[node].metadata start = date2num(md.started) runtime = date2num(md.completed) - start pos[node] = (start, runtime) colors[node] = md.engine_id validate_tree(G, results) nx.draw( G, pos, node_list=list(colors.keys()), node_color=list(colors.values()), cmap=gist_rainbow, with_labels=False, ) x, y = zip(*pos.values()) xmin, ymin = map(min, (x, y)) xmax, ymax = map(max, (x, y)) xscale = xmax - xmin yscale = ymax - ymin plt.xlim(xmin - xscale * 0.1, xmax + xscale * 0.1) plt.ylim(ymin - yscale * 0.1, ymax + yscale * 0.1) return G, results if __name__ == '__main__': from matplotlib import pyplot as plt # main(5,10) main(32, 96) plt.show() ipyparallel-7.1.0/docs/source/examples/dask.ipynb000066400000000000000000001224631412533144600221030ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Working with IPython and dask.distributed\n", "\n", "[dask.distributed](https://distributed.readthedocs.io) is a cool library for doing distributed execution. You should check it out, if you haven't already.\n", "\n", "In many cases, dask.distributed should replace using IPython Parallel if you primarily use the LoadBalancedView.\n", "\n", "However, you may already have infrastructure for deploying and managing IPython engines,\n", "and IPython Parallel's interactive debugging features can still be useful.\n", "\n", "Any IPython cluster can *become* a dask clsuter at any time,\n", "and be used simultaneously via both APIs." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can turn your IPython cluster into a distributed cluster by calling `Client.become_dask()`:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Starting 4 engines with \n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "12bcc9bb5f5849b281bc6b305c5cfe7d", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/4 [00:00\n", "
\n", "
\n", "

Client

\n", "

Client-4de93880-0b0d-11ec-b993-784f4385c030

\n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
Connection method: Direct
\n", " Dashboard: http://192.168.1.31:53263/status\n", "
\n", "\n", " \n", "
\n", "

Scheduler Info

\n", "
\n", "
\n", "
\n", "
\n", "

Scheduler

\n", "

Scheduler-faed09d3-e79f-4698-9162-b38cece51ddf

\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Comm: tcp://192.168.1.31:53264\n", " \n", " Workers: 2\n", "
\n", " Dashboard: http://192.168.1.31:53263/status\n", " \n", " Total threads: 2\n", "
\n", " Started: Just now\n", " \n", " Total memory: 8.00 GiB\n", "
\n", "
\n", "
\n", "\n", "
\n", " \n", "

Workers

\n", "
\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", "

Worker: tcp://192.168.1.31:53626

\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
\n", " Comm: tcp://192.168.1.31:53626\n", " \n", " Total threads: 1\n", "
\n", " Dashboard: http://192.168.1.31:53634/status\n", " \n", " Memory: 4.00 GiB\n", "
\n", " Nanny: None\n", "
\n", " Local directory: /Users/minrk/dev/ip/parallel/docs/source/examples/dask-worker-space/worker-4ra4jer1\n", "
\n", " Tasks executing: 0\n", " \n", " Tasks in memory: 0\n", "
\n", " Tasks ready: 0\n", " \n", " Tasks in flight: 0\n", "
\n", " CPU usage: 0.0%\n", " \n", " Last seen: Just now\n", "
\n", " Memory usage: 96.50 MiB\n", " \n", " Spilled bytes: 0 B\n", "
\n", " Read bytes: 0.0 B\n", " \n", " Write bytes: 0.0 B\n", "
\n", "
\n", "
\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", "

Worker: tcp://192.168.1.31:53627

\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
\n", " Comm: tcp://192.168.1.31:53627\n", " \n", " Total threads: 1\n", "
\n", " Dashboard: http://192.168.1.31:53635/status\n", " \n", " Memory: 4.00 GiB\n", "
\n", " Nanny: None\n", "
\n", " Local directory: /Users/minrk/dev/ip/parallel/docs/source/examples/dask-worker-space/worker-03ml7c0q\n", "
\n", " Tasks executing: 0\n", " \n", " Tasks in memory: 0\n", "
\n", " Tasks ready: 0\n", " \n", " Tasks in flight: 0\n", "
\n", " CPU usage: 0.0%\n", " \n", " Last seen: Just now\n", "
\n", " Memory usage: 96.43 MiB\n", " \n", " Spilled bytes: 0 B\n", "
\n", " Read bytes: 234.10 kiB\n", " \n", " Write bytes: 234.10 kiB\n", "
\n", "
\n", "
\n", "
\n", " \n", "\n", "
\n", "
\n", "
\n", " \n", "\n", "
\n", "" ], "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask_client = rc.become_dask(ncores=1)\n", "dask_client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will:\n", "\n", "1. start a Scheduler on the Hub\n", "2. start a Worker on each engine\n", "3. return an Executor, the distributed client API\n", "\n", "By default, distributed Workers will use threads to run on all cores of a machine. \n", "In this case, since I already have one *engine* per core,\n", "I tell distributed to run one core per Worker with `ncores=1`.\n", "\n", "We can now use our IPython cluster with distributed:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b9874c7d2ae04c6fa879f14f00135e30", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from distributed import progress\n", "\n", "\n", "def square(x):\n", " return x ** 2\n", "\n", "\n", "def neg(x):\n", " return -x\n", "\n", "\n", "A = dask_client.map(square, range(1000))\n", "B = dask_client.map(neg, A)\n", "total = dask_client.submit(sum, B)\n", "progress(total)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-332833500" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "total.result()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I could also let distributed do its multithreading thing, and run one multi-threaded Worker per engine.\n", "\n", "First, I need to get a mapping of one engine per host:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{0: 'touchy', 1: 'touchy', 2: 'touchy', 3: 'touchy'}" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import socket\n", "\n", "engine_hosts = rc[:].apply_async(socket.gethostname).get_dict()\n", "engine_hosts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I can reverse this mapping, to get a list of engines on each host:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'touchy': [0, 1, 2, 3]}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "host_engines = {}\n", "for engine_id, host in engine_hosts.items():\n", " if host not in host_engines:\n", " host_engines[host] = []\n", " host_engines[host].append(engine_id)\n", "\n", "host_engines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now I can get one engine per host:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "one_engine_per_host = [engines[0] for engines in host_engines.values()]\n", "one_engine_per_host" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Here's a concise, but more opaque version that does the same thing:*" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "one_engine_per_host = list({host: eid for eid, host in engine_hosts.items()}.values())\n", "one_engine_per_host" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I can now stop the first distributed cluster, and start a new one on just these engines, letting distributed allocate threads:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "
\n", "

Client

\n", "

Client-4f4545ca-0b0d-11ec-b993-784f4385c030

\n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
Connection method: Direct
\n", " Dashboard: http://192.168.1.31:54310/status\n", "
\n", "\n", " \n", "
\n", "

Scheduler Info

\n", "
\n", "
\n", "
\n", "
\n", "

Scheduler

\n", "

Scheduler-ecb384f5-10bb-4d53-9e4c-67db7ff07d03

\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Comm: tcp://192.168.1.31:54313\n", " \n", " Workers: 1\n", "
\n", " Dashboard: http://192.168.1.31:54310/status\n", " \n", " Total threads: 1\n", "
\n", " Started: Just now\n", " \n", " Total memory: 4.00 GiB\n", "
\n", "
\n", "
\n", "\n", "
\n", " \n", "

Workers

\n", "
\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", " \n", "

Worker: tcp://192.168.1.31:54344

\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
\n", " Comm: tcp://192.168.1.31:54344\n", " \n", " Total threads: 1\n", "
\n", " Dashboard: http://192.168.1.31:54345/status\n", " \n", " Memory: 4.00 GiB\n", "
\n", " Nanny: None\n", "
\n", " Local directory: /Users/minrk/dev/ip/parallel/docs/source/examples/dask-worker-space/worker-kmqmwu7_\n", "
\n", " Tasks executing: 0\n", " \n", " Tasks in memory: 0\n", "
\n", " Tasks ready: 0\n", " \n", " Tasks in flight: 0\n", "
\n", " CPU usage: 0.0%\n", " \n", " Last seen: Just now\n", "
\n", " Memory usage: 104.27 MiB\n", " \n", " Spilled bytes: 0 B\n", "
\n", " Read bytes: 1.46 MiB\n", " \n", " Write bytes: 1.46 MiB\n", "
\n", "
\n", "
\n", "
\n", " \n", "\n", "
\n", "
\n", "
\n", " \n", "\n", "
\n", "
" ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rc.stop_distributed()\n", "\n", "dask_client = rc.become_dask(one_engine_per_host)\n", "dask_client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And submit the same tasks again:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b6eddbe0edfb49e09f289ed49a29e988", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox()" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "A = dask_client.map(square, range(100))\n", "B = dask_client.map(neg, A)\n", "total = dask_client.submit(sum, B)\n", "progress(total)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Debugging distributed with IPython" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "
\n", "
\n", "

Client

\n", "

Client-4f8be14c-0b0d-11ec-b993-784f4385c030

\n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
Connection method: Direct
\n", " Dashboard: http://192.168.1.31:54467/status\n", "
\n", "\n", " \n", "
\n", "

Scheduler Info

\n", "
\n", "
\n", "
\n", "
\n", "

Scheduler

\n", "

Scheduler-9de91656-970d-405d-85ce-7e065b96c0c8

\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", " Comm: tcp://192.168.1.31:54478\n", " \n", " Workers: 0\n", "
\n", " Dashboard: http://192.168.1.31:54467/status\n", " \n", " Total threads: 0\n", "
\n", " Started: Just now\n", " \n", " Total memory: 0 B\n", "
\n", "
\n", "
\n", "\n", "
\n", " \n", "

Workers

\n", "
\n", "\n", " \n", "\n", "
\n", "
\n", "
\n", " \n", "\n", "
\n", "
" ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rc.stop_distributed()\n", "\n", "dask_client = rc.become_dask(one_engine_per_host)\n", "dask_client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's set the %px magics to only run on our one engine per host:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "view = rc[one_engine_per_host]\n", "view.block = True\n", "view.activate()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's submit some work that's going to fail somewhere in the middle:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "524ea13e69ef4fbc80650087f3f9b746", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox()" ] }, "metadata": {}, "output_type": "display_data" }, { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/var/folders/qr/3vxfnp1x2t1fw55dr288mphc0000gn/T/ipykernel_14739/3426399645.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0mtotal\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdask_client\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msubmit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msum\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minverted\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0mdisplay\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprogress\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtotal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0mtotal\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m~/conda/lib/python3.9/site-packages/distributed/client.py\u001b[0m in \u001b[0;36mresult\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 231\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatus\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"error\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 232\u001b[0m \u001b[0mtyp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 233\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mexc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwith_traceback\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 234\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatus\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"cancelled\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 235\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/var/folders/qr/3vxfnp1x2t1fw55dr288mphc0000gn/T/ipykernel_14739/3426399645.py\u001b[0m in \u001b[0;36minverse\u001b[0;34m()\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minverse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "source": [ "from IPython.display import display\n", "from distributed import progress\n", "\n", "\n", "def shift5(x):\n", " return x - 5\n", "\n", "\n", "def inverse(x):\n", " return 1 / x\n", "\n", "\n", "shifted = dask_client.map(shift5, range(1, 10))\n", "inverted = dask_client.map(inverse, shifted)\n", "\n", "total = dask_client.submit(sum, inverted)\n", "display(progress(total))\n", "total.result()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see which task failed:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[f for f in inverted if f.status == \"error\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When IPython starts a worker on each engine,\n", "it stores it in the `distributed_worker` variable in the engine's namespace.\n", "This lets us query the worker interactively.\n", "\n", "We can check out the current data resident on each worker:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[0;31mOut[3:1]: \u001b[0mBuffer<, deserialize_bytes >>" ] }, "metadata": { "after": [], "completed": "2021-09-01T10:14:10.163710", "data": {}, "engine_id": 3, "engine_uuid": "f3a63abb-5985ad4fd1b9c71f02922bb4", "error": null, "execute_input": "dask_worker.data\n", "execute_result": { "data": { "text/plain": "Buffer<, deserialize_bytes >>" }, "execution_count": 1, "metadata": {} }, "follow": [], "is_broadcast": false, "is_coalescing": false, "msg_id": "867ada26-ed0b46a8d787906a2a56aca7_24", "outputs": [], "received": "2021-09-01T10:14:10.167670", "started": "2021-09-01T10:14:10.138718", "status": "ok", "stderr": "", "stdout": "", "submitted": "2021-09-01T10:14:10.130438" }, "output_type": "display_data" } ], "source": [ "%%px\n", "dask_worker.data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we can poke around with each Worker,\n", "we can have a slightly easier time figuring out what went wrong." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/dependencies.py000066400000000000000000000061521412533144600231120ustar00rootroot00000000000000import ipyparallel as ipp from ipyparallel import Dependency client = ipp.Client() # this will only run on machines that can import numpy: @ipp.require('numpy') def norm(A): from numpy.linalg import norm return norm(A, 2) def checkpid(pid): """return the pid of the engine""" import os return os.getpid() == pid def checkhostname(host): import socket return socket.gethostname() == host def getpid(): import os return os.getpid() pid0 = client[0].apply_sync(getpid) # this will depend on the pid being that of target 0: @ipp.depend(checkpid, pid0) def getpid2(): import os return os.getpid() view = client.load_balanced_view() view.block = True # will run on anything: pids1 = [view.apply(getpid) for i in range(len(client.ids))] print(pids1) # will only run on e0: pids2 = [view.apply(getpid2) for i in range(len(client.ids))] print(pids2) print("now test some dependency behaviors") def wait(t): import time time.sleep(t) return t # fail after some time: def wait_and_fail(t): import time time.sleep(t) return 1 / 0 successes = [view.apply_async(wait, 1).msg_ids[0] for i in range(len(client.ids))] failures = [ view.apply_async(wait_and_fail, 1).msg_ids[0] for i in range(len(client.ids)) ] mixed = [failures[0], successes[0]] d1a = Dependency(mixed, all=False, failure=True) # yes d1b = Dependency(mixed, all=False) # yes d2a = Dependency(mixed, all=True, failure=True) # yes after / no follow d2b = Dependency(mixed, all=True) # no d3 = Dependency(failures, all=False) # no d4 = Dependency(failures, all=False, failure=True) # yes d5 = Dependency(failures, all=True, failure=True) # yes after / no follow d6 = Dependency(successes, all=True, failure=True) # yes after / no follow view.block = False flags = view.temp_flags with flags(after=d1a): r1a = view.apply(getpid) with flags(follow=d1b): r1b = view.apply(getpid) with flags(after=d2b, follow=d2a): r2a = view.apply(getpid) with flags(after=d2a, follow=d2b): r2b = view.apply(getpid) with flags(after=d3): r3 = view.apply(getpid) with flags(after=d4): r4a = view.apply(getpid) with flags(follow=d4): r4b = view.apply(getpid) with flags(after=d3, follow=d4): r4c = view.apply(getpid) with flags(after=d5): r5 = view.apply(getpid) with flags(follow=d5, after=d3): r5b = view.apply(getpid) with flags(follow=d6): r6 = view.apply(getpid) with flags(after=d6, follow=d2b): r6b = view.apply(getpid) def should_fail(f): try: f() except ipp.error.KernelError: pass else: print('should have raised') # raise Exception("should have raised") # print(r1a.msg_ids) r1a.get() # print(r1b.msg_ids) r1b.get() # print(r2a.msg_ids) should_fail(r2a.get) # print(r2b.msg_ids) should_fail(r2b.get) # print(r3.msg_ids) should_fail(r3.get) # print(r4a.msg_ids) r4a.get() # print(r4b.msg_ids) r4b.get() # print(r4c.msg_ids) should_fail(r4c.get) # print(r5.msg_ids) r5.get() # print(r5b.msg_ids) should_fail(r5b.get) # print(r6.msg_ids) should_fail(r6.get) # assuming > 1 engine # print(r6b.msg_ids) should_fail(r6b.get) print('done') ipyparallel-7.1.0/docs/source/examples/fetchparse.py000066400000000000000000000054151412533144600226110ustar00rootroot00000000000000""" An exceptionally lousy site spider Ken Kinder Updated for newparallel by Min Ragan-Kelley This module gives an example of how the task interface to the IPython controller works. Before running this script start the IPython controller and some engines using something like:: ipcluster start -n 4 """ import sys import time import bs4 # noqa this isn't necessary, but it helps throw the dependency error earlier import ipyparallel as ipp def fetchAndParse(url, data=None): import requests from urllib.parse import urljoin import bs4 # noqa links = [] r = requests.get(url, data=data) r.raise_for_status() if 'text/html' in r.headers.get('content-type'): doc = bs4.BeautifulSoup(r.text, "html.parser") for node in doc.findAll('a'): href = node.get('href', None) if href: links.append(urljoin(url, href)) return links class DistributedSpider: # Time to wait between polling for task results. pollingDelay = 0.5 def __init__(self, site): self.client = ipp.Client() self.view = self.client.load_balanced_view() self.mux = self.client[:] self.allLinks = [] self.linksWorking = {} self.linksDone = {} self.site = site def visitLink(self, url): if url not in self.allLinks: self.allLinks.append(url) if url.startswith(self.site): print(' ', url) self.linksWorking[url] = self.view.apply(fetchAndParse, url) def onVisitDone(self, links, url): print(url + ':') self.linksDone[url] = None del self.linksWorking[url] for link in links: self.visitLink(link) def run(self): self.visitLink(self.site) while self.linksWorking: print(len(self.linksWorking), 'pending...') self.synchronize() time.sleep(self.pollingDelay) def synchronize(self): for url, ar in list(self.linksWorking.items()): # Calling get_task_result with block=False will return None if the # task is not done yet. This provides a simple way of polling. try: links = ar.get(0) except ipp.error.TimeoutError: continue except Exception as e: self.linksDone[url] = None del self.linksWorking[url] print('%s: %s' % (url, e)) else: self.onVisitDone(links, url) def main(): if len(sys.argv) > 1: site = sys.argv[1] else: site = input('Enter site to crawl: ') distributedSpider = DistributedSpider(site) distributedSpider.run() if __name__ == '__main__': main() ipyparallel-7.1.0/docs/source/examples/interengine/000077500000000000000000000000001412533144600224155ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/examples/interengine/bintree.py000066400000000000000000000155321412533144600244250ustar00rootroot00000000000000""" BinaryTree inter-engine communication class use from bintree_script.py Provides parallel [all]reduce functionality """ from __future__ import print_function import re import socket from functools import reduce import zmq from ipyparallel.serialize import deserialize_object from ipyparallel.serialize import serialize_object from ipyparallel.util import disambiguate_url # ---------------------------------------------------------------------------- # bintree-related construction/printing helpers # ---------------------------------------------------------------------------- def bintree(ids, parent=None): """construct {child:parent} dict representation of a binary tree keys are the nodes in the tree, and values are the parent of each node. The root node has parent `parent`, default: None. >>> tree = bintree(range(7)) >>> tree {0: None, 1: 0, 2: 1, 3: 1, 4: 0, 5: 4, 6: 4} >>> print_bintree(tree) 0 1 2 3 4 5 6 """ parents = {} n = len(ids) if n == 0: return parents root = ids[0] parents[root] = parent if len(ids) == 1: return parents else: ids = ids[1:] n = len(ids) left = bintree(ids[: n // 2], parent=root) right = bintree(ids[n // 2 :], parent=root) parents.update(left) parents.update(right) return parents def reverse_bintree(parents): """construct {parent:[children]} dict from {child:parent} keys are the nodes in the tree, and values are the lists of children of that node in the tree. reverse_tree[None] is the root node >>> tree = bintree(range(7)) >>> reverse_bintree(tree) {None: 0, 0: [1, 4], 4: [5, 6], 1: [2, 3]} """ children = {} for child, parent in parents.iteritems(): if parent is None: children[None] = child continue elif parent not in children: children[parent] = [] children[parent].append(child) return children def depth(n, tree): """get depth of an element in the tree""" d = 0 parent = tree[n] while parent is not None: d += 1 parent = tree[parent] return d def print_bintree(tree, indent=' '): """print a binary tree""" for n in sorted(tree.keys()): print("%s%s" % (indent * depth(n, tree), n)) # ---------------------------------------------------------------------------- # Communicator class for a binary-tree map # ---------------------------------------------------------------------------- ip_pat = re.compile(r'^\d+\.\d+\.\d+\.\d+$') def disambiguate_dns_url(url, location): """accept either IP address or dns name, and return IP""" if not ip_pat.match(location): location = socket.gethostbyname(location) return disambiguate_url(url, location) class BinaryTreeCommunicator: id = None pub = None sub = None downstream = None upstream = None pub_url = None tree_url = None def __init__(self, id, interface='tcp://*', root=False): self.id = id self.root = root # create context and sockets self._ctx = zmq.Context() if root: self.pub = self._ctx.socket(zmq.PUB) else: self.sub = self._ctx.socket(zmq.SUB) self.sub.SUBSCRIBE = b'' self.downstream = self._ctx.socket(zmq.PULL) self.upstream = self._ctx.socket(zmq.PUSH) # bind to ports interface_f = interface + ":%i" if self.root: pub_port = self.pub.bind_to_random_port(interface) self.pub_url = interface_f % pub_port tree_port = self.downstream.bind_to_random_port(interface) self.tree_url = interface_f % tree_port self.downstream_poller = zmq.Poller() self.downstream_poller.register(self.downstream, zmq.POLLIN) # guess first public IP from socket self.location = socket.gethostbyname_ex(socket.gethostname())[-1][0] def __del__(self): self.downstream.close() self.upstream.close() if self.root: self.pub.close() else: self.sub.close() self._ctx.term() @property def info(self): """return the connection info for this object's sockets.""" return (self.tree_url, self.location) def connect(self, peers, btree, pub_url, root_id=0): """connect to peers. `peers` will be a dict of 4-tuples, keyed by name. {peer : (ident, addr, pub_addr, location)} where peer is the name, ident is the XREP identity, addr,pub_addr are the """ # count the number of children we have self.nchildren = list(btree.values()).count(self.id) if self.root: return # root only binds root_location = peers[root_id][-1] self.sub.connect(disambiguate_dns_url(pub_url, root_location)) parent = btree[self.id] tree_url, location = peers[parent] self.upstream.connect(disambiguate_dns_url(tree_url, location)) def serialize(self, obj): """serialize objects. Must return list of sendable buffers. Can be extended for more efficient/noncopying serialization of numpy arrays, etc. """ return serialize_object(obj) def deserialize(self, msg): """inverse of serialize""" return deserialize_object(msg)[0] def publish(self, value): assert self.root self.pub.send_multipart(self.serialize(value)) def consume(self): assert not self.root return self.deserialize(self.sub.recv_multipart()) def send_upstream(self, value, flags=0): assert not self.root self.upstream.send_multipart(self.serialize(value), flags=flags) def recv_downstream(self, flags=0, timeout=2000.0): # wait for a message, so we won't block if there was a bug self.downstream_poller.poll(timeout) msg = self.downstream.recv_multipart(flags) return self.deserialize(msg) def reduce(self, f, value, flat=True, all=False): """parallel reduce on binary tree if flat: value is an entry in the sequence else: value is a list of entries in the sequence if all: broadcast final result to all nodes else: only root gets final result """ if not flat: value = reduce(f, value) for i in range(self.nchildren): value = f(value, self.recv_downstream()) if not self.root: self.send_upstream(value) if all: if self.root: self.publish(value) else: value = self.consume() return value def allreduce(self, f, value, flat=True): """parallel reduce followed by broadcast of the result""" return self.reduce(f, value, flat=flat, all=True) ipyparallel-7.1.0/docs/source/examples/interengine/bintree_script.py000077500000000000000000000054551412533144600260170ustar00rootroot00000000000000#!/usr/bin/env python """ Script for setting up and using [all]reduce with a binary-tree engine interconnect. usage: `python bintree_script.py` This spanning tree strategy ensures that a single node node mailbox will never receive more that 2 messages at once. This is very important to scale to large clusters (e.g. 1000 nodes) since if you have many incoming messages of a couple of megabytes you might saturate the network interface of a single node and potentially its memory buffers if the messages are not consumed in a streamed manner. Note that the AllReduce scheme implemented with the spanning tree strategy impose the aggregation function to be commutative and distributive. It might not be the case if you implement the naive gather / reduce / broadcast strategy where you can reorder the partial data before performing the reduce. """ from __future__ import print_function import ipyparallel as ipp # connect client and create views rc = ipp.Client() rc.block = True ids = rc.ids root_id = ids[0] root = rc[root_id] view = rc[:] from bintree import bintree, print_bintree # generate binary tree of parents btree = bintree(ids) print("setting up binary tree interconnect:") print_bintree(btree) view.run('bintree.py') view.scatter('id', ids, flatten=True) view['root_id'] = root_id # create the Communicator objects on the engines view.execute('com = BinaryTreeCommunicator(id, root = id==root_id )') pub_url = root.apply_sync(lambda: com.pub_url) # noqa: F821 # gather the connection information into a dict ar = view.apply_async(lambda: com.info) # noqa: F821 peers = ar.get_dict() # this is a dict, keyed by engine ID, of the connection info for the EngineCommunicators # connect the engines to each other: def connect(com, peers, tree, pub_url, root_id): """this function will be called on the engines""" com.connect(peers, tree, pub_url, root_id) view.apply_sync(connect, ipp.Reference('com'), peers, btree, pub_url, root_id) # functions that can be used for reductions # max and min builtins can be used as well def add(a, b): """cumulative sum reduction""" return a + b def mul(a, b): """cumulative product reduction""" return a * b view['add'] = add view['mul'] = mul # scatter some data data = list(range(1000)) view.scatter('data', data) # perform cumulative sum via allreduce print("reducing") ar = view.execute("data_sum = com.allreduce(add, data, flat=False)", block=False) ar.wait_interactive() print("allreduce sum of data on all engines:", view['data_sum']) # perform cumulative sum *without* final broadcast # when not broadcasting with allreduce, the final result resides on the root node: view.execute("ids_sum = com.reduce(add, id, flat=True)") print("reduce sum of engine ids (not broadcast):", root['ids_sum']) print("partial result on each engine:", view['ids_sum']) ipyparallel-7.1.0/docs/source/examples/interengine/communicator.py000066400000000000000000000051161412533144600254720ustar00rootroot00000000000000import socket import uuid import zmq from ipyparallel.util import disambiguate_url class EngineCommunicator: def __init__(self, interface='tcp://*', identity=None): self._ctx = zmq.Context() self.socket = self._ctx.socket(zmq.XREP) self.pub = self._ctx.socket(zmq.PUB) self.sub = self._ctx.socket(zmq.SUB) # configure sockets self.identity = identity or bytes(uuid.uuid4()) self.socket.IDENTITY = self.identity self.sub.SUBSCRIBE = b'' # bind to ports port = self.socket.bind_to_random_port(interface) pub_port = self.pub.bind_to_random_port(interface) self.url = interface + ":%i" % port self.pub_url = interface + ":%i" % pub_port # guess first public IP from socket self.location = socket.gethostbyname_ex(socket.gethostname())[-1][0] self.peers = {} def __del__(self): self.socket.close() self.pub.close() self.sub.close() self._ctx.term() @property def info(self): """return the connection info for this object's sockets.""" return (self.identity, self.url, self.pub_url, self.location) def connect(self, peers): """connect to peers. `peers` will be a dict of 4-tuples, keyed by name. {peer : (ident, addr, pub_addr, location)} where peer is the name, ident is the XREP identity, addr,pub_addr are the """ for peer, (ident, url, pub_url, location) in peers.items(): self.peers[peer] = ident if ident != self.identity: self.sub.connect(disambiguate_url(pub_url, location)) if ident > self.identity: # prevent duplicate xrep, by only connecting # engines to engines with higher IDENTITY # a doubly-connected pair will crash self.socket.connect(disambiguate_url(url, location)) def send(self, peers, msg, flags=0, copy=True): if not isinstance(peers, list): peers = [peers] if not isinstance(msg, list): msg = [msg] for p in peers: ident = self.peers[p] self.socket.send_multipart([ident] + msg, flags=flags, copy=copy) def recv(self, flags=0, copy=True): return self.socket.recv_multipart(flags=flags, copy=copy)[1:] def publish(self, msg, flags=0, copy=True): if not isinstance(msg, list): msg = [msg] self.pub.send_multipart(msg, copy=copy) def consume(self, flags=0, copy=True): return self.sub.recv_multipart(flags=flags, copy=copy) ipyparallel-7.1.0/docs/source/examples/interengine/interengine.py000066400000000000000000000025471412533144600253060ustar00rootroot00000000000000import ipyparallel as ipp rc = ipp.Client() rc.block = True view = rc[:] view.run('communicator.py') view.execute('com = EngineCommunicator()') # gather the connection information into a dict ar = view.apply_async(lambda: com.info) # noqa: F821 peers = ar.get_dict() # this is a dict, keyed by engine ID, of the connection info for the EngineCommunicators # connect the engines to each other: view.apply_sync(lambda pdict: com.connect(pdict), peers) # noqa: F821 # now all the engines are connected, and we can communicate between them: def broadcast(client, sender, msg_name, dest_name=None, block=None): """broadcast a message from one engine to all others.""" dest_name = msg_name if dest_name is None else dest_name client[sender].execute('com.publish(%s)' % msg_name, block=None) targets = client.ids targets.remove(sender) return client[targets].execute('%s=com.consume()' % dest_name, block=None) def send(client, sender, targets, msg_name, dest_name=None, block=None): """send a message from one to one-or-more engines.""" dest_name = msg_name if dest_name is None else dest_name def _send(targets, m_name): msg = globals()[m_name] return com.send(targets, msg) # noqa: F821 client[sender].apply_async(_send, targets, msg_name) return client[targets].execute('%s=com.recv()' % dest_name, block=None) ipyparallel-7.1.0/docs/source/examples/iopubwatcher.py000066400000000000000000000054651412533144600231660ustar00rootroot00000000000000"""A script for watching all traffic on the IOPub channel (stdout/stderr/pyerr) of engines. This connects to the default cluster, or you can pass the path to your ipcontroller-client.json Try running this script, and then running a few jobs that print (and call sys.stdout.flush), and you will see the print statements as they arrive, notably not waiting for the results to finish. You can use the zeromq SUBSCRIBE mechanism to only receive information from specific engines, and easily filter by message type. Authors ------- * MinRK """ import json import sys import zmq from ipykernel.connect import find_connection_file from jupyter_client.session import Session def main(connection_file): """watch iopub channel, and print messages""" ctx = zmq.Context.instance() with open(connection_file) as f: cfg = json.loads(f.read()) reg_url = cfg['interface'] iopub_port = cfg['iopub'] iopub_url = "%s:%s" % (reg_url, iopub_port) session = Session(key=cfg['key'].encode('ascii')) sub = ctx.socket(zmq.SUB) # This will subscribe to all messages: sub.SUBSCRIBE = b'' # replace with b'' with b'engine.1.stdout' to subscribe only to engine 1's stdout # 0MQ subscriptions are simple 'foo*' matches, so 'engine.1.' subscribes # to everything from engine 1, but there is no way to subscribe to # just stdout from everyone. # multiple calls to subscribe will add subscriptions, e.g. to subscribe to # engine 1's stderr and engine 2's stdout: # sub.SUBSCRIBE = b'engine.1.stderr' # sub.SUBSCRIBE = b'engine.2.stdout' sub.connect(iopub_url) while True: try: idents, msg = session.recv(sub, mode=0) except KeyboardInterrupt: return # ident always length 1 here topic = idents[0].decode('utf8', 'replace') if msg['msg_type'] == 'stream': # stdout/stderr # stream names are in msg['content']['name'], if you want to handle # them differently print("%s: %s" % (topic, msg['content']['text'])) elif msg['msg_type'] == 'error': # Python traceback c = msg['content'] print(topic + ':') for line in c['traceback']: # indent lines print(' ' + line) elif msg['msg_type'] == 'error': # Python traceback c = msg['content'] print(topic + ':') for line in c['traceback']: # indent lines print(' ' + line) if __name__ == '__main__': if len(sys.argv) > 1: pattern = sys.argv[1] else: # This gets the security file for the default profile: pattern = 'ipcontroller-client.json' cf = find_connection_file(pattern) print("Using connection file %s" % cf) main(cf) ipyparallel-7.1.0/docs/source/examples/itermapresult.py000066400000000000000000000044361412533144600233670ustar00rootroot00000000000000"""Example of iteration through AsyncMapResults, without waiting for all results When you call view.map(func, sequence), you will receive a special AsyncMapResult object. These objects are used to reconstruct the results of the split call. One feature AsyncResults provide is that they are iterable *immediately*, so you can iterate through the actual results as they complete. This is useful if you submit a large number of tasks that may take some time, but want to perform logic on elements in the result, or even abort subsequent tasks in cases where you are searching for the first affirmative result. By default, the results will match the ordering of the submitted sequence, but if you call `map(...ordered=False)`, then results will be provided to the iterator on a first come first serve basis. Authors ------- * MinRK """ from __future__ import print_function import time import ipyparallel as ipp # create client & view rc = ipp.Client() dv = rc[:] v = rc.load_balanced_view() # scatter 'id', so id=0,1,2 on engines 0,1,2 dv.scatter('id', rc.ids, flatten=True) print("Engine IDs: ", dv['id']) # create a Reference to `id`. This will be a different value on each engine ref = ipp.Reference('id') print("sleeping for `id` seconds on each engine") tic = time.time() ar = dv.apply(time.sleep, ref) for i, r in enumerate(ar): print("%i: %.3f" % (i, time.time() - tic)) def sleep_here(t): import time time.sleep(t) return id, t # one call per task print("running with one call per task") amr = v.map(sleep_here, [0.01 * t for t in range(100)]) tic = time.time() for i, r in enumerate(amr): print("task %i on engine %i: %.3f" % (i, r[0], time.time() - tic)) print("running with four calls per task") # with chunksize, we can have four calls per task amr = v.map(sleep_here, [0.01 * t for t in range(100)], chunksize=4) tic = time.time() for i, r in enumerate(amr): print("task %i on engine %i: %.3f" % (i, r[0], time.time() - tic)) print("running with two calls per task, with unordered results") # We can even iterate through faster results first, with ordered=False amr = v.map( sleep_here, [0.01 * t for t in range(100, 0, -1)], ordered=False, chunksize=2 ) tic = time.time() for i, r in enumerate(amr): print("slept %.2fs on engine %i: %.3f" % (r[1], r[0], time.time() - tic)) ipyparallel-7.1.0/docs/source/examples/joblib.ipynb000066400000000000000000000134771412533144600224260ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Using IPython Parallel as a joblib backend\n", "\n", "[joblib][] is a tool for running tasks, which includes support for implementing custom parallel backends.\n", "IPython defines one such backend, so you can use IPython parallel with joblib.\n", "\n", "[joblib]: https://joblib.readthedocs.io\n", "\n", "The simplest way to set this up is a single call:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [] }, "outputs": [], "source": [ "import ipyparallel as ipp\n", "\n", "ipp.register_joblib_backend()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This registers the 'ipyparallel' backend with all the defaults." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Starting 4 engines with \n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "be0e2704e2204783ae9435d63d6facff", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/1 [00:00\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "dea6ba0c1c7b419eae13a54f7b6c66fb", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/4 [00:00>> print list(mergesort([[1,2,3,4], ... [2,3.25,3.75,4.5,6,7], ... [2.625,3.625,6.625,9]])) [1, 2, 2, 2.625, 3, 3.25, 3.625, 3.75, 4, 4.5, 6, 6.625, 7, 9] # note stability >>> print list(mergesort([[1,2,3,4], ... [2,3.25,3.75,4.5,6,7], ... [2.625,3.625,6.625,9]], ... key=int)) [1, 2, 2, 2.625, 3, 3.25, 3.75, 3.625, 4, 4.5, 6, 6.625, 7, 9] >>> print list(mergesort([[4, 3, 2, 1], ... [7, 6, 4.5, 3.75, 3.25, 2], ... [9, 6.625, 3.625, 2.625]], ... key=lambda x: -x)) [9, 7, 6.625, 6, 4.5, 4, 3.75, 3.625, 3.25, 3, 2.625, 2, 2, 1] """ heap = [] for i, itr in enumerate(iter(pl) for pl in list_of_lists): try: item = next(itr) if key: toadd = (key(item), i, item, itr) else: toadd = (item, i, itr) heap.append(toadd) except StopIteration: pass heapq.heapify(heap) if key: while heap: _, idx, item, itr = heap[0] yield item try: item = next(itr) heapq.heapreplace(heap, (key(item), idx, item, itr)) except StopIteration: heapq.heappop(heap) else: while heap: item, idx, itr = heap[0] yield item try: heapq.heapreplace(heap, (next(itr), idx, itr)) except StopIteration: heapq.heappop(heap) def remote_iterator(view, name): """Return an iterator on an object living on a remote engine.""" view.execute('it%s=iter(%s)' % (name, name), block=True) while True: try: result = view.apply_sync(next, ipp.Reference('it' + name)) # This causes the StopIteration exception to be raised. except RemoteError as e: if e.ename == 'StopIteration': raise StopIteration else: raise e else: yield result # Main, interactive testing if __name__ == '__main__': rc = ipp.Client() view = rc[:] print('Engine IDs:', rc.ids) # Make a set of 'sorted datasets' a0 = range(5, 20) a1 = range(10) a2 = range(15, 25) # Now, imagine these had been created in the remote engines by some long # computation. In this simple example, we just send them over into the # remote engines. They will all be called 'a' in each engine. rc[0]['a'] = a0 rc[1]['a'] = a1 rc[2]['a'] = a2 # And we now make a local object which represents the remote iterator aa0 = remote_iterator(rc[0], 'a') aa1 = remote_iterator(rc[1], 'a') aa2 = remote_iterator(rc[2], 'a') # Let's merge them, both locally and remotely: print('Merge the local datasets:') print(list(mergesort([a0, a1, a2]))) print('Locally merge the remote sets:') print(list(mergesort([aa0, aa1, aa2]))) ipyparallel-7.1.0/docs/source/examples/phistogram.py000066400000000000000000000023471412533144600226430ustar00rootroot00000000000000"""Parallel histogram function""" from ipyparallel import Reference def phistogram(view, a, bins=10, rng=None, normed=False): """Compute the histogram of a remote array a. Parameters ---------- view IPython DirectView instance a : str String name of the remote array bins : int Number of histogram bins rng : (float, float) Tuple of min, max of the range to histogram normed : boolean Should the histogram counts be normalized to 1 """ nengines = len(view.targets) # view.push(dict(bins=bins, rng=rng)) with view.sync_imports(): import numpy rets = view.apply_sync( lambda a, b, rng: numpy.histogram(a, b, rng), Reference(a), bins, rng ) hists = [r[0] for r in rets] lower_edges = [r[1] for r in rets] # view.execute('hist, lower_edges = numpy.histogram(%s, bins, rng)' % a) lower_edges = view.pull('lower_edges', targets=0) hist_array = numpy.array(hists).reshape(nengines, -1) # hist_array.shape = (nengines,-1) total_hist = numpy.sum(hist_array, 0) if normed: total_hist = total_hist / numpy.sum(total_hist, dtype=float) return total_hist, lower_edges ipyparallel-7.1.0/docs/source/examples/pi/000077500000000000000000000000001412533144600205165ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/examples/pi/parallelpi.py000066400000000000000000000037731412533144600232270ustar00rootroot00000000000000"""Calculate statistics on the digits of pi in parallel. This program uses the functions in :file:`pidigits.py` to calculate the frequencies of 2 digit sequences in the digits of pi. The results are plotted using matplotlib. To run, text files from https://www.super-computing.org/ must be installed in the working directory of the IPython engines. The actual filenames to be used can be set with the ``filestring`` variable below. The dataset we have been using for this is the 200 million digit one here: ftp://pi.super-computing.org/.2/pi200m/ and the files used will be downloaded if they are not in the working directory of the IPython engines. """ from __future__ import print_function from timeit import default_timer as clock from matplotlib import pyplot as plt from pidigits import compute_two_digit_freqs from pidigits import fetch_pi_file from pidigits import plot_two_digit_freqs from pidigits import reduce_freqs import ipyparallel as ipp # Files with digits of pi (10m digits each) filestring = 'pi200m.ascii.%(i)02dof20' files = [filestring % {'i': i} for i in range(1, 21)] # Connect to the IPython cluster c = ipp.Client() c[:].run('pidigits.py') # the number of engines n = len(c) id0 = c.ids[0] v = c[:] v.block = True # fetch the pi-files print("downloading %i files of pi" % n) v.map(fetch_pi_file, files[:n]) # noqa: F821 print("done") # Run 10m digits on 1 engine t1 = clock() freqs10m = c[id0].apply_sync(compute_two_digit_freqs, files[0]) t2 = clock() digits_per_second1 = 10.0e6 / (t2 - t1) print("Digits per second (1 core, 10m digits): ", digits_per_second1) # Run n*10m digits on all engines t1 = clock() freqs_all = v.map(compute_two_digit_freqs, files[:n]) freqs150m = reduce_freqs(freqs_all) t2 = clock() digits_per_second8 = n * 10.0e6 / (t2 - t1) print("Digits per second (%i engines, %i0m digits): " % (n, n), digits_per_second8) print("Speedup: ", digits_per_second8 / digits_per_second1) plot_two_digit_freqs(freqs150m) plt.title("2 digit sequences in %i0m digits of pi" % n) plt.show() ipyparallel-7.1.0/docs/source/examples/pi/pidigits.py000066400000000000000000000100011412533144600226740ustar00rootroot00000000000000"""Compute statistics on the digits of pi. This uses precomputed digits of pi from the website of Professor Yasumasa Kanada at the University of Tokoyo: https://www.super-computing.org/ Currently, there are only functions to read the .txt (non-compressed, non-binary) files, but adding support for compression and binary files would be straightforward. This focuses on computing the number of times that all 1, 2, n digits sequences occur in the digits of pi. If the digits of pi are truly random, these frequencies should be equal. """ import os from urllib.request import urlretrieve import numpy as np from matplotlib import pyplot as plt # Top-level functions def fetch_pi_file(filename): """This will download a segment of pi from super-computing.org if the file is not already present. """ ftpdir = "ftp://pi.super-computing.org/.2/pi200m/" if os.path.exists(filename): # we already have it return else: # download it urlretrieve(ftpdir + filename, filename) def compute_one_digit_freqs(filename): """ Read digits of pi from a file and compute the 1 digit frequencies. """ d = txt_file_to_digits(filename) freqs = one_digit_freqs(d) return freqs def compute_two_digit_freqs(filename): """ Read digits of pi from a file and compute the 2 digit frequencies. """ d = txt_file_to_digits(filename) freqs = two_digit_freqs(d) return freqs def reduce_freqs(freqlist): """ Add up a list of freq counts to get the total counts. """ allfreqs = np.zeros_like(freqlist[0]) for f in freqlist: allfreqs += f return allfreqs def compute_n_digit_freqs(filename, n): """ Read digits of pi from a file and compute the n digit frequencies. """ d = txt_file_to_digits(filename) freqs = n_digit_freqs(d, n) return freqs # Read digits from a txt file def txt_file_to_digits(filename, the_type=str): """ Yield the digits of pi read from a .txt file. """ with open(filename, 'r') as f: for line in f.readlines(): for c in line: if c != '\n' and c != ' ': yield the_type(c) # Actual counting functions def one_digit_freqs(digits, normalize=False): """ Consume digits of pi and compute 1 digit freq. counts. """ freqs = np.zeros(10, dtype='i4') for d in digits: freqs[int(d)] += 1 if normalize: freqs = freqs / freqs.sum() return freqs def two_digit_freqs(digits, normalize=False): """ Consume digits of pi and compute 2 digits freq. counts. """ freqs = np.zeros(100, dtype='i4') last = next(digits) this = next(digits) for d in digits: index = int(last + this) freqs[index] += 1 last = this this = d if normalize: freqs = freqs / freqs.sum() return freqs def n_digit_freqs(digits, n, normalize=False): """ Consume digits of pi and compute n digits freq. counts. This should only be used for 1-6 digits. """ freqs = np.zeros(pow(10, n), dtype='i4') current = np.zeros(n, dtype=int) for i in range(n): current[i] = next(digits) for d in digits: index = int(''.join(map(str, current))) freqs[index] += 1 current[0:-1] = current[1:] current[-1] = d if normalize: freqs = freqs / freqs.sum() return freqs # Plotting functions def plot_two_digit_freqs(f2): """ Plot two digits frequency counts using matplotlib. """ f2_copy = f2.copy() f2_copy.shape = (10, 10) ax = plt.matshow(f2_copy) plt.colorbar() for i in range(10): for j in range(10): plt.text(i - 0.2, j + 0.2, str(j) + str(i)) plt.ylabel('First digit') plt.xlabel('Second digit') return ax def plot_one_digit_freqs(f1): """ Plot one digit frequency counts using matplotlib. """ ax = plt.plot(f1, 'bo-') plt.title('Single digit counts in pi') plt.xlabel('Digit') plt.ylabel('Count') return ax ipyparallel-7.1.0/docs/source/examples/progress.ipynb000066400000000000000000001363521412533144600230270ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "id": "e06e6145-5d8a-42f0-a573-883ceefeb456", "metadata": {}, "source": [ "# Using widgets in IPython Parallel\n", "\n", "IPython Parallel 7.1 introduces basic support for using Jupyter widgets from a notebook to engines.\n", "\n", "This allows things like progress bars for incremental stages,\n", "when IPP's own task-level progress doesn't show you enough information.\n", "\n", "As always, we start by creating and connecting to a cluster:" ] }, { "cell_type": "code", "execution_count": 1, "id": "cc0f626a-4e9e-4cd7-b9d5-37aa72656045", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Starting 4 engines with \n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "dfdbae12208c47ea98ef749d45d64f62", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/4 [00:00" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import ipyparallel as ipp\n", "rc = ipp.Cluster(n=4).start_and_connect_sync()\n", "rc.activate()" ] }, { "cell_type": "markdown", "id": "9a68b37e-8f8c-4e21-87ff-ff993d74af4d", "metadata": {}, "source": [ "IPython widgets support updateable readouts of things like progress.\n", "\n", "There are [lots of widgets to choose from][widget list], but for our purposes,\n", "we are going to use `IntProgress`\n", "\n", "[widget list]: https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html" ] }, { "cell_type": "code", "execution_count": 2, "id": "a2ee17a5-ba31-4d3e-9a91-cba386b8e2a9", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "358962d716384d2199b8522907c9033b", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntProgress(value=0, description='Step 0', max=10)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import time\n", "\n", "import ipywidgets as W\n", "from IPython.display import display\n", "\n", "progress = W.IntProgress(min=0, max=10, description=\"Step 0\", readout=True)\n", "display(progress)\n", "\n", "for i in range(10):\n", " progress.value += 1\n", " progress.description = f\"Step {progress.value}/{progress.max}\"\n", " time.sleep(0.1)\n", "\n", "progress.bar_style = \"success\" # change color when it's done" ] }, { "cell_type": "markdown", "id": "6ff1fe0c-adb3-43f7-b89f-f259acc8a72a", "metadata": {}, "source": [ "IPython Parallel supports progress and interactive waits on AsyncResult objects,\n", "which is great when your tasks are small and you have a lot of them:" ] }, { "cell_type": "code", "execution_count": 3, "id": "2d510a7e-69af-4d5f-bdd3-bb35933933bd", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "05a2877edaee4d75bae10bfa7ada3c32", "version_major": 2, "version_minor": 0 }, "text/plain": [ ": 0%| | 0/1000 [00:00" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rc[:].scatter(\"rank\", rc.ids, flatten=True)" ] }, { "cell_type": "code", "execution_count": 6, "id": "307a2f86-0948-461e-94f1-748471707feb", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[output:3]" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e02c6493e62e4ed38d2dfd45b7115667", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntProgress(value=0, description='rank 3: 0', max=10)" ] }, "metadata": { "engine": 3 }, "output_type": "display_data" }, { "data": { "text/plain": [ "[output:2]" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "197376ab6c2247c1af0769eae93220cb", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntProgress(value=0, description='rank 2: 0', max=10)" ] }, "metadata": { "engine": 2 }, "output_type": "display_data" }, { "data": { "text/plain": [ "[output:0]" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "dd5ef98076d94493b57b021455162c56", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntProgress(value=0, description='rank 0: 0', max=10)" ] }, "metadata": { "engine": 0 }, "output_type": "display_data" }, { "data": { "text/plain": [ "[output:1]" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "697cf15dec6447beaa227add1fa986e8", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntProgress(value=0, description='rank 1: 0', max=10)" ] }, "metadata": { "engine": 1 }, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "8b5ac39bd6114df9b1820ff1402e5a6c", "version_major": 2, "version_minor": 0 }, "text/plain": [ "%px: 0%| | 0/4 [00:002 # # # Eigenvalue distribution of Gaussian orthogonal random matrices # # The eigenvalues of random matrices obey certain statistical laws. Here we construct random matrices # from the Gaussian Orthogonal Ensemble (GOE), find their eigenvalues and then investigate the nearest # neighbor eigenvalue distribution $\rho(s)$. # from rmtkernel import ensemble_diffs, normalize_diffs, GOE import numpy as np import ipyparallel as ipp # # ## Wigner's nearest neighbor eigenvalue distribution # # The Wigner distribution gives the theoretical result for the nearest neighbor eigenvalue distribution # for the GOE: # # $$\rho(s) = \frac{\pi s}{2} \exp(-\pi s^2/4)$$ # def wigner_dist(s): """Returns (s, rho(s)) for the Wigner GOE distribution.""" return (np.pi*s/2.0) * np.exp(-np.pi*s**2/4.) # def generate_wigner_data(): s = np.linspace(0.0,4.0,400) rhos = wigner_dist(s) return s, rhos # s, rhos = generate_wigner_data() # plot(s, rhos) xlabel('Normalized level spacing s') ylabel('Probability $\rho(s)$') # # ## Serial calculation of nearest neighbor eigenvalue distribution # # In this section we numerically construct and diagonalize a large number of GOE random matrices # and compute the nerest neighbor eigenvalue distribution. This comptation is done on a single core. # def serial_diffs(num, N): """Compute the nearest neighbor distribution for num NxX matrices.""" diffs = ensemble_diffs(num, N) normalized_diffs = normalize_diffs(diffs) return normalized_diffs # serial_nmats = 1000 serial_matsize = 50 # %timeit -r1 -n1 serial_diffs(serial_nmats, serial_matsize) # serial_diffs = serial_diffs(serial_nmats, serial_matsize) # # The numerical computation agrees with the predictions of Wigner, but it would be nice to get more # statistics. For that we will do a parallel computation. # hist_data = hist(serial_diffs, bins=30, normed=True) plot(s, rhos) xlabel('Normalized level spacing s') ylabel('Probability $P(s)$') # # ## Parallel calculation of nearest neighbor eigenvalue distribution # # Here we perform a parallel computation, where each process constructs and diagonalizes a subset of # the overall set of random matrices. # def parallel-diffs(rc, num, N): nengines = len(rc.targets) num_per_engine = num/nengines print "Running with", num_per_engine, "per engine." ar = rc.apply_async(ensemble_diffs, num_per_engine, N) diffs = np.array(ar.get()).flatten() normalized_diffs = normalize_diffs(diffs) return normalized_diffs # client = ipp.Client() view = client[:] view.run('rmtkernel.py') view.block = False # parallel-nmats = 40*serial_nmats parallel-matsize = 50 # %timeit -r1 -n1 parallel-diffs(view, parallel-nmats, parallel-matsize) # pdiffs = parallel-diffs(view, parallel-nmats, parallel-matsize) # # Again, the agreement with the Wigner distribution is excellent, but now we have better # statistics. # hist_data = hist(pdiffs, bins=30, normed=True) plot(s, rhos) xlabel('Normalized level spacing s') ylabel('Probability $P(s)$') ipyparallel-7.1.0/docs/source/examples/rmt/rmt.ipynb000066400000000000000000001306031412533144600225600ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Eigenvalue distribution of Gaussian orthogonal random matrices" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The eigenvalues of random matrices obey certain statistical laws. Here we construct random matrices \n", "from the Gaussian Orthogonal Ensemble (GOE), find their eigenvalues and then investigate the nearest\n", "neighbor eigenvalue distribution $\\rho(s)$." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from rmtkernel import ensemble_diffs, normalize_diffs, GOE\n", "import numpy as np\n", "import ipyparallel as ipp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Wigner's nearest neighbor eigenvalue distribution" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Wigner distribution gives the theoretical result for the nearest neighbor eigenvalue distribution\n", "for the GOE:\n", "\n", "$$\\rho(s) = \\frac{\\pi s}{2} \\exp(-\\pi s^2/4)$$" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "def wigner_dist(s):\n", " \"\"\"Returns (s, rho(s)) for the Wigner GOE distribution.\"\"\"\n", " return (np.pi*s/2.0) * np.exp(-np.pi*s**2/4.)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "def generate_wigner_data():\n", " s = np.linspace(0.0,4.0,400)\n", " rhos = wigner_dist(s)\n", " return s, rhos" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "s, rhos = generate_wigner_data()" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<matplotlib.text.Text at 0x3828790>" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\nSUVORK5CYII=\n" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plot(s, rhos)\n", "xlabel('Normalized level spacing s')\n", "ylabel('Probability $\\rho(s)$')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Serial calculation of nearest neighbor eigenvalue distribution" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this section we numerically construct and diagonalize a large number of GOE random matrices\n", "and compute the nerest neighbor eigenvalue distribution. This comptation is done on a single core." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "def serial_diffs(num, N):\n", " \"\"\"Compute the nearest neighbor distribution for num NxX matrices.\"\"\"\n", " diffs = ensemble_diffs(num, N)\n", " normalized_diffs = normalize_diffs(diffs)\n", " return normalized_diffs" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "serial_nmats = 1000\n", "serial_matsize = 50" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 1: 1.19 s per loop" ] } ], "source": [ "%timeit -r1 -n1 serial_diffs(serial_nmats, serial_matsize)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "serial_diffs = serial_diffs(serial_nmats, serial_matsize)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The numerical computation agrees with the predictions of Wigner, but it would be nice to get more\n", "statistics. For that we will do a parallel computation." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<matplotlib.text.Text at 0x3475bd0>" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\nvDyaNWtGcHAwzZs3N7u/dUNyat2XJSk81FTR/rToO+4JIYQwHbM+JCWEEMJ8SMEQQghhECkYQggh\nDCIFQwghhEGkYIhKZWNjQ3BwsP75v//977/cArTq+fv7c/jwYQACAwNJTU2t0PrCw8MZOnSowa9X\nxbaq0tWrV3nmmWdMuk1hmaRgiEpVs2ZNvv/+e5KSkgDjr8TNy8urcIZ7t7l161bs7e0rvE5r1qJF\nCzZu3Kh1DGEBpGCISmVnZ8cLL7zAokWLii27evUqM2fOxNPTk1mzZnHjxg0AJkyYwMsvv4yvry+v\nvvoqEydO5JVXXsHHx4f27dtz5MgRXnjhBTp06MC8efP06/v73/9O165d6dGjB6tXry4xj7OzM0lJ\nSXz00Ud4e3vj7e1NmzZtePzxx4GCecDGjRuHr68vc+bM0V+RGxsbS9++ffH29mb79u1lfu6MjAw+\n/PBD/Pz8CAwMJDw8HIDu3bsXuZq2cO8nMzOzxPaluXz5MoMGDcLLywtPT0/OnTtHXFwc7u7uTJ48\nGTc3N+bPn6/P//bbb+Pj40PXrl1ZsGBBkfW88soreHt706VLFy5cuEBcXBwdO3YE4IsvvmD06NEM\nHjwYDw8Pli5dqn/vtm3b6N69Oz4+Prz00ktMnz69WM6jR4/St29fvLy86Ny5M7dv3y6z74QFqcjU\nuUL8Vb169VRqaqpydnZWKSkp6t///reaN2+eUkqpWbNmqffff18ppdSCBQvU7NmzlVJKjR8/Xvn5\n+emn3J4wYYIaNGiQysrKUl988YWqV6+eCg8PV1lZWcrNzU0/dXxycrJSSqmsrCzl6+urbt++rZRS\nyt/fXx06dEgppZSzs7NKSkrS58vJyVG9e/dWoaGh+ra3bt1SSik1e/Zs9c033yillOrUqZOKjo5W\nt2/fVgMHDixxeujdu3fr7zPw+eefqyVLliillLp+/bry8fFRSim1aNEiNXfuXKWUUlevXlXt27e/\nb/t713mvuXPnqk8++UT/GTIyMtSFCxeUTqdT3333ncrMzFRPP/20+vbbb4v0TW5urho6dKg6deqU\nvq9XrFih77c7d+6oCxcu6KeS//zzz1WTJk3U1atXVWpqqnrooYdUdna2ysnJUc7OzurChQsqKSlJ\nde7cWU2fPr1YzvHjx6uwsDClVME04Lm5ucXaCMslexii0tWvX59x48YV+dcpwM8//8ykSZMAmDx5\nMlu2bAEKDiGNGDGC+vXr69uOGDGCmjVr0r17dxo2bIifnx81a9bE29tbPxPwzp07CQwMxNvbm/Pn\nz/PLL7+UmW3GjBn07duXwMBADh06xLFjx/D398fb25vQ0FAiIyO5cuUKSil8fHyoW7cuo0aNKnO6\n6k2bNrF69Wq8vb0ZOHAgN27c4MKFC4wcOZJvv/0WgA0bNujHCkpqf/78+VLX37VrVxYvXsx7771H\ncnIyDzzwAFAwLc2wYcOoVasWY8aMYdu2bQAcPHiQ4cOH06lTJw4fPsyOHTvIzs5m9+7dTJkyBSg4\nfFi7du1i2xowYADNmzenfv36uLu7c/jwYaKioujYsSPOzs44ODjwxBNPlNgn3bt3Z86cOSxfvpzc\n3FxsbW3L/J0Iy2H2U4MIy/TSSy/RuXNnJk6cWOT10r54mzdvXuR54fxcNWvWpGHDhvrXa9asSXZ2\nNmlpacyZM4c9e/bw4IMPMmzYMG7evHnfTF988QWXL19m5cqVQME01R4eHuzevbtIu/j4eMM+5D3y\n8/NZsWIFffr0KbascePG/P7772zYsEE/NUNp7Qun6/irwMBAunTpwpo1a+jZsycbN24s0i+FCsdv\npk+fzrfffouHhwezZs3i5s2b6HS6Emco/au/9ndmZiY1atQoMjZU2jqCgoLo37+/fiqS6OhomjZt\net/tCcshexiiSjRq1IiRI0fy6aef6r9oBg8ezJdffkl+fj6fffYZTzzxRLnWrZTi1q1b2NnZ0axZ\nM06fPs2uXbvu+55Dhw6xcOFCvvrqK/1rXbt25caNG/o9lvT0dM6cOcNDDz2Era0tsbGxpKens2HD\nhjIzjR07lpCQENLS0gCKTHk9atQo3nvvPVJTU/Hw8CizfUkuXLign7uob9+++nGRlJQUfvjhB7Ky\nsli/fj0DBw4kMzOTtLQ0nJ2duXLlCps3bwYKxpcee+wxVq9ejVKKrKwsMjIyyvxsOp2Obt268fvv\nvxMXF0dycjKhoaElntBw7tw5XFxc+N///V9cXV3N4l4lovJIwRCV6t4vkVdeeYXExET98+DgYC5d\nuoS3tzc3btzg5ZdfLvF9f31e0rKWLVsyfPhwPDw8mDZtWqmnohb+q3rFihXcvHmTxx57DG9vb154\n4QUAvvrqK1atWkWnTp3o0aMHf/zxBwAff/wxr732Gr169cLT07PEL0edTqd/fcSIEfj4+BAQEICH\nhwdz587VtxsxYgTr169n5MiRRV4rqf2967zXhg0b8PDwoGvXrty5c0e/LldXV3788Ue8vLzw8PAg\nMDCQBx54gDlz5uDj48OoUaMYPHiwfj3vvPMOZ8+exdPTk549e+pPPCjcZmnbt7W1Zfny5YwaNYqB\nAwfSsWNH2rRpU6zdkiVL6NixIz4+Pri6utKjR48Sfy/CMsnkg0JYqLi4OIYOHcrvv/9uku2lp6dT\nt25dUlJSGDJkCJ988gnt27c3ybaFeZAxDCEsmCnvODdv3jzCwsKws7Pj2WeflWJRDckehhBCCIPI\nGIYQQgiDSMEQQghhECkYQgghDCIFQwghhEGkYAghhDCIFAwhhBAG+X8LAxP1Be5fBAAAAABJRU5E\nrkJggg==\n" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "hist_data = hist(serial_diffs, bins=30, normed=True)\n", "plot(s, rhos)\n", "xlabel('Normalized level spacing s')\n", "ylabel('Probability $P(s)$')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parallel calculation of nearest neighbor eigenvalue distribution" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we perform a parallel computation, where each process constructs and diagonalizes a subset of\n", "the overall set of random matrices." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "def parallel_diffs(rc, num, N):\n", " nengines = len(rc.targets)\n", " num_per_engine = num/nengines\n", " print \"Running with\", num_per_engine, \"per engine.\"\n", " ar = rc.apply_async(ensemble_diffs, num_per_engine, N)\n", " diffs = np.array(ar.get()).flatten()\n", " normalized_diffs = normalize_diffs(diffs)\n", " return normalized_diffs" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "client = ipp.Client()\n", "view = client[:]\n", "view.run('rmtkernel.py')\n", "view.block = False" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [ "parallel_nmats = 40*serial_nmats\n", "parallel_matsize = 50" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running with 10000 per engine.\n", "1 loops, best of 1: 14 s per loop" ] } ], "source": [ "%timeit -r1 -n1 parallel_diffs(view, parallel_nmats, parallel_matsize)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 } ipyparallel-7.1.0/docs/source/examples/rmt/rmtkernel.py000066400000000000000000000022571412533144600232730ustar00rootroot00000000000000# ------------------------------------------------------------------------------- # Core routines for computing properties of symmetric random matrices. # ------------------------------------------------------------------------------- import numpy as np ra = np.random la = np.linalg def GOE(N): """Creates an NxN element of the Gaussian Orthogonal Ensemble""" m = ra.standard_normal((N, N)) m += m.T return m / 2 def center_eigenvalue_diff(mat): """Compute the eigvals of mat and then find the center eigval difference.""" N = len(mat) evals = np.sort(la.eigvals(mat)) diff = np.abs(evals[N / 2] - evals[N / 2 - 1]) return diff def ensemble_diffs(num, N): """Return num eigenvalue diffs for the NxN GOE ensemble.""" diffs = np.empty(num) for i in range(num): mat = GOE(N) diffs[i] = center_eigenvalue_diff(mat) return diffs def normalize_diffs(diffs): """Normalize an array of eigenvalue diffs.""" return diffs / diffs.mean() def normalized_ensemble_diffs(num, N): """Return num *normalized* eigenvalue diffs for the NxN GOE ensemble.""" diffs = ensemble_diffs(num, N) return normalize_diffs(diffs) ipyparallel-7.1.0/docs/source/examples/task_mod.py000066400000000000000000000002361412533144600222620ustar00rootroot00000000000000import numpy from numpy.linalg import norm def task(n): """Generates a 1xN array and computes its 2-norm""" A = numpy.ones(n) return norm(A, 2) ipyparallel-7.1.0/docs/source/examples/task_profiler.py000066400000000000000000000044111412533144600233240ustar00rootroot00000000000000#!/usr/bin/env python """Test the performance of the task farming system. This script submits a set of tasks via a LoadBalancedView. The tasks are basically just a time.sleep(t), where t is a random number between two limits that can be configured at the command line. To run the script there must first be an IPython controller and engines running:: ipcluster start -n 16 A good test to run with 16 engines is:: python task_profiler.py -n 128 -t 0.01 -T 1.0 This should show a speedup of 13-14x. The limitation here is that the overhead of a single task is about 0.001-0.01 seconds. """ import random import time from optparse import OptionParser import ipyparallel as ipp def main(): parser = OptionParser() parser.set_defaults(n=100) parser.set_defaults(tmin=1e-3) parser.set_defaults(tmax=1) parser.set_defaults(profile='default') parser.add_option("-n", type='int', dest='n', help='the number of tasks to run') parser.add_option( "-t", type='float', dest='tmin', help='the minimum task length in seconds' ) parser.add_option( "-T", type='float', dest='tmax', help='the maximum task length in seconds' ) parser.add_option( "-p", '--profile', type='str', dest='profile', help="the cluster profile [default: 'default']", ) (opts, args) = parser.parse_args() assert opts.tmax >= opts.tmin, "tmax must not be smaller than tmin" rc = ipp.Client() view = rc.load_balanced_view() print(view) rc.block = True nengines = len(rc.ids) # the jobs should take a random time within a range times = [ random.random() * (opts.tmax - opts.tmin) + opts.tmin for i in range(opts.n) ] stime = sum(times) print( "executing %i tasks, totalling %.1f secs on %i engines" % (opts.n, stime, nengines) ) time.sleep(1) start = time.perf_counter() amr = view.map(time.sleep, times) amr.get() stop = time.perf_counter() ptime = stop - start scale = stime / ptime print("executed %.1f secs in %.1f secs" % (stime, ptime)) print("%.3fx parallel performance on %i engines" % (scale, nengines)) print("%.1f%% of theoretical max" % (100 * scale / nengines)) if __name__ == '__main__': main() ipyparallel-7.1.0/docs/source/examples/throughput.py000066400000000000000000000031311412533144600226670ustar00rootroot00000000000000from __future__ import print_function import time import numpy as np import ipyparallel as parallel nlist = map(int, np.logspace(2, 9, 16, base=2)) nlist2 = map(int, np.logspace(2, 8, 15, base=2)) tlist = map(int, np.logspace(7, 22, 16, base=2)) nt = 16 def wait(t=0): import time time.sleep(t) def echo(s=''): return s def time_throughput(nmessages, t=0, f=wait): client = parallel.Client() view = client.load_balanced_view() # do one ping before starting timing if f is echo: t = np.random.random(t / 8) view.apply_sync(echo, '') client.spin() tic = time.time() for i in range(nmessages): view.apply(f, t) lap = time.time() client.wait() toc = time.time() return lap - tic, toc - tic def do_runs(nlist, t=0, f=wait, trials=2, runner=time_throughput): A = np.zeros((len(nlist), 2)) for i, n in enumerate(nlist): t1 = t2 = 0 for _ in range(trials): time.sleep(0.25) ts = runner(n, t, f) t1 += ts[0] t2 += ts[1] t1 /= trials t2 /= trials A[i] = (t1, t2) A[i] = n / A[i] print(n, A[i]) return A def do_echo(n, tlist=[0], f=echo, trials=2, runner=time_throughput): A = np.zeros((len(tlist), 2)) for i, t in enumerate(tlist): t1 = t2 = 0 for _ in range(trials): time.sleep(0.25) ts = runner(n, t, f) t1 += ts[0] t2 += ts[1] t1 /= trials t2 /= trials A[i] = (t1, t2) A[i] = n / A[i] print(t, A[i]) return A ipyparallel-7.1.0/docs/source/examples/visualizing-tasks.ipynb000066400000000000000000012041161412533144600246450ustar00rootroot00000000000000{ "cells": [ { "cell_type": "markdown", "id": "c12b2b6a-f643-4cb9-b5b4-dca60086a8e7", "metadata": {}, "source": [ "# Visualizing an AsyncResult in progress" ] }, { "cell_type": "code", "execution_count": 1, "id": "91255a04-00e0-44d5-8817-e5346226b4ca", "metadata": {}, "outputs": [], "source": [ "import ipyparallel as ipp" ] }, { "cell_type": "code", "execution_count": 2, "id": "4bd7ec9e-285a-4e40-bfdf-89290a5e0e35", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using existing profile dir: '/Users/minrk/.ipython/profile_default'\n", "Starting 8 engines with \n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "a88e9002f12849979a16528e113e9a54", "version_major": 2, "version_minor": 0 }, "text/plain": [ " 0%| | 0/8 [00:00" ] }, "metadata": { "image/png": { "height": 384, "width": 612 }, "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "from matplotlib import pyplot as plt\n", "\n", "df.plot.barh(y=['prepare', 'schedule', 'compute', 'reply'], stacked=True, width=1)\n", "plt.grid((True, False))\n", "yticks = [0, len(df) // 2, len(df)]\n", "plt.yticks(yticks, yticks);\n", "plt.xlabel(\"seconds\")\n", "plt.ylabel(\"tasks\")\n", "# plt.ylim([0, len(df)])" ] }, { "cell_type": "markdown", "id": "4e41ad4e-9af8-4a46-a745-2f0aa4af87eb", "metadata": {}, "source": [ "In this visualization, the blue indicates the time spent in the client preparing the tasks to be submitted.\n", "It takes 200ms to prepare and send all of these mesages.\n", "\n", "The blue area is mostly pure overhead of IPython parallel,\n", "and should be minimized. The same goes for red, which is the same thing, just in the other direction - delivering completed results from engines to the client.\n", "\n", "The orange indicates scheduling time - the time tasks are waiting in queues before starting.\n", "There are two contributors to this:\n", "\n", "1. available compute (if there are more tasks than engines, tasks need to wait). Allocating more engines can help with this.\n", " The optimal scenario here is one engine per task, which would mean no task ever has to wait for busy engines.\n", " On the other hand, your engines will probably spend a lot of time idle, so that's lost of wasted resources.\n", "2. Scheduler overhead - There's always a cost to delivering messages.\n", " There is currently no separate measure of when a task has been *assigned* to an engine,\n", " so it is difficult to measure this one except by comparing the time between completing one task and starting the next\n", " \n", "The green is time spent actually working on tasks. This is all the 'real' work. In a perfect world, the graph would be all green, but that's never going to happen.\n", "\n", "The little bits of red are the overhead of delivering results back to the client.\n", "That cost is usually very low." ] }, { "cell_type": "markdown", "id": "cca4b6af-36a4-4aca-8e45-7536bf43f2f9", "metadata": {}, "source": [ "We can also visualize the work on the engines,\n", "which should look like a solid block of color if there's no wasted time.\n", "\n", "The left and right edges are the times when the first task was created and the last result was received at the client (the total effective computation time).\n", "\n", "The y axis is the engine id, and the x axis is time since the beginning of the task.\n", "Space in the graph indicates idle engines." ] }, { "cell_type": "code", "execution_count": 7, "id": "d1d7a1ef-a541-4f17-b6ef-9bb9bd3a0138", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'engine id')" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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5cHfPphUO/2H9WfIvkP48vUv+XLSlxQuKKTLGl3YbK/NOOmhk3j9zte57cjbmnUfipcV88LfXmD/v+zPwBED+fK1L5tVj6SE+fPnM378+KTKdu7cmZtvvpnS0lLGjBnDlClTALjooot44YUXOPDAAzn44IO59957ARg+fDjDhw/nyCOPrPyANn36dMaPH88WW2xR+UttdnY2t956KxMmTODWW29tgXsptT1ZWVmcdNJJfPbZZ7z22mu19m+66aYUFRXV2n7cccdx8803c+mll/Lzn/88qbYuu+wyNthgA6666irOOuusyu2/+c1vuPbaa7nhhhvYfffdm3U/ioqKuPfeeznxxBNZb731+OKLL5pVj5RKu+yyCwMHDuS2225j9913Z+zYsZx//vkUFxenu2tSm9HYeWqrrbbilltu4YMPPmC33XZj9uzZ1fbn5CQf7mor5ymH0EpKmS0P3ZKO3Tsy6fZJtfb17N+TA648gMXzFnPZFpdx9yl3c/+Z9/OHzf7AnC/msMvZuzBw64FJtfPmPW9yVu+zuH6v67n7lLt55IJHuOPYO7hw3Qv57qPv2PqIrVln+DrNvh+T/z2Z0pJSxvx6TLPrkNR0BxxwAKutthr33HNPZfAOEh98LrzwQgB+9atfVW7v378/AG+88Ubltoq/K/YBnHfeeay33nocf/zxzepX//79iTFy2223sf7663PPPfcwe/ZsSktLq81ZtMsuu/DEE0/www8/sGzZMr744gv+8pe/0LVr11p1Dh48mP/85z+VwzTmzJnDlClTuPrqq6t94LztttuIMVa7PxWSHfpy2223MXHiRADGjRtXbShzRf9zc3P5zW9+w5QpUygoKGDJkiVMnz6dRx55hB133DHpxyo/P59zzjmHN998k4ULF7Jo0SI++ugj/va3v7HaaqtVK7vGGmtw3XXXMX36dIqKipgzZw4PPvggm2++ea16x44dS4yRsWPHstNOO/HSSy+xaNEi5syZw6233lr5GA8ZMoTHHnuMgoICFi1axKOPPlrnY1cxD0+7du34wx/+wJdffln5P/v9739Pbm5utfJVnwN1qTmvTzKPeYVDDjmEF154gYKCAgoLC/noo4+44IILaNeuXZ1tHXzwwbz11lssXbqU2bNnc8cdd7DmmmvWWbYhO++8M/369asMiNdUV/AO4L777gNg/fXXT6qdDh06cMQRR7B48eJaz9WK//9uu+3GgAEDqu078sgjefXVV5kzZw6FhYXMnDmTp59+uloWboV77rmHrKwsjjnmmKT6JLW0ivPNzTffzF133UXv3r3Zb7/9Wqw9z1Oep1bF89Rf/vIXcnJyOPzww2sF7wBKSkqSaqctnafMwFO9Vlt/NUYdM4qNdtqIHv170L5LexZ+v5CPnvmIxy95nPnfzq91zNZHbs3oE0ez2vqr0b5zexb9sIhZH81i0q2TeOu+txi03SDOmvhTRPumeFPl35Nun8S/jv5Xvf2pWvayGZdV/j13xtzKobj9Nu/H1kduzaAxg+ixdg/adWhHwdcFTP3vVJ7845Msnb+0Wp3ZudmMPmk0I48aSc8BPcnNy2XhnIV88943TPj7BD55/pNGH6ddzt6F/f68H1++9iXX73U9S39c2mD59bZZj13P2ZW1h65Np96dWPrjUubNmMeHT33I45c8Xq1sbn4uO562I1scvAWrrb8aMUa+e/87Xrj2Bd68p/ocABWP7WPjHuPdR95l30v3Zd1R65LTLocZb87g4fMerjObrPNqndn3sn3ZbM/NaN+lPbM/nc1zVz9HwVcFjd73mkYdO4riomLefeTdWvtGHjOS3Pa5PPPnZ6oNf146fylPXfYUY28dy+iTRvPl641nvJUU1f1mvGzRMj565iP6bNyH1dZfjRlvzqjc12tAL3Y7dzc22GEDuvXtRnFhMfO/nc8Xr37Boxc8ypKCJZVlF8xawBcvf8HmB2zOf379H5YtWpb8gyCpmry8PA477DD69evHkiVLmDp1Ki+99BJlZWW1yu6www4APP3007X2vfTSSyxZsoSRI0fSrl07li9fzsyZiUzfYcOG8emnnwKwxRZbAPDVV18BsPHGG3PhhRdy5plnVpZvrnXXXZfJkyfz2Wefcdddd5Gfn8/ChQuBRJbgJZdcwrx583j88ceZM2cOm222Gb/97W/ZY489GDFiBIsWLQISX4omT55MjJH//ve/TJ8+nS5durDeeuvx61//mgsvvDDpD53JeOSRRwA46qijmDhxYuUHdoAZM2YAcPvtt3PooYfy/vvvc8cdd1BYWEifPn3YZptt2G233Xj++ecbbadbt25MmDCBIUOG8Mknn3DrrbeyfPly1l13XY455hgeeugh5syZA8A666zDK6+8Qt++fXn++ee5++67WXvttTnwwAP5+c9/zv77788TTzxRq429996bPffck8cff5wbb7yRkSNHcvTRRzNgwADOPfdcnn/+eV5++WX++c9/MnjwYPbee2/WXXddBg8eXOfE2ffddx/Dhw/ngQceoLi4mH322acyg3Pvvfdu+oNdLpnHHOCWW27h2GOP5euvv+ahhx5i/vz5bL311vzxj39kxx13ZOedd6a0tLSy/Omnn87VV1/Njz/+yB133MH8+fPZddddmTRpEgsWLGhSH3faaScAXnnllSYdt9deewEwderUpMqPGDGCDh068Mwzz9QaOh9j5Nlnn+XEE09k++23rxyedOmll3L++efz5Zdfct9997FgwQLWXHNNhg8fzoEHHlgZRKzwxhtvsHz5cnbeeWfOP//8Jt0fKdVWW2019t57bz799FNee+01Fi5cyFlnncUJJ5xQ67mbap6nGuZ56idt/TzVt29fRo8ezZtvvsmHH37ImDFjGDZsGDFG3n333SYtmNGWzlMG8FSvob8YyuiTRvPphE+ZNmkaJctL6LNJH0YdN4rN9tqMy7a4jPnfza8sv++l+7L7+bvzw5c/MOW+KRQuKKTrml3pP7w/mx+4OW/d9xbzZszjsXGPsePpiV9Jnr/mpzfar9/9usH+PDbuMYbsO4S1h6zN89c8XxmMK5xfWFlmm+O3Yeh+Q/nsxc/45LlPyMrOot/m/dj5rJ3ZZPdNuHyryyla/NMvykfdfhRbHrol377/La/f8TrFhcV07dOV9bZZj01226TBAF4IgYOuOYgdTt2Bdx56h1sOvaXewFKFTXbdhFOeOIXChYVM/e9U5n87nw49OrDmRmuy3a+3qxbAy++az5kvnEm/zfvx1ZSvmHTrJEJWYONdN+a4u4+jzyZ9ePSiR2u10X+L/ux6zq58+dqXvHLLK/To14PN99+cM54/gz8O+SOzP/vp14mOPTryu0m/o/e6vfn85c/54pUv6LpmVw678TA+evajBu9LTe27tKf/Fv356s2vKC6sPTxgwx02BODDpz+ste+Dpz4AYIMdNmhSmzXl5udW1vHt+99Wbu+yRhfOe/M88rvk8/6T7/POg++Q0z6HXgN6sfURWzPxuonVAngA016dxgbbb8D6o9fn/SfeX6F+SauyNddck3//+9/Vtn355ZccffTRvPTSS9W2b7BB4vX72Wef1aqntLSU6dOns+mmmzJw4EA++eQT3nzzTaZMmcJNN93EyJEjK+fAe+ONN3jrrbfIysri1ltv5fXXX+eGG25Y4fuy7bbbctlll3HBBdXnbx0zZgyXXHIJkyZNYo899qj2AXXs2LHcfvvtjB8/njPPPLNyW35+Pvvssw///e9/q9XVrVs3li5t+Iegpnr00UeZP39+5Yf0mkOau3TpwiGHHMJbb73FVlttVSu42qNHj6Tauf766xkyZAj/93//x8knn1ztg3OnTp2qLUJy44030rdvXy644AIuu+ynH+VuuOEGXnrpJf71r3/Rv39/liyp/t689957s+OOO1Y+d0IIPPPMM+y88848+eSTnHDCCfznP/+pLF/xxWOvvfaq9VgDbLTRRmyyySbMnz8fgAsuuIAJEyaw1157cfjhh9d67iarscccEs+DY489loceeojDDjuMZct++rHo4osvZty4cZx88slce+21QCK74vLLL6egoIDNN9+8Mkh93nnncf/997P//vs3qY/bbLMNAG+99VaD5Y499ljWWmstOnXqxODBg9lpp52YMWMG5557blLtNPS6Bvj8888Bqk3yf+KJJ/LNN9+w6aabUlhYWK18z549a9WxbNkyPvzwQ4YOHUqnTp3qnWNTWhmOPvpo2rVrx+233w7Ahx9+yJQpU9h+++1Zd911mTZtWou17XmqYZ6nftLWz1MVC0J8/vnnvPDCC2y//fbV9k+dOpVf/OIXSb3e2tJ5ygCe6jX5zsk8f/XzlCyvHpTaaOeNOPWpU9njwj34z69/evPZ9sRt+fGbHxm/6fhaAZyOPTsCMO+reTw+/nFGHDUCgMfHV884a8jj4x+n5zo9KwN4dS1i8fSfnubuk+8mllWPto86ZhRH/vNIxvx6DM/85RkgEXDa4pAt+Oqtr/jTVn+qdUzHHh3r7UtOXg7H3nUsm++/ORP+PoF7T7s3qQj/NsdvQ1Z2FleNuYpvpn5Tvb2e1ds76JqD6Ld5Px4850GeveLZam3/+pFfs9v5uzHlgSl88171ejbbczNuP+p2XvvXT/MEbHvCthx+0+HscNoO3H3y3ZXb9/vTfvRetzfPXf0c9595f+X2iddN5Hev/a7R+1PVuiPWJTsnm6/e+qrO/RWrulYNIFZY+P1Cli1eRo+1e5Cbn1tnALAuvdftzVaHb0VWdhZdVu/Cpj/flO59u/PUZU9VC+ANO2AYnXp24t7T7uWFa1+oVke7Du1q/e+Byuw9A3hS89122228/PLLfPjhhyxatIiBAwdyyimncMIJJ/DUU08xYsSIalk8FcNL6vuFtmJ7t27dACgrK2Ovvfbi6quv5qCDDiLGyAMPPMAZZ5xBjJGzzjqLwYMH87Of/Yxu3brx97//nX322Yfc3FyeffZZfvWrXzVpQYvvv/++zg+4p556KpAYMlWz7//617847bTTOOywwyq/GFWo+YEPqPyAvjLFGMnKyqKoqKjOzMiCgsYzsnv37s3BBx/Md999x9lnn13rnFj1g2rfvn3Zdddd+eqrr/jLX/5Srdxrr73G3XffzRFHHMEvfvGLWpNP33333dUCvzFG7rzzTnbeeWc++OCDal+KAO644w6OPfZYhgwZUucXoz/84Q/VHvOioiLOO+88Jk6cyDHHHNPsL0bJOO200yguLuaYY46p9qWool+nnHIKhx12WOUXo8MOO4y8vDwuv/zyyi9FkHgMfvvb37LvvvuSnZ2ddPv9+vVj+fLljf5/jzvuOLbeeuvK22+88QaHHnpo0kGIpr6uKxQXF1fL6qgwb17di5h9//33DB06lL59+1Zm5ErpcNxxx1FaWsodd9xRue32229n2LBhHHfccZx33nkt1rbnqfp5nmq61nyeqhjufNBBBzF37lz2228/nn/+eXr37s3FF1/MkUceyRNPPMHgwYMbnXuyLZ2nDOCpXlWz66r6+H8f892H37HxrhvX2ldaXEosrR0MWTJvSa1tLaFgZt1v3q/e+ioHXnUgG++6cWUAj5iYGLO4qLjOAE7NjKwKHbp34OT/nszAkQN56HcP/VRfEywvXF67vSqPUcceHdnq8K2Y8eaMasE7SAwffeh3D3HRbhex5aFb1grgffHKF9WCd5C4/7+87pcM2PKncftZOVlsediWFC4s5LFxj1Ur/9WUr5h812RGHjUy6fvUo1/il68Fs+p+48vvmg9A4YLaHwQqtrfv1J78rvlJB/BWW2819hq3V+Xt4qJiHjj7Af731//VWb6uepcvrf2/AFjwfeJ+VNwvSU13ySWXVLv94Ycf8qtf/YrFixdz9tlnM27cOH7xi18kXV8IAaDah+5Zs2ZxyCGH1Cq73nrrMX78eC666CK++OILHn74YcaMGcPJJ5/MwoULue6663jooYeqBSYa895777F8ee33jBEjRrB8+XIOPPDAOo9r164dq622Gj169KCgoIB7772X0047jUceeYQHHniA5557jldffZUvv0zPojmLFi3iv//9L3vvvTfvvvsuDz74IC+//DKTJ0+u88tbXYYPH052djYvvfRSo5kZQ4cOBeDll1+ucwjWCy+8wBFHHMHQoUNrfTGq61f4iiBs1XkTK3z7beLHnLXWWqvOvrz44ou1tr388ssUFxdX9rMl5Ofn87Of/Yy5c+dy+umn11mmqKiIjTbaqPJ2xZxLdfV5+vTpfP3116yzzjpJ96Fnz578+OOPjZYbMSLxo2uPHj3YfPPNufTSS5kyZQoHH3wwzzzT9M9ANdX1ur7rrrs49dRT+fDDD7n//vt58cUXK4ci1qfiC16vXr0M4CltdthhB9Zbbz2efvrpaj8Q/ec//+HKK6/kqKOO4qKLLkrp8NOqPE/Vz/NU07T281RFIDAnJ4fjjjuucjjzokWLGDt2LBtttBHDhw9n//3355577km6zbq0pvOUATw1aKvDtmLEUSNY62dr0aF7B7JzfoqYFxdVD4a8cdcb7HDqDlz84cVMuX8Kn7/4OdNem8ayhStv/rCsnCxGnzia4YcMZ82N1yS/az5Z2T+lQnfr263y72WLlvHef9/jZ3v/jAvfvZB3HnyHz1/+nOmTp9cbQOqyehfOefUceg3sxW1H3MYb/3mjznL1eeOuN9h8/805d/K5vHXvW3w24TO+ePWLWvMJrjN8HbJzsokxsufFe9aqJzs38X9Yc6Pak4HWlQFXVlLGwtkL6dC9Q+W2NTZcg7yOeXz+0ud1/o8+m/hZkwJ4FRmES35sXrC24o2R5KYqAODDZz7kxHAiWTlZ9OjXg60O24p9L9uXQdsN4sb9b6S0OPGLyHv/fY99L9uXX17/SzbedWM+euYjvnj1C2Z9NKveupcWJE7snXp1atb9kVS/G2+8kbPPPpvRo0dX217xC2ddk2lDYghN1XIN+ec//8nUqVO5+uqrWW+99dh333258MILKz9od+7cmTvvvJPtt9+eCRMmJNXv77//vs7tPXv2JDc3l3HjxjV4fKdOnSgoKODNN99k22235YILLuCAAw7gyCOPBOCTTz5h/PjxK/xBszkOPvhgfve733HooYdWBl4LCwt54IEHOPvssyvnBKpPxa/SFV9EGlLx/501q+734IrtNX/phrr/9xVfrhraV3Oy7wp1TXpdVlbGvHnzak1mnkrdu3cnKyuL1VZbrdHnTYWKx62uPkPi+dmUL0aFhYW0b98+6fIFBQU899xzvPnmm3zyySfccccd9O/fv1ZWRk3NeV2fccYZTJs2jWOOOYbzzjuP8847j+LiYp588knOOuusOrP/8vPzK++XlC4nnHACQOXw2QoFBQU89thjHHDAAeyzzz48+OCDLdK+56n6eZ5qmtZ+nqoI7C1btownn3yy1v5HH32U4cOHs+WWWzb6fG1L5ykDeKrXgVcdyE5n7MT87+bz0TMfMf/b+ZWZYyOOGkGvdXpVK3/fGffxw7QfGHnMSHY/b3d2P293SotLef/J93ngrAf4YdoPLd7nE+49gaG/GMoP037gvUffY8H3Cyrnpdvx9B3Jyav+lL/54JvZ9Xe7suWhW7L3JYlJQJcXLuftB97mgbMfYNGcRdXKd1mjC/ld8vnxmx/5/OXPm9y/dx5+h7///O/sfNbOjDpmFNudlFjh56u3vuLh8x7m4+c+Bn4Khg3YckC1rLma8jrl1dpWc6GOCqUlpYTsUHm7IiNu4ey6fyVY+H39vx7UpSLomdu+7pNP4YJCOvfuTH7X/DqzG9t3Sbw5Fy5s+htaWUkZc7+cyxN/eIKS5SX84vJfsMOpO1Rm4hXMLOBPW/6JvcbtxSa7bcLm+29euf3ZK59lwt9rf3nPzc+tdr8kpU7Fh+yOHatPHfDpp58yfPhwBg0axNtvv11tX3Z2NgMGDKC4uLjRDIBTTjmFrbbaiqFDhxJjrPx1uGqdFb+Cb7LJJkkH8OqbKmHBggVkZWXVOedJfV5//XX22msv2rVrx7Bhw9htt934zW9+w913380PP/xQORl3xVChqiv+Vajri0NzLVu2jPHjxzN+/HjWWmstRo8ezVFHHcURRxzBOuusUyvYWlPF8J6+ffs22lbFB+A11lijzv0VK9U1dbLr5lh99dX5+uvqc/BW/C+r/ore0P8Bmv6/qLhvb7/9NsOGDWvSMauvvjoffVR7ntr6Hs/6zJkzh0GDBpGTk9OkbKAFCxbw2muvsd9++7HJJpvUmVFSVUWWQdW5g6qqWM226txDZWVlXHvttVx77bX07t2bbbbZhkMOOYSDDjqITTbZhE022aRWllHF66+xL/FSS+nVqxf77rsvkFhxsr6gwQknnNBiATzPU/XzPNWtSe229vNUxbll0aJFdT7vKwJ8FUGzhrSl85QBPNWpc+/O7HDqDnz7/rf8eeSfqy38ADD8l8NrHRPLIi9c+wIvXPsCnXt3Zr1t1mOLQ7Zgi4O2oM8mfRi/yfha8+mlUv9h/Rn6i6F89L+P+Psef6es5Kf5EUII7HrOrrWOKV5WzOPjH+fx8Y/Tfa3urD96fUYcNYKtj9ianuv05MrRV1Yr/8173/DqLa8y9vaxnP3S2Vy9w9XMnT63Sf384MkP+ODJD2jXoR0DthrAZntuxuhfjebkx0/m0qGXMuvjWZXDTJ+76jnuP+v+Rmpsnoo2uqzepc79Xdaoe3t9Fs5JnEA69aw7Y232p7Pp3Lszqw9avdZKs13W6EL7Tu0p+LpghQNmHz71Ib+4/BcMGjOo2lDa7z/5npsPuZms7CzW+tlabLTTRmz/m+055NpDWL5kOa/e+mq1eiqCqBX3S1LqVAzJqxmIe+GFFzj88MPZbbfdan3xGT16NB07duTFF1+sc3hQhf79+3PZZZdxySWX8PHHiR9FKjJ88/J++tGjKZlHjXn99dfZc8892Xjjjev8wNqQ5cuX89prr/Haa6/x+eefc+edd7LPPvtUfjGq+AC69tpr1/o1t2LF3WRUzNGSzNwz33zzDf/5z3+4++67+eSTT9h2220rh1XV54033qC0tJTRo0fToUOHBocnvfPOO0Bicurs7Oxa88dUTERdM4jbErbbbrta8wdtu+225ObmVvYTqv8faurcuXOdH/obesyXLFnCBx98wCabbEL37t2TGsr69ttvs//++7PddtvVCjoPGDCgzr41ZOrUqQwaNIgNNtiADz+svcBUQyq+ACcT+Hv99ddZunQpo0aNqjVxdwiBXXbZBaDeQPoPP/zAww8/zMMPP0zPnj3Zcccd2XTTTWs9PzbYYAPmzp3LN998U2c9UksbO3YseXl5vPXWW7z77rt1ltl7773ZaaedWGeddaqt9NnSPE95nsq089TUqVP54Ycf6N27N6uttlqtoNimm24KkNTrrC2dp7IaL6JVUa+BvcjKzuKjZz+qFbzr1rcbvQf2bvD4RT8s4p2H3+Hmg2/mk+c/YbX1VqPPpn0q98fSWG1oa7LKShNBuaqZZBV6r5fo09T/Tq0WvANYZ8t1aNehXYN1//jNj7zxnze4dtdrmf3ZbNbfdv06F7KYfNdkbjnkFrr16cbZL53Naus3L3V5+dLlfDrhU+4/636euuwpcvNy2WT3TQCY8cYMykrLWG/b9ZpVdzK+/+R7ipYUsdaQtSqz36oaNKbuXyDq8+3URDr66huuXuf+T15IrOi7yW6b1Nq36e6JN9hPX1jxOWsqhknXfA5UKCstY+bbM3nmL89wyy9vAWDIvkNqlVtjw8QvRN+86xcBqTk23nhjunfvXmt7v379uO666wBqfSB94IEH+OGHHzjkkEOq/dqbl5fHH//4RwD+7//+r8F2b775Zj7//HP+/Oc/V26r+NC3114/zZlZ8XdTAxd1ufrqqyvbrvhVvqoOHTqw1VZbVd7eZpttKodjVLX66on3z6pfKt54IzFVw/HHH1+t7Kabbsppp52WdB8rJlTu169frX29evViyy23rLW9Y8eOdO7cmeLi4gaDpgBz587lnnvuoU+fPlx55ZU/TYtQpa6K+/ztt9/y7LPPMmDAgFrz6my55ZYceuihFBQU8PDDDyd9/5rroosuqpaVkJeXx5/+9CcgsQhLhcWLF/Pxxx8zatSoavP9ZGVlcdVVV9Ghw09TVFRo6DEHuOqqq8jLy+PWW2+tc9hOt27dqs1vdNddd7F8+XJ+85vf0L9//8rtIQSuuOKKJk0MDjBx4kSAOueB7NevHwMG1D0C4IQTTmDLLbdk5syZvP9+9UWeNthgg8rV/CosWbKEO++8k06dOtUahnXKKacwYMAAnn76aaZPnw4k5uLaYYcdarWbk5NTudJkzS/e66yzDmussUblfZLS4bjjjgPg17/+Nccff3ydl5tuuomsrKzKsiuL5ynPU5l2niotLeWmm24C4C9/+Uu1/+emm27KUUcdRXFxMQ888EC149r6ecoMPNVp3ozEi3m9bdYjZIXKRR7yOuZxxM1HVM7BViGnXQ7rbbNeZZCmQlZOFh16JN4sqi4WsHjeYtbabC1y2+dSvCz5jKuKhR569OvB3C+rZ75V9HnQmEFMuO6n6Hjn3p355fW/rFVXp16d6DWwFzPemFFte7uO7WjfuT2lxaX1Zgy+/eDblOxfwgn3n8DZL57N1Ttd3eB8ahU23GFDpk2aVus+V2TBVTxGi35YxOS7JjPiyBHsceEePP2npyuDlxV6DexFLIuV97upykrKeOOuN9j2hG3Za9xe1Vah7T+sP1sdtlUDR9f23YffsXDOQgZuPbDO/ZNum8Su5+zKmFPGMOm2SZWrCHfo1oHdz98dgJdufKnaMR17dqRTr04snru42iIf645al+mTp9cK0nXq1Yn9Lt8PoNrKsesMX4d5X82rPSS6xuNeVcX9+HSCE2FLzXHggQdy7rnnMmHCBKZPn86iRYtYd911+fnPf05+fj5PPPEEV15ZPct50aJFHH/88TzwwANMnDiRe+65h4KCAvbee2823HBD7r//fu6999562zzuuOMYM2YMw4cPr/Zr+bRp03jooYc45phj6NSpEwsXLuSoo45i8uTJSQ+fbcgLL7zA7373O/70pz/x+eef8+STTzJ9+nQ6depE//792W677XjllVfYfffEe91ZZ53FLrvswsSJE/nyyy9ZvHgxm2yyCbvvvjsFBQX84x//qKz70Ucf5bPPPuPQQw9lrbXWYvLkyfTr14999tmHRx99lIMPPjipPn766ad88803HHLIISxfvpyZM2dWrozXvXt3Jk+ezEcffcTbb7/N119/TZcuXdhzzz1Zc801+dvf/lbt1+j6nHLKKWy66ab86le/YsyYMTzzzDMsX76cAQMGsOuuu7L33ntXTmx90kkn8eqrr3LllVeyyy678NZbb7H22mtz4IEHUlZWxtFHH51Umyvq448/5sMPP+SBBx6guLiYffbZh/XWW4/HH3+81sTkV1xxBbfeeiuvvvoq999/P8uWLWP77bcnNzeXd999lyFDhlQr39BjPnPmTG677TaGDRvGySefzLRp03jmmWeYOXMmPXr0YMCAAYwePZrbbruNX/3qVwB89dVXnHvuuVx11VW888473HvvvSxYsIBdd92Vbt268d577/Gzn/0s6fv+yCOPcM0117Drrrvyz3/+s9q+oUOH8tBDDzFp0iQ+++wzZs+eTc+ePdl6663ZbLPNWLRoEUcccUSt1SA/+STxObDmF+Pzzz+fMWPGcNZZZzFkyBDeeOMNNtpoI/bdd19mz57NySefXFk2Pz+f559/nunTpzN58mS++uor2rdvz84778zGG2/Mo48+WtlOhYrsiJYalig1ZrvttmPDDTdk6tSpvPnmm/WW++c//8kFF1zA0UcfzcUXX1znCpYtwfNUguepIdXKt+XzFMBll13GjjvuyNixYxk8eDATJ06kd+/e7L///uTn53PmmWfWygpt6+cpA3iq08LZC3nj7jfY8pdbctG7F/HRsx+R3zWfjXbeiOJlxcx8Zyb9hv4Uqc/Nz+WM589g7vS5TJ88nYKvCshpn8NGO29En4378O6j7/L9Jz9NqvrJ858wYMsBnPr0qXz+0ueUFJXwzXvfMPXxqQ3265PnP2HXc3bliJuP4O0H3qZocRFL5y9l4vUTmfHmDL545Qs2339zznn1HL545Qu6rN6FTXbfhNmfzubHb6un/Xbr243zJp/Hdx99x9dvf03B1wXkd8ln8J6D6bpmV57/2/O1sg+rmvr4VG7Y5wZ+9fCvOGviWVyz0zV8M7XhbK0D/noAPdfpyWcTP2PejHmULC+h/7D+bLjjhsydMZe37vlp1aJ7TrmH1ddfnX3+sA9bH7E1X7zyBQtnL6Rbn26ssdEaDNhyADcfcnOzA3gAD5//MBvuuCE7nbET/bfozxevfEHXNbuyxcFb8P6T7zNknyFNqu/dh99l9ImjWXPjNWsFNOfNmMeDv32QQ/5+COe/dT5v3fsWJctL2PyAzemxdg+evfLZWkNrtz9le/YatxePjXuMx8c/Xrn9l9f9ki5rdGHaq9MomFlAWWkZPdfpyeA9BtOuQzveefidakNitzx0S8acPIbPXvyMOV/MYemPS+m9bm8222szipcV8/w1z1drN4TAhjtuyPeffM93H36HpKabMGECG2ywAUOHDmXEiBF07NiR+fPn88orr3DnnXfW+tBZ4dFHH2W77bbjggsuYP/996d9+/Z88cUXnHHGGVx77bX1ttenTx+uuOIKLr/8ct57771a+4855hgWLVrEPvvsQ25uLo8//ni1D2Mr6i9/+Quvvvoqp556Kttssw377LMPCxYs4Ntvv+Uf//gH//nPfyrL3nDDDfz4449stdVWjBo1ipycHL755htuuOEG/vrXvzJz5szKskVFRey4445ceeWV7LzzzgwfPpwPPvig8tf/ZL8YlZWVsd9++3H55Zdz0EEH0blzZ7KysnjllVd49913+f3vf8+YMWPYfvvt6dWrFwUFBXz66aece+65SU9WPn/+fEaOHMnpp5/OwQcfzAknnEBpaSlff/01t956a7VhW9OnT2eLLbbgwgsvZI899mDMmDEsXLiQp59+mksvvbTOVfxawkEHHcRFF13EYYcdRp8+ffj222+5+OKLufzyy2uVve222wghcOaZZzJ27Fh+/PFHHn30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6pwbY2YPUiSA55zLM7PxwIAwbU9EREREREREJK4559YGmbUWmGtmPwLzgF+AN6utYlLtEsJUzlnA1+U0j10HHBGm7YmIiIiIiIiIHNZ8A318A9wR7bpIZIUrgNcUWF7OMnlA7TBtT0REREREREREYCvQMdqVkMgKVxPaXUCrcpbpBGwJ0/ZEREREREREJBCNBHvYMLNEYAiwN9p1kcgKVwBvInCumTVzzpUK0plZR2Ao8EGYticiIiIiIiIiEtfM7OQgs5LwJlJdA/QC3qiuOkl0hCuA9yRwHjDWzO4EagGYWW3gZOBZwAM8HabtiYiIiIiIiEgApgy8eDKGsnMqDRgH/KlaaiNRE5YAnnNuqpndALwKfFtk1j7faz5wrXNuYTi2JyIiIiIiIiJyGHiYwAE8D7AbmOacm1a9VZJoCFcGHs65t81sAvB7oD/QEG8b7CnAS865peHaloiIiIiIiIhIvHPODY92HSQ2hC2AB+CcWw7cFc4yRURERERERKQS1IRWJO4kRLsCIiIiIiIiIiIiElxYM/BERERERERERCQ0ZrYqxFWdc65DWCsjMUUBPBEREREREZF4oia0NVkCpf+DKUBz3+/5wE684w74YzqbgdxqqZ1EjZrQioiIiIiIiIjEAOdcW+dcO/8P0BPYiHeA0MFAmnOuOZAGDAGmAhuAo6NVZ6keysATERERERERiRcOLJoZeMr+C7dHgUygh3OuMMvOOecBxpjZYGC+b7nbo1JDqRbKwBMRERERERERiU0XACOKBu+Kcs5lAyOAC6u1VlLtFMATEREREREREYlNDYHkcpZJ9i0ncUwBPBEREREREZF44qL4I+G2ErjYzDICzTSz+sDFQKij10oNoQCeiIiIiIiIiEhsehU4AphmZleZWVszS/e9DsM7iEUz4OWo1lIiToNYiIiIiIiIiMQJI7qDWFj0Nh2XnHMvmVlH4Dbg7QCLGPCic+6V6q2ZVDcF8EREREREREREYpRz7g4z+wS4FugNZAB7gVnAO865SdGsn1QPBfBERERERERERGKYc24yMDna9ZDoUQBPREREREREJJ5oMAmRuKNBLERERERERERERGKYAngiIiIiIiIiIiIxTE1oRUREREREROKJmtCKxB1l4ImIiIiIiIiIiMQwZeCJiIiIiIiIxAsHFs0MPGX/iUSEMvBERERERERERERi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Al4ws1OAxXiz2QbjbfJ6X5FlW/jmr8UbrAu1nEJmVg+4BO/gFe+WU9dxZrYMmA5sxNvn3gCgB3AIuMI5t6/cdyxSBmXgiYiIiIiIiMSTmj2AhfcteLPnjsU78ms/4I9AB+AF4Hjn3M4Il3MF3gy9igxe8RSwBRgC3AFcBSQDLwNHOed+qkhdRcqiDDwRERERERERiTm+UVyvqcByawCrajkl1vkn8M8KLvunypQtEgpl4ImIiIiIiIiIiMQwZeCJiIiIiIiIxJMwNmUVkdigDDwREREREREREZEYpgCeiIiIiIiIiIhIDFMTWhEREREREZE4YmpCKxJ3lIEnIiIiIiIiIiISw5SBJyIiIiIiIhIvHNEdxELZfyIRoQw8ERERERERERGRGKYAnoiIiIiIiIiISAxTE1oRERERERGROGGAuei1Y7WobVkkvikDT0REREREREREJIYpgCciIiIiIiIiIhLD1IRWREREREREJJ5oJFiRuKMMPBERERERERERkRimDDwRERERERGROGLKwBOJO8rAExERERERERERiWEK4ImIiIiIiIiIiMQwNaEVERERERERiSdqQisSd5SBJyIiIiIiIiIiEsOUgSciIiIiIiISL1yUB7FQ9p9IRCgDT0REREREREREJIYpgCciIiIiIiIiIhLD1IRWREREREREJJ6oGatI3FEGnoiIiIiIiIiISAxTAE9ERERERERERCSGqQmtiIiIiIiISByJ6ii0IhIRysATERERERERERGJYcrAExEREREREYknysATiTvKwBMREREREREREYlhCuCJiIiIiIiIiIjEMDWhFREREREREYkjGsRCJP4oA09ERERERERERCSGKYAnIiIiIiIiIiISw9SEVkRERERERCReOOf9ieb2RSTslIEnIiIiIiIiIiISw5SBJyIiIiIiIhInjOgOYmHR27RIXFMGnoiIiIiIiIiISAxTAE9ERERERERERCSGqQmtiIiIiIiISDzROBIicUcZeCIiIiIiIiIiIjFMGXgiIiIiIiIiccQ80a6BiISbMvBERERERERERERimAJ4IiIiIiIiIiIiMUxNaEVERERERETihSO6g1hoAA2RiFAGnoiIiIiIiIiISAxTAE9ERERERERERCSGqQmtiIiIiIiISBwxNWMViTvKwBMREREREREREYlhNSoDz8waAhcA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iWFqQisiIiIiIiISTzzRroCIhJsy8ERERERERERERGKYMvBERERERERE4oQ5ojqIhWkMC5GIUAaeiIiIiIiIiIhIDFMAT0REREREREREJIapCa2IiIiIiIhIPFEzVpG4oww8ERERERERERGRGKYAnoiIiIiIiIiISAxTE1oRERERERGReBLFUWhFJDKUgSciIiIiIiIiIhLDlIEnIiIiIiIiEkdMCXgicUcZeCIiIiIiIiIiIjFMATwREREREREREZEYpia0IiIiIiIiIvFEg1iIxB1l4ImIiIiIiIiIiMQwZeCJiIiIiIiIxAsH5onu9kUk/JSBJyIiIiIiIiIiEsMUwBMREREREREREYlhakIrIiIiIiIiEk80iIVI3FEGnoiIiIiIiIiISAxTAE9ERERERERERCSGqQmtiIiIiIiISDxRC1qRuKMMPBERERERERERkRimDDwRERERERGRuOGwqA5iofQ/kUhQBp6IiIiIiIiIiEgMUwBPREREREREREQkhqkJrYiIiIiIiEi8cEA0m9CqBa1IRCgDT0REREREREREJIYpgCciIiIiIiIiIhLD1IRWREREREREJJ54ol0BEQk3ZeCJiIiIiIiIiIjEMGXgiYiIiIiIiMQRi+YgFiISEcrAExERERERERERiWEK4ImIiIiIiIiIiMQwNaEVERERERERiSdqQisSd5SBJyIiIiIiIiIiEsOUgSciIiIiIiISLxzRzcBT8p9IRCgDT0REREREREREJIYpgCciIiIiIiIiIhLD1IRWREREREREJJ54ol0BEQk3ZeCJiIiIiIiIiIjEMAXwREREREREREREYpia0IqIiIiIiIjECcNhURyF1jQMrUhEKANPREREREREREQkhtW4AJ6ZXWxmL5rZeDPbZ2bOzD6Idr1EREREREREYoJz0fsRkYioiU1o/wr0BA4AG4Au0a2OiIiIiIiIiIhI5NS4DDzgLqATUA+4Ocp1ERERERERERERiagal4HnnBvt/93MolkVERERERERkdjiiG5TVrWiFYmImpiBJyIiIiIiIiIictgwV4M7mTSzQcBo4EPn3G8rsd7MILO6tG/fPv3ll1/eEobqsTV/X4Nsl5da1jI3zlpRZhk3Dt9U5vxTvj8WgD2HapGbn1zJGhaXkpRHs9p7STJP1B+abN3fPCc/JavMz64i8j0JOBc4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UM72Atnuey1zxFyR8igDT0RERERERERiyWjf6+lmVixuYWZ18TZnzQKmRKIcM0sBPscbvHsPuDLE4B1AP9/rqhDXFwEUwBMRERERERGRGOKcW4l3QIi2wC0lZj+EN5vtPefcQQAzSzazLmbWoSrl+MpKBb4EzgPeBK5xznnKqq+ZnRRgmpnZX4DjgR3AD2WVIVIeNaEVERERERERiSfRHIU2fH4PTAJeMLNTgMV4s9kG423yel+RZVv45q/FG6wLtRyAV4Gz8AbdNgIPmFmJRRjjnBtT5O9xZrYMmO5bJwNvdl8P4BBwhXNuX8XfukhpCuCJiIiIiIiISExxzq00s2OBh4GheINqm4EXgIecc7siVE4732sj4IEyih5T5PengL7AEKAB4AHWAS8Dzzjn1HxWqkwBPBEREREREZF4Eh8ZeP5RXK+pwHJrgFJpcpUtx7fsoApWr+g6f6rsOiKVpT7wREREREREREREYpgCeCIiIiIiIiIiIjFMTWhFRERERERE4oUjuk1o46P1rkjMUQaeiIiIiIiIiIhIDFMAT0REREREREREJIapCa2IiIiIiIhI3HDgiWY7VrWhFYkEZeCJiIiIiIiIiIjEMGXgiYiIiIiIiMQT54l2DUQkzJSBJyIiIiIiIiIiEsMUwBMREREREREREYlhakIrIiIiIiIiEk+cBpIQiTfKwBMREREREREREYlhysATERERERERiRcO8EQxA0/JfyIRoQw8ERERERERERGRGKYAnoiIiIiIiIiISAxTE1oRERERERGRuOGiPIiF2tCKRIIy8ERERERERERERGKYAngiIiIiIiIiIiIxTE1oRUREREREROJJVJvQikgkKANPREREREREREQkhikDT0RERERERCSeKANPJO4oA09ERERERERERCSGKYAnIiIiIiIiIiISw9SEVkRERERERCReOMDjie72RSTslIEnIiIiIiIiIiISwxTAExERERERERERiWFqQisiIiIiIiISN1yUR6FVG1qRSFAGnoiIiIiIiIiISAxTBp6IiIiIiIhIPIlqBp6IRIIy8ERERERERERERGKYAngiIiIiIiIiIiIxTE1oRUREREREROKFAzxRbEKr1rsiEaEMPBERERERERERkRimDDwRERERERGROOKcJ9pVEJEwUwaeiIiIiIiIiIhIDFMAT0REREREREREJIapCa2IiIiIiIhI3HDRHcRCo1iIRIQy8ERERERERERERGKYAngiIiIiIiIiIiIxTE1oRUREREREROKJUzNWkXijDDwREREREREREZEYpgw8ERERERERkXjhAI8nutsXkbBTBp6IiIiIiIiIiEgMUwBPREREREREREQkhqkJrYiIiIiIiEg80SAWInFHGXgiIiIiIiIiIiIxTAE8ERERERERERGRGKYmtCIiIiIiIiJxwuFwURyF1mkYWpGIUAaeiIiIiIiIiIhIDFMGnoiIiIiIiEi8cER3EAsl4IlEhDLwREREREREREREYpgCeCIiIiIiIiIiIjFMTWhFRERERERE4olH7VhF4o0y8ERERERERERERGKYMvBERERERERE4oYD54nu9kUk7JSBJyIiIiIiIiIiEsMUwBMREREREREREYlhakIrIiIiIiIiEi8cuGgOYqEWtCIRoQw8ERERERERERGRGKYAnoiIiIiIiIiISAxTE1oRERERERGReBLVUWhFJBKUgSciIiIiIiIiIhLDamQAz8xamtlbZrbJzHLMbI2ZPWdm9aNdNxEREREREZFoch4XtZ9wCte9fyjlmNkJZvadme0ys0NmNs/M7jSzxDLWGWZm08zsgJntNbMxZvarytRVJJgaF8Azsw7ATOAaYBrwLLAKuAOYbGYNo1g9EREREREREamicN37h1KOmZ0HjANOBr4EXgZSfOt+EmQ7TwHvAM2B14EPgKOAb8zs1orUVaQsNS6AB7wCNAFud86d75y7xzk3BO8XqTPwaFRrJyIiIiIiIiJVFa57/0qVY2b18AbgCoBBzrnrnHN/AnoBk4GLzezSEuucAPwRWAkc7Zy7yzl3C9AH2AU8ZWZtK/0JiBRRowJ4ZtYeOB1YgzcCXtSDwEHgSjOrXc1VExEREREREYkBzjuIRbR+qHoz2nDd+4dYzsVAY+AT59wM/0TnXDbwV9+fN5co6ybf66POud1F1vFvNxVvBqBIyGpUAA8Y4nv9ybniw+o45/YDE4FaQP/qrpiIiIiIiIiIhEW47v1DKce/zg8ByhsHHAJOMLPUCq7zfYllREJizoW3k8lIMrMngbuBu51zTweY/xJwC/B759w/yyhnZpBZPVNSUhJbtGiRF476FiS45PKWaXQop8z56zaXXZW67ermAeRDuduqiJTE/HAUEzJPQVJY3w8W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\n", "text/plain": [ "
" ] }, "metadata": { "image/png": { "height": 401, "width": 632 }, "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "y = df.engine_id\n", "x = df.started\n", "width = df.completed\n", "height = np.ones(len(df))\n", "\n", "import matplotlib as mpl\n", "from matplotlib.collections import PatchCollection\n", "from matplotlib.patches import Rectangle\n", "from matplotlib import cm\n", "\n", "color_map = mpl.cm.ScalarMappable(\n", " norm=mpl.colors.Normalize(vmin=0, vmax=df.compute.max()),\n", " cmap=cm.viridis,\n", ")\n", "\n", "plt.colorbar(color_map, label=\"duration (s)\")\n", "\n", "\n", "def working_rect(row):\n", " \"\"\"Create a rectangle representing one task\n", "\n", " x = start time\n", " y = engine id\n", " width = duration\n", " height = 1\n", " color = duration\n", " \"\"\"\n", " duration = row.completed - row.started\n", " return Rectangle(\n", " (row.started, row.engine_id),\n", " width=duration,\n", " height=1,\n", " color=color_map.to_rgba(duration),\n", " )\n", "\n", "\n", "rects = df.apply(working_rect, axis=1)\n", "pc = PatchCollection(rects, match_original=True)\n", "\n", "ax = plt.gca()\n", "ax.set_ylim(0, df.engine_id.max() + 1)\n", "ax.set_xlim(0, df.received.max())\n", "ax.add_collection(pc)\n", "\n", "last_send = df.submitted.max()\n", "finished = df.received.max()\n", "first_result = df.received.min()\n", "half_done = df.received.quantile(0.5)\n", "\n", "ymin, ymax = ax.get_ylim()\n", "\n", "\n", "def annotate_milestone(ax, t, label, color=\"red\", dark=False, top=True):\n", " \"\"\"Add a vertical line annotation for a milestone time\"\"\"\n", "\n", " patch = plt.vlines(\n", " [t],\n", " *ax.get_ylim(),\n", " linewidth=4,\n", " color=color,\n", " )\n", " if top:\n", " xy = (t, ymax)\n", " xytext = (10, 10)\n", " else:\n", " xy = (t, ymin)\n", " xytext = (10, -30)\n", " full_label = f\"{label} ({t:.1f}s)\"\n", " ax.annotate(\n", " full_label,\n", " xy=xy,\n", " xytext=xytext,\n", " textcoords=\"offset points\",\n", " color=\"white\" if dark else \"black\",\n", " bbox=dict(\n", " boxstyle=\"round\",\n", " fc=color,\n", " ),\n", " arrowprops=dict(\n", " arrowstyle=\"wedge,tail_width=1.\",\n", " fc=color,\n", " patchA=None,\n", " patchB=patch,\n", " relpos=(0.2, 0.8),\n", " # connectionstyle=\"arc3,rad=-0.1\",\n", " ),\n", " )\n", "\n", "\n", "annotate_milestone(\n", " ax,\n", " df.submitted.max(),\n", " \"last task send\",\n", " color=(0.4, 0, 0.4),\n", " dark=True,\n", ")\n", "annotate_milestone(\n", " ax,\n", " df.completed.quantile(0.5),\n", " \"50% results computed\",\n", " color=\"black\",\n", " dark=True,\n", ")\n", "annotate_milestone(\n", " ax,\n", " df.completed.max(),\n", " \"All results computed\",\n", " color=\"black\",\n", " dark=True,\n", ")\n", "\n", "annotate_milestone(\n", " ax,\n", " df.received.min(),\n", " \"first result arrives\",\n", " color=(1, 0.5, 0.5),\n", " top=False,\n", ")\n", "annotate_milestone(\n", " ax,\n", " df.received.quantile(0.5),\n", " \"50% results arrived\",\n", " color=(1, 0.5, 0.5),\n", " top=False,\n", ")\n", "annotate_milestone(\n", " ax,\n", " df.received.max(),\n", " \"All done\",\n", " color=(1, 0.5, 0.5),\n", " top=False,\n", ")\n", "\n", "\n", "# plt.vlines(\n", "# [first_result, half_done, finished],\n", "# *ax.get_ylim(),\n", "# linewidth=4,\n", "# color=\"red\",\n", "# )\n", "# plt.legend(loc=0)\n", "plt.xlabel(\"seconds\")\n", "plt.ylabel(\"engine id\")" ] }, { "cell_type": "markdown", "id": "c4ecbaf9-d541-482e-8749-62aaa8a2192d", "metadata": {}, "source": [ "Sometimes in this example, a bubble of idle engines can be produced around when the last task is sent.\n", "\n", "This is caused when lots of results arrive in the client while the client is still trying to submit tasks.\n", "Because of IPython Parallel's threaded implementation,\n", "this can cause lots of contention,\n", "and receiving lots of results can significantly affect overal performance if the client is still trying to assemble tasks to submit.\n", "\n", "This is an opportunity for [optimization in the client](https://github.com/ipython/ipyparallel/pull/534)!\n", "\n", "A similar visualization can be done with altair." ] }, { "cell_type": "code", "execution_count": 8, "id": "a5ce43d6-2202-4fd6-a1d9-251d8572d380", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "" ], "text/plain": [ "alt.Chart(...)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import altair as alt\n", "\n", "alt.Chart(df).mark_rect().encode(\n", " x=\"started\",\n", " x2=\"completed\",\n", " y=\"engine_id\",\n", " y2=\"next_engine_id\",\n", " color=\"compute\",\n", ").transform_calculate(\n", " next_engine_id=\"datum.engine_id + 1\",\n", ")" ] }, { "cell_type": "markdown", "id": "7d5d9bda-a58b-43b9-9839-59811956a6f8", "metadata": {}, "source": [ "We can also visualize 'load' as a measure of how busy the cluster is.\n", "It's essentially collecting samples of the above plot by drawing lots of vertical lines,\n", "and counting how many boxes are intersected instead of empty space.\n", "\n", "That is, for any given point in time, how many engines are working,\n", "vs how many are waiting for their next task.\n", "\n", "We do this by sampling points in time and counting the number of 'active' tasks.\n", "This is computed as the average value across a given window (default: sample every 10ms, plot the average load over 100ms)" ] }, { "cell_type": "code", "execution_count": 9, "id": "bdabe160-5548-492d-910c-d1af39e88739", "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "image/png": { "height": 384, "width": 599 }, "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "def plot_load(asyncresult_df, samples=None, window=10):\n", " if samples is None:\n", " samples = min(int(1e2 * df['received'].max()), 1001)\n", "\n", " timeline = np.linspace(0, df['received'].max(), samples)\n", " remaining = df\n", " working = []\n", " for t in timeline:\n", "\n", " remaining = remaining[remaining.completed >= t]\n", " active = remaining[remaining.started < t]\n", " working.append(len(active))\n", "\n", " s = pd.Series(working, index=timeline, name=\"load\")\n", " if window > 1:\n", " windowed = s.rolling(window).mean()\n", " else:\n", " windowed = s\n", " windowed.plot()\n", " plt.ylim(0, len(df.engine_id.unique()))\n", " plt.xlabel(\"seconds\")\n", " plt.ylabel(\"busy engines\")\n", " plt.xlim(0, timeline[-1])\n", " \n", "plot_load(df)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "0421d9cb7f9b406d982ee0c005a9a532": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_b3cd9eef2db342308a832c022cbb620d", "style": "IPY_MODEL_8fd7b6a17e804c90961d59398b635244", "value": "100%" } }, "2325225341874e0b9cee528398d6424b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "5109f916201e46279f135d7403112689": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "6849ab149d6d41c99fe55794adfc8d23": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": { "description_width": "" } }, "7ba86883f6654cb8bd011b877aa43270": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8fd7b6a17e804c90961d59398b635244": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "a304343ad95c4cf888b0052e1ac42917": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": { "layout": "IPY_MODEL_c33ae892827943a9b19193d0fbbfa253", "style": "IPY_MODEL_2325225341874e0b9cee528398d6424b", "value": " 8/8 [00:09<00:00, 1.37s/engine]" } }, "a88e9002f12849979a16528e113e9a54": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_0421d9cb7f9b406d982ee0c005a9a532", "IPY_MODEL_f10a317efcce44cd9ddd950a291e1076", "IPY_MODEL_a304343ad95c4cf888b0052e1ac42917" ], "layout": "IPY_MODEL_7ba86883f6654cb8bd011b877aa43270" } }, "b3cd9eef2db342308a832c022cbb620d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c33ae892827943a9b19193d0fbbfa253": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "f10a317efcce44cd9ddd950a291e1076": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": { "bar_style": "success", "layout": "IPY_MODEL_5109f916201e46279f135d7403112689", "max": 8, "style": "IPY_MODEL_6849ab149d6d41c99fe55794adfc8d23", "value": 8 } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 } ipyparallel-7.1.0/docs/source/examples/wave2D/000077500000000000000000000000001412533144600212365ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/examples/wave2D/RectPartitioner.py000077500000000000000000000462251412533144600247420ustar00rootroot00000000000000#!/usr/bin/env python """A rectangular domain partitioner and associated communication functionality for solving PDEs in (1D,2D) using FDM written in the python language The global solution domain is assumed to be of rectangular shape, where the number of cells in each direction is stored in nx, ny, nz The numerical scheme is fully explicit Authors ------- * Xing Cai * Min Ragan-Kelley """ from __future__ import print_function from numpy import ascontiguousarray from numpy import frombuffer from numpy import zeros try: from mpi4py import MPI except ImportError: pass else: mpi = MPI.COMM_WORLD class RectPartitioner: """ Responsible for a rectangular partitioning of a global domain, which is expressed as the numbers of cells in the different spatial directions. The partitioning info is expressed as an array of integers, each indicating the number of subdomains in one spatial direction. """ def __init__(self, my_id=-1, num_procs=-1, global_num_cells=[], num_parts=[]): self.nsd = 0 self.my_id = my_id self.num_procs = num_procs self.redim(global_num_cells, num_parts) def redim(self, global_num_cells, num_parts): nsd_ = len(global_num_cells) # print("Inside the redim function, nsd=%d" %nsd_) if nsd_ < 1 | nsd_ > 3 | nsd_ != len(num_parts): print('The input global_num_cells is not ok!') return self.nsd = nsd_ self.global_num_cells = global_num_cells self.num_parts = num_parts def prepare_communication(self): """ Find the subdomain rank (tuple) for each processor and determine the neighbor info. """ nsd_ = self.nsd if nsd_ < 1: print('Number of space dimensions is %d, nothing to do' % nsd_) return self.subd_rank = [-1, -1, -1] self.subd_lo_ix = [-1, -1, -1] self.subd_hi_ix = [-1, -1, -1] self.lower_neighbors = [-1, -1, -1] self.upper_neighbors = [-1, -1, -1] num_procs = self.num_procs my_id = self.my_id num_subds = 1 for i in range(nsd_): num_subds = num_subds * self.num_parts[i] if my_id == 0: print("# subds=", num_subds) # should check num_subds againt num_procs offsets = [1, 0, 0] # find the subdomain rank self.subd_rank[0] = my_id % self.num_parts[0] if nsd_ >= 2: offsets[1] = self.num_parts[0] self.subd_rank[1] = my_id / offsets[1] if nsd_ == 3: offsets[1] = self.num_parts[0] offsets[2] = self.num_parts[0] * self.num_parts[1] self.subd_rank[1] = (my_id % offsets[2]) / self.num_parts[0] self.subd_rank[2] = my_id / offsets[2] print("my_id=%d, subd_rank: " % my_id, self.subd_rank) if my_id == 0: print("offsets=", offsets) # find the neighbor ids for i in range(nsd_): rank = self.subd_rank[i] if rank > 0: self.lower_neighbors[i] = my_id - offsets[i] if rank < self.num_parts[i] - 1: self.upper_neighbors[i] = my_id + offsets[i] k = self.global_num_cells[i] / self.num_parts[i] m = self.global_num_cells[i] % self.num_parts[i] ix = rank * k + max(0, rank + m - self.num_parts[i]) self.subd_lo_ix[i] = ix ix = ix + k if rank >= (self.num_parts[i] - m): ix = ix + 1 # load balancing if rank < self.num_parts[i] - 1: ix = ix + 1 # one cell of overlap self.subd_hi_ix[i] = ix print( "subd_rank:", self.subd_rank, "lower_neig:", self.lower_neighbors, "upper_neig:", self.upper_neighbors, ) print( "subd_rank:", self.subd_rank, "subd_lo_ix:", self.subd_lo_ix, "subd_hi_ix:", self.subd_hi_ix, ) class RectPartitioner1D(RectPartitioner): """ Subclass of RectPartitioner, for 1D problems """ def prepare_communication(self): """ Prepare the buffers to be used for later communications """ RectPartitioner.prepare_communication(self) if self.lower_neighbors[0] >= 0: self.in_lower_buffers = [zeros(1, float)] self.out_lower_buffers = [zeros(1, float)] if self.upper_neighbors[0] >= 0: self.in_upper_buffers = [zeros(1, float)] self.out_upper_buffers = [zeros(1, float)] def get_num_loc_cells(self): return [self.subd_hi_ix[0] - self.subd_lo_ix[0]] class RectPartitioner2D(RectPartitioner): """ Subclass of RectPartitioner, for 2D problems """ def prepare_communication(self): """ Prepare the buffers to be used for later communications """ RectPartitioner.prepare_communication(self) self.in_lower_buffers = [[], []] self.out_lower_buffers = [[], []] self.in_upper_buffers = [[], []] self.out_upper_buffers = [[], []] size1 = self.subd_hi_ix[1] - self.subd_lo_ix[1] + 1 if self.lower_neighbors[0] >= 0: self.in_lower_buffers[0] = zeros(size1, float) self.out_lower_buffers[0] = zeros(size1, float) if self.upper_neighbors[0] >= 0: self.in_upper_buffers[0] = zeros(size1, float) self.out_upper_buffers[0] = zeros(size1, float) size0 = self.subd_hi_ix[0] - self.subd_lo_ix[0] + 1 if self.lower_neighbors[1] >= 0: self.in_lower_buffers[1] = zeros(size0, float) self.out_lower_buffers[1] = zeros(size0, float) if self.upper_neighbors[1] >= 0: self.in_upper_buffers[1] = zeros(size0, float) self.out_upper_buffers[1] = zeros(size0, float) def get_num_loc_cells(self): return [ self.subd_hi_ix[0] - self.subd_lo_ix[0], self.subd_hi_ix[1] - self.subd_lo_ix[1], ] class MPIRectPartitioner2D(RectPartitioner2D): """ Subclass of RectPartitioner2D, which uses MPI via mpi4py for communication """ def __init__( self, my_id=-1, num_procs=-1, global_num_cells=[], num_parts=[], slice_copy=True ): RectPartitioner.__init__(self, my_id, num_procs, global_num_cells, num_parts) self.slice_copy = slice_copy def update_internal_boundary(self, solution_array): nsd_ = self.nsd if nsd_ != len(self.in_lower_buffers) | nsd_ != len(self.out_lower_buffers): print("Buffers for communicating with lower neighbors not ready") return if nsd_ != len(self.in_upper_buffers) | nsd_ != len(self.out_upper_buffers): print("Buffers for communicating with upper neighbors not ready") return loc_nx = self.subd_hi_ix[0] - self.subd_lo_ix[0] loc_ny = self.subd_hi_ix[1] - self.subd_lo_ix[1] lower_x_neigh = self.lower_neighbors[0] upper_x_neigh = self.upper_neighbors[0] lower_y_neigh = self.lower_neighbors[1] upper_y_neigh = self.upper_neighbors[1] # communicate in the x-direction first if lower_x_neigh > -1: if self.slice_copy: self.out_lower_buffers[0] = ascontiguousarray(solution_array[1, :]) else: for i in range(0, loc_ny + 1): self.out_lower_buffers[0][i] = solution_array[1, i] mpi.Isend(self.out_lower_buffers[0], lower_x_neigh) if upper_x_neigh > -1: mpi.Recv(self.in_upper_buffers[0], upper_x_neigh) if self.slice_copy: solution_array[loc_nx, :] = self.in_upper_buffers[0] self.out_upper_buffers[0] = ascontiguousarray( solution_array[loc_nx - 1, :] ) else: for i in range(0, loc_ny + 1): solution_array[loc_nx, i] = self.in_upper_buffers[0][i] self.out_upper_buffers[0][i] = solution_array[loc_nx - 1, i] mpi.Isend(self.out_upper_buffers[0], upper_x_neigh) if lower_x_neigh > -1: mpi.Recv(self.in_lower_buffers[0], lower_x_neigh) if self.slice_copy: solution_array[0, :] = self.in_lower_buffers[0] else: for i in range(0, loc_ny + 1): solution_array[0, i] = self.in_lower_buffers[0][i] # communicate in the y-direction afterwards if lower_y_neigh > -1: if self.slice_copy: self.out_lower_buffers[1] = ascontiguousarray(solution_array[:, 1]) else: for i in range(0, loc_nx + 1): self.out_lower_buffers[1][i] = solution_array[i, 1] mpi.Isend(self.out_lower_buffers[1], lower_y_neigh) if upper_y_neigh > -1: mpi.Recv(self.in_upper_buffers[1], upper_y_neigh) if self.slice_copy: solution_array[:, loc_ny] = self.in_upper_buffers[1] self.out_upper_buffers[1] = ascontiguousarray( solution_array[:, loc_ny - 1] ) else: for i in range(0, loc_nx + 1): solution_array[i, loc_ny] = self.in_upper_buffers[1][i] self.out_upper_buffers[1][i] = solution_array[i, loc_ny - 1] mpi.Isend(self.out_upper_buffers[1], upper_y_neigh) if lower_y_neigh > -1: mpi.Recv(self.in_lower_buffers[1], lower_y_neigh) if self.slice_copy: solution_array[:, 0] = self.in_lower_buffers[1] else: for i in range(0, loc_nx + 1): solution_array[i, 0] = self.in_lower_buffers[1][i] class ZMQRectPartitioner2D(RectPartitioner2D): """ Subclass of RectPartitioner2D, which uses 0MQ via pyzmq for communication The first two arguments must be `comm`, an EngineCommunicator object, and `addrs`, a dict of connection information for other EngineCommunicator objects. """ def __init__( self, comm, addrs, my_id=-1, num_procs=-1, global_num_cells=[], num_parts=[], slice_copy=True, ): RectPartitioner.__init__(self, my_id, num_procs, global_num_cells, num_parts) self.slice_copy = slice_copy self.comm = comm # an Engine self.addrs = addrs def prepare_communication(self): RectPartitioner2D.prepare_communication(self) # connect west/south to east/north west_id, south_id = self.lower_neighbors[:2] west = self.addrs.get(west_id, None) south = self.addrs.get(south_id, None) self.comm.connect(south, west) def update_internal_boundary_x_y(self, solution_array): """update the inner boundary with the same send/recv pattern as the MPIPartitioner""" nsd_ = self.nsd dtype = solution_array.dtype if nsd_ != len(self.in_lower_buffers) | nsd_ != len(self.out_lower_buffers): print("Buffers for communicating with lower neighbors not ready") return if nsd_ != len(self.in_upper_buffers) | nsd_ != len(self.out_upper_buffers): print("Buffers for communicating with upper neighbors not ready") return loc_nx = self.subd_hi_ix[0] - self.subd_lo_ix[0] loc_ny = self.subd_hi_ix[1] - self.subd_lo_ix[1] lower_x_neigh = self.lower_neighbors[0] upper_x_neigh = self.upper_neighbors[0] lower_y_neigh = self.lower_neighbors[1] upper_y_neigh = self.upper_neighbors[1] trackers = [] flags = dict(copy=False, track=False) # communicate in the x-direction first if lower_x_neigh > -1: if self.slice_copy: self.out_lower_buffers[0] = ascontiguousarray(solution_array[1, :]) else: for i in range(0, loc_ny + 1): self.out_lower_buffers[0][i] = solution_array[1, i] t = self.comm.west.send(self.out_lower_buffers[0], **flags) trackers.append(t) if upper_x_neigh > -1: msg = self.comm.east.recv(copy=False) self.in_upper_buffers[0] = frombuffer(msg, dtype=dtype) if self.slice_copy: solution_array[loc_nx, :] = self.in_upper_buffers[0] self.out_upper_buffers[0] = ascontiguousarray( solution_array[loc_nx - 1, :] ) else: for i in range(0, loc_ny + 1): solution_array[loc_nx, i] = self.in_upper_buffers[0][i] self.out_upper_buffers[0][i] = solution_array[loc_nx - 1, i] t = self.comm.east.send(self.out_upper_buffers[0], **flags) trackers.append(t) if lower_x_neigh > -1: msg = self.comm.west.recv(copy=False) self.in_lower_buffers[0] = frombuffer(msg, dtype=dtype) if self.slice_copy: solution_array[0, :] = self.in_lower_buffers[0] else: for i in range(0, loc_ny + 1): solution_array[0, i] = self.in_lower_buffers[0][i] # communicate in the y-direction afterwards if lower_y_neigh > -1: if self.slice_copy: self.out_lower_buffers[1] = ascontiguousarray(solution_array[:, 1]) else: for i in range(0, loc_nx + 1): self.out_lower_buffers[1][i] = solution_array[i, 1] t = self.comm.south.send(self.out_lower_buffers[1], **flags) trackers.append(t) if upper_y_neigh > -1: msg = self.comm.north.recv(copy=False) self.in_upper_buffers[1] = frombuffer(msg, dtype=dtype) if self.slice_copy: solution_array[:, loc_ny] = self.in_upper_buffers[1] self.out_upper_buffers[1] = ascontiguousarray( solution_array[:, loc_ny - 1] ) else: for i in range(0, loc_nx + 1): solution_array[i, loc_ny] = self.in_upper_buffers[1][i] self.out_upper_buffers[1][i] = solution_array[i, loc_ny - 1] t = self.comm.north.send(self.out_upper_buffers[1], **flags) trackers.append(t) if lower_y_neigh > -1: msg = self.comm.south.recv(copy=False) self.in_lower_buffers[1] = frombuffer(msg, dtype=dtype) if self.slice_copy: solution_array[:, 0] = self.in_lower_buffers[1] else: for i in range(0, loc_nx + 1): solution_array[i, 0] = self.in_lower_buffers[1][i] # wait for sends to complete: if flags['track']: for t in trackers: t.wait() def update_internal_boundary_send_recv(self, solution_array): """update the inner boundary, sending first, then receiving""" nsd_ = self.nsd dtype = solution_array.dtype if nsd_ != len(self.in_lower_buffers) | nsd_ != len(self.out_lower_buffers): print("Buffers for communicating with lower neighbors not ready") return if nsd_ != len(self.in_upper_buffers) | nsd_ != len(self.out_upper_buffers): print("Buffers for communicating with upper neighbors not ready") return loc_nx = self.subd_hi_ix[0] - self.subd_lo_ix[0] loc_ny = self.subd_hi_ix[1] - self.subd_lo_ix[1] lower_x_neigh = self.lower_neighbors[0] upper_x_neigh = self.upper_neighbors[0] lower_y_neigh = self.lower_neighbors[1] upper_y_neigh = self.upper_neighbors[1] trackers = [] flags = dict(copy=False, track=False) # send in all directions first if lower_x_neigh > -1: if self.slice_copy: self.out_lower_buffers[0] = ascontiguousarray(solution_array[1, :]) else: for i in range(0, loc_ny + 1): self.out_lower_buffers[0][i] = solution_array[1, i] t = self.comm.west.send(self.out_lower_buffers[0], **flags) trackers.append(t) if lower_y_neigh > -1: if self.slice_copy: self.out_lower_buffers[1] = ascontiguousarray(solution_array[:, 1]) else: for i in range(0, loc_nx + 1): self.out_lower_buffers[1][i] = solution_array[i, 1] t = self.comm.south.send(self.out_lower_buffers[1], **flags) trackers.append(t) if upper_x_neigh > -1: if self.slice_copy: self.out_upper_buffers[0] = ascontiguousarray( solution_array[loc_nx - 1, :] ) else: for i in range(0, loc_ny + 1): self.out_upper_buffers[0][i] = solution_array[loc_nx - 1, i] t = self.comm.east.send(self.out_upper_buffers[0], **flags) trackers.append(t) if upper_y_neigh > -1: if self.slice_copy: self.out_upper_buffers[1] = ascontiguousarray( solution_array[:, loc_ny - 1] ) else: for i in range(0, loc_nx + 1): self.out_upper_buffers[1][i] = solution_array[i, loc_ny - 1] t = self.comm.north.send(self.out_upper_buffers[1], **flags) trackers.append(t) # now start receiving if upper_x_neigh > -1: msg = self.comm.east.recv(copy=False) self.in_upper_buffers[0] = frombuffer(msg, dtype=dtype) if self.slice_copy: solution_array[loc_nx, :] = self.in_upper_buffers[0] else: for i in range(0, loc_ny + 1): solution_array[loc_nx, i] = self.in_upper_buffers[0][i] if lower_x_neigh > -1: msg = self.comm.west.recv(copy=False) self.in_lower_buffers[0] = frombuffer(msg, dtype=dtype) if self.slice_copy: solution_array[0, :] = self.in_lower_buffers[0] else: for i in range(0, loc_ny + 1): solution_array[0, i] = self.in_lower_buffers[0][i] if upper_y_neigh > -1: msg = self.comm.north.recv(copy=False) self.in_upper_buffers[1] = frombuffer(msg, dtype=dtype) if self.slice_copy: solution_array[:, loc_ny] = self.in_upper_buffers[1] else: for i in range(0, loc_nx + 1): solution_array[i, loc_ny] = self.in_upper_buffers[1][i] if lower_y_neigh > -1: msg = self.comm.south.recv(copy=False) self.in_lower_buffers[1] = frombuffer(msg, dtype=dtype) if self.slice_copy: solution_array[:, 0] = self.in_lower_buffers[1] else: for i in range(0, loc_nx + 1): solution_array[i, 0] = self.in_lower_buffers[1][i] # wait for sends to complete: if flags['track']: for t in trackers: t.wait() # use send/recv pattern instead of x/y sweeps update_internal_boundary = update_internal_boundary_send_recv ipyparallel-7.1.0/docs/source/examples/wave2D/communicator.py000066400000000000000000000036271412533144600243200ustar00rootroot00000000000000#!/usr/bin/env python """A simple Communicator class that has N,E,S,W neighbors connected via 0MQ PEER sockets""" import socket import zmq from ipyparallel.util import disambiguate_url class EngineCommunicator: """An object that connects Engines to each other. north and east sockets listen, while south and west sockets connect. This class is useful in cases where there is a set of nodes that must communicate only with their nearest neighbors. """ def __init__(self, interface='tcp://*', identity=None): self._ctx = zmq.Context() self.north = self._ctx.socket(zmq.PAIR) self.west = self._ctx.socket(zmq.PAIR) self.south = self._ctx.socket(zmq.PAIR) self.east = self._ctx.socket(zmq.PAIR) # bind to ports northport = self.north.bind_to_random_port(interface) eastport = self.east.bind_to_random_port(interface) self.north_url = interface + ":%i" % northport self.east_url = interface + ":%i" % eastport # guess first public IP from socket self.location = socket.gethostbyname_ex(socket.gethostname())[-1][0] def __del__(self): self.north.close() self.south.close() self.east.close() self.west.close() self._ctx.term() @property def info(self): """return the connection info for this object's sockets.""" return (self.location, self.north_url, self.east_url) def connect(self, south_peer=None, west_peer=None): """connect to peers. `peers` will be a 3-tuples, of the form: (location, north_addr, east_addr) as produced by """ if south_peer is not None: location, url, _ = south_peer self.south.connect(disambiguate_url(url, location)) if west_peer is not None: location, _, url = west_peer self.west.connect(disambiguate_url(url, location)) ipyparallel-7.1.0/docs/source/examples/wave2D/parallelwave-mpi.py000077500000000000000000000161061412533144600250610ustar00rootroot00000000000000#!/usr/bin/env python """ A simple python program of solving a 2D wave equation in parallel. Domain partitioning and inter-processor communication are done by an object of class MPIRectPartitioner2D (which is a subclass of RectPartitioner2D and uses MPI via mpi4py) An example of running the program is (8 processors, 4x2 partition, 400x100 grid cells):: $ ipcluster start --engines=MPIExec -n 8 # start 8 engines with mpiexec $ python parallelwave-mpi.py --grid 400 100 --partition 4 2 See also parallelwave-mpi, which runs the same program, but uses MPI (via mpi4py) for the inter-engine communication. Authors ------- * Xing Cai * Min Ragan-Kelley """ import argparse import time from numpy import sqrt import ipyparallel as ipp def setup_partitioner(index, num_procs, gnum_cells, parts): """create a partitioner in the engine namespace""" global partitioner p = MPIRectPartitioner2D(my_id=index, num_procs=num_procs) # noqa: F821 p.redim(global_num_cells=gnum_cells, num_parts=parts) p.prepare_communication() # put the partitioner into the global namespace: partitioner = p def setup_solver(*args, **kwargs): """create a WaveSolver in the engine namespace""" global solver solver = WaveSolver(*args, **kwargs) # noqa: F821 def wave_saver(u, x, y, t): """save the wave log""" global u_hist global t_hist t_hist.append(t) u_hist.append(1.0 * u) # main program: if __name__ == '__main__': parser = argparse.ArgumentParser() paa = parser.add_argument paa( '--grid', '-g', type=int, nargs=2, default=[100, 100], dest='grid', help="Cells in the grid, e.g. --grid 100 200", ) paa( '--partition', '-p', type=int, nargs=2, default=None, help="Process partition grid, e.g. --partition 4 2 for 4x2", ) paa('-c', type=float, default=1.0, help="Wave speed (I think)") paa('-Ly', type=float, default=1.0, help="system size (in y)") paa('-Lx', type=float, default=1.0, help="system size (in x)") paa('-t', '--tstop', type=float, default=1.0, help="Time units to run") paa( '--profile', type=str, default=u'default', help="Specify the ipcluster profile for the client to connect to.", ) paa( '--save', action='store_true', help="Add this flag to save the time/wave history during the run.", ) paa( '--scalar', action='store_true', help="Also run with scalar interior implementation, to see vector speedup.", ) ns = parser.parse_args() # set up arguments grid = ns.grid partition = ns.partition Lx = ns.Lx Ly = ns.Ly c = ns.c tstop = ns.tstop if ns.save: user_action = wave_saver else: user_action = None num_cells = 1.0 * (grid[0] - 1) * (grid[1] - 1) final_test = True # create the Client rc = ipp.Client(profile=ns.profile) num_procs = len(rc.ids) if partition is None: partition = [1, num_procs] assert ( partition[0] * partition[1] == num_procs ), "can't map partition %s to %i engines" % (partition, num_procs) view = rc[:] print("Running %s system on %s processes until %f" % (grid, partition, tstop)) # functions defining initial/boundary/source conditions def I(x, y): from numpy import exp return 1.5 * exp(-100 * ((x - 0.5) ** 2 + (y - 0.5) ** 2)) def f(x, y, t): return 0.0 # from numpy import exp,sin # return 10*exp(-(x - sin(100*t))**2) def bc(x, y, t): return 0.0 # initial imports, setup rank view.execute( '\n'.join( [ "from mpi4py import MPI", "import numpy", "mpi = MPI.COMM_WORLD", "my_id = MPI.COMM_WORLD.Get_rank()", ] ), block=True, ) # initialize t_hist/u_hist for saving the state at each step (optional) view['t_hist'] = [] view['u_hist'] = [] # set vector/scalar implementation details impl = {} impl['ic'] = 'vectorized' impl['inner'] = 'scalar' impl['bc'] = 'vectorized' # execute some files so that the classes we need will be defined on the engines: view.run('RectPartitioner.py') view.run('wavesolver.py') # setup remote partitioner # note that Reference means that the argument passed to setup_partitioner will be the # object named 'my_id' in the engine's namespace view.apply_sync( setup_partitioner, ipp.Reference('my_id'), num_procs, grid, partition ) # wait for initial communication to complete view.execute('mpi.barrier()') # setup remote solvers view.apply_sync( setup_solver, I, f, c, bc, Lx, Ly, partitioner=ipp.Reference('partitioner'), dt=0, implementation=impl, ) # lambda for calling solver.solve: _solve = lambda *args, **kwargs: solver.solve(*args, **kwargs) if ns.scalar: impl['inner'] = 'scalar' # run first with element-wise Python operations for each cell t0 = time.time() ar = view.apply_async( _solve, tstop, dt=0, verbose=True, final_test=final_test, user_action=user_action, ) if final_test: # this sum is performed element-wise as results finish s = sum(ar) # the L2 norm (RMS) of the result: norm = sqrt(s / num_cells) else: norm = -1 t1 = time.time() print('scalar inner-version, Wtime=%g, norm=%g' % (t1 - t0, norm)) impl['inner'] = 'vectorized' # setup new solvers view.apply_sync( setup_solver, I, f, c, bc, Lx, Ly, partitioner=ipp.Reference('partitioner'), dt=0, implementation=impl, ) view.execute('mpi.barrier()') # run again with numpy vectorized inner-implementation t0 = time.time() ar = view.apply_async( _solve, tstop, dt=0, verbose=True, final_test=final_test, user_action=user_action, ) if final_test: # this sum is performed element-wise as results finish s = sum(ar) # the L2 norm (RMS) of the result: norm = sqrt(s / num_cells) else: norm = -1 t1 = time.time() print('vector inner-version, Wtime=%g, norm=%g' % (t1 - t0, norm)) # if ns.save is True, then u_hist stores the history of u as a list # If the partion scheme is Nx1, then u can be reconstructed via 'gather': if ns.save and partition[-1] == 1: import matplotlib.pyplot as plt view.execute('u_last=u_hist[-1]') # map mpi IDs to IPython IDs, which may not match ranks = view['my_id'] targets = range(len(ranks)) for idx in range(len(ranks)): targets[idx] = ranks.index(idx) u_last = rc[targets].gather('u_last', block=True) plt.pcolor(u_last) plt.show() ipyparallel-7.1.0/docs/source/examples/wave2D/parallelwave.py000077500000000000000000000163711412533144600243020ustar00rootroot00000000000000#!/usr/bin/env python """ A simple python program of solving a 2D wave equation in parallel. Domain partitioning and inter-processor communication are done by an object of class ZMQRectPartitioner2D (which is a subclass of RectPartitioner2D and uses 0MQ via pyzmq) An example of running the program is (8 processors, 4x2 partition, 200x200 grid cells):: $ ipcluster start -n 8 # start 8 engines $ python parallelwave.py --grid 200 200 --partition 4 2 See also parallelwave-mpi, which runs the same program, but uses MPI (via mpi4py) for the inter-engine communication. Authors ------- * Xing Cai * Min Ragan-Kelley """ import argparse import time from numpy import sqrt import ipyparallel as ipp def setup_partitioner(comm, addrs, index, num_procs, gnum_cells, parts): """create a partitioner in the engine namespace""" global partitioner p = ZMQRectPartitioner2D( # noqa: F821 comm, addrs, my_id=index, num_procs=num_procs, ) p.redim(global_num_cells=gnum_cells, num_parts=parts) p.prepare_communication() # put the partitioner into the global namespace: partitioner = p def setup_solver(*args, **kwargs): """create a WaveSolver in the engine namespace.""" global solver solver = WaveSolver(*args, **kwargs) # noqa: F821 def wave_saver(u, x, y, t): """save the wave state for each timestep.""" global u_hist global t_hist t_hist.append(t) u_hist.append(1.0 * u) # main program: if __name__ == '__main__': parser = argparse.ArgumentParser() paa = parser.add_argument paa( '--grid', '-g', type=int, nargs=2, default=[100, 100], dest='grid', help="Cells in the grid, e.g. --grid 100 200", ) paa( '--partition', '-p', type=int, nargs=2, default=None, help="Process partition grid, e.g. --partition 4 2 for 4x2", ) paa('-c', type=float, default=1.0, help="Wave speed (I think)") paa('-Ly', type=float, default=1.0, help="system size (in y)") paa('-Lx', type=float, default=1.0, help="system size (in x)") paa('-t', '--tstop', type=float, default=1.0, help="Time units to run") paa( '--profile', type=str, default=u'default', help="Specify the ipcluster profile for the client to connect to.", ) paa( '--save', action='store_true', help="Add this flag to save the time/wave history during the run.", ) paa( '--scalar', action='store_true', help="Also run with scalar interior implementation, to see vector speedup.", ) ns = parser.parse_args() # set up arguments grid = ns.grid partition = ns.partition Lx = ns.Lx Ly = ns.Ly c = ns.c tstop = ns.tstop if ns.save: user_action = wave_saver else: user_action = None num_cells = 1.0 * (grid[0] - 1) * (grid[1] - 1) final_test = True # create the Client rc = ipp.Client(profile=ns.profile) num_procs = len(rc.ids) if partition is None: partition = [num_procs, 1] else: num_procs = min(num_procs, partition[0] * partition[1]) assert ( partition[0] * partition[1] == num_procs ), "can't map partition %s to %i engines" % (partition, num_procs) # construct the View: view = rc[:num_procs] print("Running %s system on %s processes until %f" % (grid, partition, tstop)) # functions defining initial/boundary/source conditions def I(x, y): from numpy import exp return 1.5 * exp(-100 * ((x - 0.5) ** 2 + (y - 0.5) ** 2)) def f(x, y, t): return 0.0 # from numpy import exp,sin # return 10*exp(-(x - sin(100*t))**2) def bc(x, y, t): return 0.0 # initialize t_hist/u_hist for saving the state at each step (optional) view['t_hist'] = [] view['u_hist'] = [] # set vector/scalar implementation details impl = {} impl['ic'] = 'vectorized' impl['inner'] = 'scalar' impl['bc'] = 'vectorized' # execute some files so that the classes we need will be defined on the engines: view.execute('import numpy') view.run('communicator.py') view.run('RectPartitioner.py') view.run('wavesolver.py') # scatter engine IDs view.scatter('my_id', range(num_procs), flatten=True) # create the engine connectors view.execute('com = EngineCommunicator()') # gather the connection information into a single dict ar = view.apply_async(lambda: com.info) # noqa: F821 peers = ar.get_dict() # print peers # this is a dict, keyed by engine ID, of the connection info for the EngineCommunicators # setup remote partitioner # note that Reference means that the argument passed to setup_partitioner will be the # object named 'com' in the engine's namespace view.apply_sync( setup_partitioner, ipp.Reference('com'), peers, ipp.Reference('my_id'), num_procs, grid, partition, ) time.sleep(1) # convenience lambda to call solver.solve: _solve = lambda *args, **kwargs: solver.solve(*args, **kwargs) if ns.scalar: impl['inner'] = 'scalar' # setup remote solvers view.apply_sync( setup_solver, I, f, c, bc, Lx, Ly, partitioner=ipp.Reference('partitioner'), dt=0, implementation=impl, ) # run first with element-wise Python operations for each cell t0 = time.time() ar = view.apply_async( _solve, tstop, dt=0, verbose=True, final_test=final_test, user_action=user_action, ) if final_test: # this sum is performed element-wise as results finish s = sum(ar) # the L2 norm (RMS) of the result: norm = sqrt(s / num_cells) else: norm = -1 t1 = time.time() print('scalar inner-version, Wtime=%g, norm=%g' % (t1 - t0, norm)) # run again with faster numpy-vectorized inner implementation: impl['inner'] = 'vectorized' # setup remote solvers view.apply_sync( setup_solver, I, f, c, bc, Lx, Ly, partitioner=ipp.Reference('partitioner'), dt=0, implementation=impl, ) t0 = time.time() ar = view.apply_async( _solve, tstop, dt=0, verbose=True, final_test=final_test, user_action=user_action, ) if final_test: # this sum is performed element-wise as results finish s = sum(ar) # the L2 norm (RMS) of the result: norm = sqrt(s / num_cells) else: norm = -1 t1 = time.time() print('vector inner-version, Wtime=%g, norm=%g' % (t1 - t0, norm)) # if ns.save is True, then u_hist stores the history of u as a list # If the partion scheme is Nx1, then u can be reconstructed via 'gather': if ns.save and partition[-1] == 1: import matplotlib.pyplot as plt view.execute('u_last=u_hist[-1]') u_last = view.gather('u_last', block=True) plt.pcolor(u_last) plt.show() ipyparallel-7.1.0/docs/source/examples/wave2D/wavesolver.py000077500000000000000000000266161412533144600240230ustar00rootroot00000000000000#!/usr/bin/env python """ A simple WaveSolver class for evolving the wave equation in 2D. This works in parallel by using a RectPartitioner object. Authors ------- * Xing Cai * Min Ragan-Kelley """ import time from numpy import arange from numpy import newaxis from numpy import sqrt from numpy import zeros def iseq(start=0, stop=None, inc=1): """ Generate integers from start to (and including!) stop, with increment of inc. Alternative to range/xrange. """ if stop is None: # allow isequence(3) to be 0, 1, 2, 3 # take 1st arg as stop, start as 0, and inc=1 stop = start start = 0 inc = 1 return arange(start, stop + inc, inc) class WaveSolver: """ Solve the 2D wave equation u_tt = u_xx + u_yy + f(x,y,t) with u = bc(x,y,t) on the boundary and initial condition du/dt = 0. Parallelization by using a RectPartitioner object 'partitioner' nx and ny are the total number of global grid cells in the x and y directions. The global grid points are numbered as (0,0), (1,0), (2,0), ..., (nx,0), (0,1), (1,1), ..., (nx, ny). dt is the time step. If dt<=0, an optimal time step is used. tstop is the stop time for the simulation. I, f are functions: I(x,y), f(x,y,t) user_action: function of (u, x, y, t) called at each time level (x and y are one-dimensional coordinate vectors). This function allows the calling code to plot the solution, compute errors, etc. implementation: a dictionary specifying how the initial condition ('ic'), the scheme over inner points ('inner'), and the boundary conditions ('bc') are to be implemented. Normally, values are legal: 'scalar' or 'vectorized'. 'scalar' means straight loops over grid points, while 'vectorized' means special NumPy vectorized operations. If a key in the implementation dictionary is missing, it defaults in this function to 'scalar' (the safest strategy). Note that if 'vectorized' is specified, the functions I, f, and bc must work in vectorized mode. It is always recommended to first run the 'scalar' mode and then compare 'vectorized' results with the 'scalar' results to check that I, f, and bc work. verbose: true if a message at each time step is written, false implies no output during the simulation. final_test: true means the discrete L2-norm of the final solution is to be computed. """ def __init__( self, I, f, c, bc, Lx, Ly, partitioner=None, dt=-1, user_action=None, implementation={ 'ic': 'vectorized', # or 'scalar' 'inner': 'vectorized', 'bc': 'vectorized', }, ): nx = partitioner.global_num_cells[0] # number of global cells in x dir ny = partitioner.global_num_cells[1] # number of global cells in y dir dx = Lx / float(nx) dy = Ly / float(ny) loc_nx, loc_ny = partitioner.get_num_loc_cells() nx = loc_nx ny = loc_ny # now use loc_nx and loc_ny instead lo_ix0 = partitioner.subd_lo_ix[0] lo_ix1 = partitioner.subd_lo_ix[1] hi_ix0 = partitioner.subd_hi_ix[0] hi_ix1 = partitioner.subd_hi_ix[1] x = iseq(dx * lo_ix0, dx * hi_ix0, dx) # local grid points in x dir y = iseq(dy * lo_ix1, dy * hi_ix1, dy) # local grid points in y dir self.x = x self.y = y xv = x[:, newaxis] # for vectorized expressions with f(xv,yv) yv = y[newaxis, :] # -- " -- if dt <= 0: dt = (1 / float(c)) * (1 / sqrt(1 / dx ** 2 + 1 / dy ** 2)) # max time step Cx2 = (c * dt / dx) ** 2 Cy2 = (c * dt / dy) ** 2 dt2 = dt ** 2 # help variables u = zeros((nx + 1, ny + 1)) # solution array u_1 = u.copy() # solution at t-dt u_2 = u.copy() # solution at t-2*dt # preserve for self.solve implementation = dict(implementation) # copy if 'ic' not in implementation: implementation['ic'] = 'scalar' if 'bc' not in implementation: implementation['bc'] = 'scalar' if 'inner' not in implementation: implementation['inner'] = 'scalar' self.implementation = implementation self.Lx = Lx self.Ly = Ly self.I = I self.f = f self.c = c self.bc = bc self.user_action = user_action self.partitioner = partitioner # set initial condition (pointwise - allows straight if-tests in I(x,y)): t = 0.0 if implementation['ic'] == 'scalar': for i in range(0, nx + 1): for j in range(0, ny + 1): u_1[i, j] = I(x[i], y[j]) for i in range(1, nx): for j in range(1, ny): u_2[i, j] = ( u_1[i, j] + 0.5 * Cx2 * (u_1[i - 1, j] - 2 * u_1[i, j] + u_1[i + 1, j]) + 0.5 * Cy2 * (u_1[i, j - 1] - 2 * u_1[i, j] + u_1[i, j + 1]) + dt2 * f(x[i], y[j], 0.0) ) # boundary values of u_2 (equals u(t=dt) due to du/dt=0) i = 0 for j in range(0, ny + 1): u_2[i, j] = bc(x[i], y[j], t + dt) j = 0 for i in range(0, nx + 1): u_2[i, j] = bc(x[i], y[j], t + dt) i = nx for j in range(0, ny + 1): u_2[i, j] = bc(x[i], y[j], t + dt) j = ny for i in range(0, nx + 1): u_2[i, j] = bc(x[i], y[j], t + dt) elif implementation['ic'] == 'vectorized': u_1 = I(xv, yv) u_2[1:nx, 1:ny] = ( u_1[1:nx, 1:ny] + 0.5 * Cx2 * (u_1[0 : nx - 1, 1:ny] - 2 * u_1[1:nx, 1:ny] + u_1[2 : nx + 1, 1:ny]) + 0.5 * Cy2 * (u_1[1:nx, 0 : ny - 1] - 2 * u_1[1:nx, 1:ny] + u_1[1:nx, 2 : ny + 1]) + dt2 * (f(xv[1:nx, 1:ny], yv[1:nx, 1:ny], 0.0)) ) # boundary values (t=dt): i = 0 u_2[i, :] = bc(x[i], y, t + dt) j = 0 u_2[:, j] = bc(x, y[j], t + dt) i = nx u_2[i, :] = bc(x[i], y, t + dt) j = ny u_2[:, j] = bc(x, y[j], t + dt) if user_action is not None: user_action(u_1, x, y, t) # allow user to plot etc. # print(list(self.us[2][2])) self.us = (u, u_1, u_2) def solve(self, tstop, dt=-1, user_action=None, verbose=False, final_test=False): t0 = time.time() f = self.f c = self.c bc = self.bc partitioner = self.partitioner implementation = self.implementation nx = partitioner.global_num_cells[0] # number of global cells in x dir ny = partitioner.global_num_cells[1] # number of global cells in y dir dx = self.Lx / float(nx) dy = self.Ly / float(ny) loc_nx, loc_ny = partitioner.get_num_loc_cells() nx = loc_nx ny = loc_ny # now use loc_nx and loc_ny instead x = self.x y = self.y xv = x[:, newaxis] # for vectorized expressions with f(xv,yv) yv = y[newaxis, :] # -- " -- if dt <= 0: dt = (1 / float(c)) * (1 / sqrt(1 / dx ** 2 + 1 / dy ** 2)) # max time step Cx2 = (c * dt / dx) ** 2 Cy2 = (c * dt / dy) ** 2 dt2 = dt ** 2 # help variables # id for the four possible neighbor subdomains lower_x_neigh = partitioner.lower_neighbors[0] upper_x_neigh = partitioner.upper_neighbors[0] lower_y_neigh = partitioner.lower_neighbors[1] upper_y_neigh = partitioner.upper_neighbors[1] u, u_1, u_2 = self.us # u_1 = self.u_1 t = 0.0 while t <= tstop: t_old = t t += dt if verbose: print('solving (%s version) at t=%g' % (implementation['inner'], t)) # update all inner points: if implementation['inner'] == 'scalar': for i in range(1, nx): for j in range(1, ny): u[i, j] = ( -u_2[i, j] + 2 * u_1[i, j] + Cx2 * (u_1[i - 1, j] - 2 * u_1[i, j] + u_1[i + 1, j]) + Cy2 * (u_1[i, j - 1] - 2 * u_1[i, j] + u_1[i, j + 1]) + dt2 * f(x[i], y[j], t_old) ) elif implementation['inner'] == 'vectorized': u[1:nx, 1:ny] = ( -u_2[1:nx, 1:ny] + 2 * u_1[1:nx, 1:ny] + Cx2 * ( u_1[0 : nx - 1, 1:ny] - 2 * u_1[1:nx, 1:ny] + u_1[2 : nx + 1, 1:ny] ) + Cy2 * ( u_1[1:nx, 0 : ny - 1] - 2 * u_1[1:nx, 1:ny] + u_1[1:nx, 2 : ny + 1] ) + dt2 * f(xv[1:nx, 1:ny], yv[1:nx, 1:ny], t_old) ) # insert boundary conditions (if there's no neighbor): if lower_x_neigh < 0: if implementation['bc'] == 'scalar': i = 0 for j in range(0, ny + 1): u[i, j] = bc(x[i], y[j], t) elif implementation['bc'] == 'vectorized': u[0, :] = bc(x[0], y, t) if upper_x_neigh < 0: if implementation['bc'] == 'scalar': i = nx for j in range(0, ny + 1): u[i, j] = bc(x[i], y[j], t) elif implementation['bc'] == 'vectorized': u[nx, :] = bc(x[nx], y, t) if lower_y_neigh < 0: if implementation['bc'] == 'scalar': j = 0 for i in range(0, nx + 1): u[i, j] = bc(x[i], y[j], t) elif implementation['bc'] == 'vectorized': u[:, 0] = bc(x, y[0], t) if upper_y_neigh < 0: if implementation['bc'] == 'scalar': j = ny for i in range(0, nx + 1): u[i, j] = bc(x[i], y[j], t) elif implementation['bc'] == 'vectorized': u[:, ny] = bc(x, y[ny], t) # communication partitioner.update_internal_boundary(u) if user_action is not None: user_action(u, x, y, t) # update data structures for next step u_2, u_1, u = u_1, u, u_2 t1 = time.time() print( 'my_id=%2d, dt=%g, %s version, slice_copy=%s, net Wtime=%g' % ( partitioner.my_id, dt, implementation['inner'], partitioner.slice_copy, t1 - t0, ) ) # save the us self.us = u, u_1, u_2 # check final results; compute discrete L2-norm of the solution if final_test: loc_res = 0.0 for i in iseq(start=1, stop=nx - 1): for j in iseq(start=1, stop=ny - 1): loc_res += u_1[i, j] ** 2 return loc_res return dt ipyparallel-7.1.0/docs/source/index.md000066400000000000000000000031661412533144600177270ustar00rootroot00000000000000# Using IPython for parallel computing > ```{eval-rst} > > :Release: |release| > :Date: |today| > ``` (install)= ## Installing IPython Parallel As of 4.0, IPython parallel is now a standalone package called {mod}`ipyparallel`. You can install it with: ``` pip install ipyparallel ``` or: ``` conda install ipyparallel ``` As of IPython Parallel 7, this will include installing/enabling an extension for both the classic Jupyter Notebook and JupyterLab ≥ 3.0. ## Quickstart IPython Parallel A quick example to: 1. allocate a cluster (collection of IPython engines for use in parallel) 2. run a collection of tasks on the cluster 3. wait interactively for results 4. cleanup resources after the task is done ```python import time import ipyparallel as ipp task_durations = [1] * 25 # request a cluster with ipp.Cluster() as rc: # get a view on the cluster view = rc.load_balanced_view() # submit the tasks asyncresult = view.map_async(time.sleep, task_durations) # wait interactively for results asyncresult.wait_interactive() # retrieve actual results result = asyncresult.get() # at this point, the cluster processes have been shutdown ``` Follow the [tutorial][] to learn more. [tutorial]: ./tutorial/index ## Contents ```{toctree} :maxdepth: 2 tutorial/index reference/index ``` ```{toctree} :maxdepth: 2 examples/Index ``` ```{toctree} :maxdepth: 1 changelog ``` # IPython Parallel API ```{toctree} :maxdepth: 2 api/ipyparallel.rst ``` # Indices and tables - {ref}`genindex` - {ref}`modindex` - {ref}`search` ipyparallel-7.1.0/docs/source/links.txt000066400000000000000000000105351412533144600201550ustar00rootroot00000000000000.. This (-*- rst -*-) format file contains commonly used link targets and name substitutions. It may be included in many files, therefore it should only contain link targets and name substitutions. Try grepping for "^\.\. _" to find plausible candidates for this list. NOTE: this file must have an extension *opposite* to that of the main reST files in the manuals, so that we can include it with ".. include::" directives, but without triggering warnings from Sphinx for not being listed in any toctree. Since IPython uses .txt for the main files, this one will use .rst. NOTE: reST targets are __not_case_sensitive__, so only one target definition is needed for ipython, IPython, etc. NOTE: Some of these were taken from the nipy links compendium. .. Main IPython links .. _ipython: https://ipython.org .. _`ipython manual`: https://ipython.org/documentation.html .. _ipython_github: https://github.com/ipython/ipython/ .. _ipython_github_repo: https://github.com/ipython/ipython/ .. _ipython_downloads: https://ipython.org/download.html .. _ipython_pypi: https://pypi.python.org/pypi/ipython .. _nbviewer: https://nbviewer.ipython.org .. _ZeroMQ: https://zeromq.org .. Documentation tools and related links .. _graphviz: https://www.graphviz.org .. _Sphinx: https://sphinx.pocoo.org .. _`Sphinx reST`: https://sphinx.pocoo.org/rest.html .. _sampledoc: https://matplotlib.org/sampledoc .. _reST: https://docutils.sourceforge.net/rst.html .. _docutils: https://docutils.sourceforge.net .. _lyx: https://www.lyx.org .. _pep8: https://www.python.org/dev/peps/pep-0008 .. _numpy_coding_guide: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt .. Licenses .. _GPL: https://www.gnu.org/licenses/gpl.html .. _BSD: https://www.opensource.org/licenses/bsd-license.php .. _LGPL: https://www.gnu.org/copyleft/lesser.html .. Other python projects .. _numpy: https://numpy.scipy.org .. _scipy: https://www.scipy.org .. _scipy_conference: https://conference.scipy.org .. _matplotlib: https://matplotlib.org .. _pythonxy: https://code.google.com/p/pythonxy/ .. _ETS: https://code.enthought.com/projects/tool-suite.php .. _EPD: https://www.enthought.com/products/epd.php .. _python: https://www.python.org .. _mayavi: https://code.enthought.com/projects/mayavi .. _sympy: https://code.google.com/p/sympy .. _sage: https://sagemath.org .. _pydy: https://code.google.com/p/pydy .. _vpython: https://vpython.org .. _cython: https://cython.org .. _software carpentry: https://software-carpentry.org .. Not so python scientific computing tools .. _matlab: https://www.mathworks.com .. _VTK: https://vtk.org .. Other organizations .. _enthought: https://www.enthought.com .. _kitware: https://www.kitware.com .. _netlib: https://netlib.org .. Other tools and projects .. _indefero: https://www.indefero.net .. _git: https://git-scm.com .. _github: https://github.com .. _Markdown: https://daringfireball.net/projects/markdown/syntax .. _Running Code in the IPython Notebook: notebook_p1_ .. _notebook_p1: https://nbviewer.ipython.org/urls/raw.github.com/ipython/ipython/1.x/examples/notebooks/Part%25201%2520-%2520Running%2520Code.ipynb .. _Basic Output: notebook_p2_ .. _notebook_p2: https://nbviewer.ipython.org/urls/raw.github.com/ipython/ipython/1.x/examples/notebooks/Part%202%20-%20Basic%20Output.ipynb .. _Plotting with Matplotlib: notebook_p3_ .. _notebook_p3: https://nbviewer.ipython.org/urls/raw.github.com/ipython/ipython/1.x/examples/notebooks/Part%203%20-%20Plotting%20with%20Matplotlib.ipynb .. _Markdown Cells: notebook_p4_ .. _notebook_p4: https://nbviewer.ipython.org/urls/raw.github.com/ipython/ipython/1.x/examples/notebooks/Part%204%20-%20Markdown%20Cells.ipynb .. _Rich Display System: notebook_p5_ .. _notebook_p5: https://nbviewer.ipython.org/urls/raw.github.com/ipython/ipython/1.x/examples/notebooks/Part%205%20-%20Rich%20Display%20System.ipynb .. _notebook_custom_display: https://nbviewer.ipython.org/urls/raw.github.com/ipython/ipython/1.x/examples/notebooks/Custom%20Display%20Logic.ipynb .. _Frontend/Kernel Model: notebook_two_proc_ .. _notebook_two_proc: https://nbviewer.ipython.org/urls/raw.github.com/ipython/ipython/1.x/examples/notebooks/Frontend-Kernel%20Model.ipynb .. _Cell magics: notebook_cell_magics_ .. _notebook_cell_magics: https://nbviewer.ipython.org/urls/raw.github.com/ipython/ipython/1.x/examples/notebooks/Cell%20Magics.ipynb ipyparallel-7.1.0/docs/source/reference/000077500000000000000000000000001412533144600202265ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/reference/connections.md000066400000000000000000000142721412533144600231000ustar00rootroot00000000000000(parallel-connections)= # Connection Diagrams of The IPython ZMQ Cluster This is a quick summary and illustration of the connections involved in the ZeroMQ based IPython cluster for parallel computing. ## All Connections The IPython cluster consists of a Controller, and one or more each of clients and engines. The goal of the Controller is to manage and monitor the connections and communications between the clients and the engines. The Controller is no longer a single process entity, but rather a collection of processes - specifically one Hub, and 4 (or more) Schedulers. It is important for security/practicality reasons that all connections be inbound to the controller processes. The arrows in the figures indicate the direction of the connection. ```{figure} figs/allconnections.png :align: center :alt: IPython cluster connections :width: 432px All the connections involved in connecting one client to one engine. ``` The Controller consists of 1-5 processes. Central to the cluster is the **Hub**, which monitors engine state, execution traffic, and handles registration and notification. The Hub includes a Heartbeat Monitor for keeping track of engines that are alive. Outside the Hub are 4 **Schedulers**. These devices are very small pure-C MonitoredQueue processes (or optionally threads) that relay messages very fast, but also send a copy of each message along a side socket to the Hub. The MUX queue and Control queue are MonitoredQueue ØMQ devices which relay explicitly addressed messages from clients to engines, and their replies back up. The Balanced queue performs load-balancing destination-agnostic scheduling. It may be a MonitoredQueue device, but may also be a Python Scheduler that behaves externally in an identical fashion to MQ devices, but with additional internal logic. stdout/err are also propagated from the Engines to the clients via a PUB/SUB MonitoredQueue. ### Registration ```{figure} figs/queryfade.png :align: center :alt: IPython Registration connections :width: 432px Engines and Clients only need to know where the Query `ROUTER` is located to start connecting. ``` Once a controller is launched, the only information needed for connecting clients and/or engines is the IP/port of the Hub's `ROUTER` socket called the Registrar. This socket handles connections from both clients and engines, and replies with the remaining information necessary to establish the remaining connections. Clients use this same socket for querying the Hub for state information. ### Heartbeat ```{figure} figs/hbfade.png :align: center :alt: IPython Heartbeat connections :width: 432px The heartbeat sockets. ``` The heartbeat process has been described elsewhere. To summarize: the Heartbeat Monitor publishes a distinct message periodically via a `PUB` socket. Each engine has a `zmq.FORWARDER` device with a `SUB` socket for input, and `DEALER` socket for output. The `SUB` socket is connected to the `PUB` socket labeled _ping_, and the `DEALER` is connected to the `ROUTER` labeled _pong_. This results in the same message being relayed back to the Heartbeat Monitor with the addition of the `DEALER` prefix. The Heartbeat Monitor receives all the replies via an `ROUTER` socket, and identifies which hearts are still beating by the `zmq.IDENTITY` prefix of the `DEALER` sockets, which information the Hub uses to notify clients of any changes in the available engines. ### Schedulers ```{figure} figs/queuefade.png :align: center :alt: IPython Queue connections :width: 432px Control message scheduler on the left, execution (apply) schedulers on the right. ``` The controller has at least three Schedulers. These devices are primarily for relaying messages between clients and engines, but the Hub needs to see those messages for its own purposes. Since no Python code may exist between the two sockets in a queue, all messages sent through these queues (both directions) are also sent via a `PUB` socket to a monitor, which allows the Hub to monitor queue traffic without interfering with it. For tasks, the engine need not be specified. Messages sent to the `ROUTER` socket from the client side are assigned to an engine via ZMQ's `DEALER` round-robin load balancing. Engine replies are directed to specific clients via the IDENTITY of the client, which is received as a prefix at the Engine. For Multiplexing, `ROUTER` is used for both in and output sockets in the device. Clients must specify the destination by the `zmq.IDENTITY` of the `ROUTER` socket connected to the downstream end of the device. At the Kernel level, both of these `ROUTER` sockets are treated in the same way as the `REP` socket in the serial version (except using ZMQStreams instead of explicit sockets). Execution can be done in a load-balanced (engine-agnostic) or multiplexed (engine-specified) manner. The sockets on the Client and Engine are the same for these two actions, but the scheduler used determines the actual behavior. This routing is done via the `zmq.IDENTITY` of the upstream sockets in each MonitoredQueue. ### IOPub ```{figure} figs/iopubfade.png :align: center :alt: IOPub connections :width: 432px stdout/err are published via a `PUB/SUB` MonitoredQueue ``` On the kernels, stdout/stderr are captured and published via a `PUB` socket. These `PUB` sockets all connect to a `SUB` socket input of a MonitoredQueue, which subscribes to all messages. They are then republished via another `PUB` socket, which can be subscribed by the clients. ### Client connections ```{figure} figs/queryfade.png :align: center :alt: IPython client query connections :width: 432px Clients connect to an `ROUTER` socket to query the hub. ``` The hub's registrar `ROUTER` socket also listens for queries from clients as to queue status, and control instructions. Clients connect to this socket via an `DEALER` during registration. ```{figure} figs/notiffade.png :align: center :alt: IPython Registration connections :width: 432px Engine registration events are published via a `PUB` socket. ``` The Hub publishes all registration/unregistration events via a `PUB` socket. This allows clients to stay up to date with what engines are available by subscribing to the feed with a `SUB` socket. Other processes could selectively subscribe to just registration or unregistration events. ipyparallel-7.1.0/docs/source/reference/dag_dependencies.md000066400000000000000000000132731412533144600240170ustar00rootroot00000000000000(dag-dependencies)= # DAG Dependencies Often, parallel workflow is described in terms of a [Directed Acyclic Graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) or DAG. A popular library for working with Graphs is [NetworkX]. Here, we will walk through a demo mapping a nx DAG to task dependencies. The full script that runs this demo can be found in {file}`examples/parallel/dagdeps.py`. ## Why are DAGs good for task dependencies? The 'G' in DAG is 'Graph'. A Graph is a collection of **nodes** and **edges** that connect the nodes. For our purposes, each node would be a task, and each edge would be a dependency. The 'D' in DAG stands for 'Directed'. This means that each edge has a direction associated with it. So we can interpret the edge (a,b) as meaning that b depends on a, whereas the edge (b,a) would mean a depends on b. The 'A' is 'Acyclic', meaning that there must not be any closed loops in the graph. This is important for dependencies, because if a loop were closed, then a task could ultimately depend on itself, and never be able to run. If your workflow can be described as a DAG, then it is impossible for your dependencies to cause a deadlock. ## A Sample DAG Here, we have a very simple 5-node DAG: ```{figure} figs/simpledag.* :width: 600px ``` With NetworkX, an arrow is a fattened bit on the edge. Here, we can see that task 0 depends on nothing, and can run immediately. 1 and 2 depend on 0; 3 depends on 1 and 2; and 4 depends only on 1. A possible sequence of events for this workflow: 0. Task 0 can run right away 1. 0 finishes, so 1,2 can start 2. 1 finishes, 3 is still waiting on 2, but 4 can start right away 3. 2 finishes, and 3 can finally start Further, taking failures into account, assuming all dependencies are run with the default `success=True,failure=False`, the following cases would occur for each node's failure: 0. fails: all other tasks fail as Impossible 1. 2 can still succeed, but 3,4 are unreachable 2. 3 becomes unreachable, but 4 is unaffected 3. and 4. are terminal, and can have no effect on other nodes The code to generate the simple DAG: ```python import networkx as nx G = nx.DiGraph() # add 5 nodes, labeled 0-4: map(G.add_node, range(5)) # 1,2 depend on 0: G.add_edge(0,1) G.add_edge(0,2) # 3 depends on 1,2 G.add_edge(1,3) G.add_edge(2,3) # 4 depends on 1 G.add_edge(1,4) # now draw the graph: pos = { 0 : (0,0), 1 : (1,1), 2 : (-1,1), 3 : (0,2), 4 : (2,2)} nx.draw(G, pos, edge_color='r') ``` For demonstration purposes, we have a function that generates a random DAG with a given number of nodes and edges. ```{literalinclude} ../examples/dagdeps.py :language: python :lines: 20-36 ``` So first, we start with a graph of 32 nodes, with 128 edges: ```ipython In [2]: G = random_dag(32,128) ``` Now, we need to build our dict of jobs corresponding to the nodes on the graph: ```ipython In [3]: jobs = {} # in reality, each job would presumably be different # randomwait is a function that sleeps for a random interval In [4]: for node in G: ...: jobs[node] = randomwait ``` Once we have a dict of jobs matching the nodes on the graph, we can start submitting jobs, and linking up the dependencies. Since we don't know a job's msg_id until it is submitted, which is necessary for building dependencies, it is critical that we don't submit any jobs before other jobs it may depend on. Fortunately, NetworkX provides a {meth}`topological_sort` method which ensures exactly this. It presents an iterable, that guarantees that when you arrive at a node, you have already visited all the nodes it on which it depends: ```ipython In [5]: rc = ipp.Client() In [5]: view = rc.load_balanced_view() In [6]: results = {} In [7]: for node in nx.topological_sort(G): ...: # get list of AsyncResult objects from nodes ...: # leading into this one as dependencies ...: deps = [ results[n] for n in G.predecessors(node) ] ...: # submit and store AsyncResult object ...: with view.temp_flags(after=deps, block=False): ...: results[node] = view.apply(jobs[node]) ``` Now that we have submitted all the jobs, we can wait for the results: ```ipython In [8]: view.wait(results.values()) ``` Now, at least we know that all the jobs ran and did not fail (`r.get()` would have raised an error if a task failed). But we don't know that the ordering was properly respected. For this, we can use the {attr}`metadata` attribute of each AsyncResult. These objects store a variety of metadata about each task, including various timestamps. We can validate that the dependencies were respected by checking that each task was started after all of its predecessors were completed: ```{literalinclude} ../examples/dagdeps.py :language: python :lines: 64-70 ``` We can also validate the graph visually. By drawing the graph with each node's x-position as its start time, all arrows must be pointing to the right if dependencies were respected. For spreading, the y-position will be the runtime of the task, so long tasks will be at the top, and quick, small tasks will be at the bottom. ```ipython In [10]: from matplotlib.dates import date2num In [11]: from matplotlib.cm import gist_rainbow In [12]: pos = {}; colors = {} In [12]: for node in G: ....: md = results[node].metadata ....: start = date2num(md.started) ....: runtime = date2num(md.completed) - start ....: pos[node] = (start, runtime) ....: colors[node] = md.engine_id In [13]: nx.draw(G, pos, node_list=colors.keys(), node_color=colors.values(), ....: cmap=gist_rainbow) ``` ```{figure} figs/dagdeps.* :width: 600px Time started on x, runtime on y, and color-coded by engine-id (in this case there were four engines). Edges denote dependencies. ``` [networkx]: https://networkx.org ipyparallel-7.1.0/docs/source/reference/db.md000066400000000000000000000145671412533144600211520ustar00rootroot00000000000000(parallel-db)= # IPython's Task Database ## Enabling a DB Backend The IPython Hub can store all task requests and results in a database. Currently supported backends are: MongoDB, SQLite, and an in-memory DictDB. The default is to store recent tasks in a dictionary in memory, which deletes old values if it gets too big, and only survives as long as the controller is running. Using a real database is optional due to its potential {ref}`db-cost`. You can enable one, either at the command-line: ``` $> ipcontroller --sqlitedb # or --mongodb or --nodb ``` or in your {file}`ipcontroller_config.py`: ```python c.IPController.db_class = "NoDB" c.IPController.db_class = "DictDB" # default c.IPController.db_class = "MongoDB" c.IPController.db_class = "SQLiteDB" ``` ## Using the Task Database The most common use case for this is clients requesting results for tasks they did not submit, via: ```ipython In [1]: rc.get_result(task_id) ``` However, since we have this DB backend, we provide a direct query method in the {class}`~.Client` for users who want deeper introspection into their task history. The {meth}`db_query` method of the Client is modeled after MongoDB queries, so if you have used MongoDB it should look familiar. In fact, when the MongoDB backend is in use, the query is relayed directly. When using other backends, the interface is emulated and only a subset of queries is possible. ```{seealso} MongoDB [query docs](https://docs.mongodb.com/manual/tutorial/query-documents/) ``` {meth}`Client.db_query` takes a dictionary query object, with keys from the TaskRecord key list, and values of either exact values to test, or MongoDB queries, which are dicts of The form: `{'operator' : 'argument(s)'}`. There is also an optional `keys` argument, that specifies which subset of keys should be retrieved. The default is to retrieve all keys excluding the request and result buffers. {meth}`db_query` returns a list of TaskRecord dicts. Also like MongoDB, the `msg_id` key will always be included, whether requested or not. TaskRecord keys: | Key | Type | Description | | -------------- | ----------- | ----------------------------------------------------------- | | msg_id | uuid(ascii) | The msg ID | | header | dict | The request header | | content | dict | The request content (likely empty) | | buffers | list(bytes) | buffers containing serialized request objects | | submitted | datetime | timestamp for time of submission (set by client) | | client_uuid | uuid(ascii) | IDENT of client's socket | | engine_uuid | uuid(ascii) | IDENT of engine's socket | | started | datetime | time task began execution on engine | | completed | datetime | time task finished execution (success or failure) on engine | | resubmitted | uuid(ascii) | msg_id of resubmitted task (if applicable) | | result_header | dict | header for result | | result_content | dict | content for result | | result_buffers | list(bytes) | buffers containing serialized request objects | | queue | str | The name of the queue for the task ('mux' or 'task') | | execute_input | str | Python input source | | execute_result | dict | Python output (execute_result message content) | | error | dict | Python traceback (error message content) | | stdout | str | Stream of stdout data | | stderr | str | Stream of stderr data | MongoDB operators we emulate on all backends: | Operator | Python equivalent | | -------- | ----------------- | | '\$in' | in | | '\$nin' | not in | | '\$eq' | == | | '\$ne' | != | | '\$ge' | > | | '\$gte' | >= | | '\$le' | \< | | '\$lte' | \<= | The DB Query is useful for two primary cases: 1. deep polling of task status or metadata 2. selecting a subset of tasks, on which to perform a later operation (e.g. wait on result, purge records, resubmit,...) ## Example Queries To get all msg_ids that are not completed, only retrieving their ID and start time: ```ipython In [1]: incomplete = rc.db_query({'completed' : None}, keys=['msg_id', 'started']) ``` All jobs started in the last hour by me: ```ipython In [1]: from datetime import datetime, timedelta In [2]: hourago = datetime.now() - timedelta(1./24) In [3]: recent = rc.db_query({'started' : {'$gte' : hourago }, 'client_uuid' : rc.session.session}) ``` All jobs started more than an hour ago, by clients _other than me_: ```ipython In [3]: recent = rc.db_query({'started' : {'$le' : hourago }, 'client_uuid' : {'$ne' : rc.session.session}}) ``` Result headers for all jobs on engine 3 or 4: ```ipython In [1]: uuids = map(rc._engines.get, (3,4)) In [2]: hist34 = rc.db_query({'engine_uuid' : {'$in' : uuids }, keys='result_header') ``` (db-cost)= ## Cost The advantage of the database backends is, of course, that large amounts of data can be stored that won't fit in memory. The basic DictDB 'backend' stores all of this information in a Python dictionary. This is very fast, but will run out of memory quickly if you move a lot of data around, or your cluster is to run for a long time. Unfortunately, the DB backends (SQLite and MongoDB) right now are rather slow, and can still consume large amounts of resources, particularly if large tasks or results are being created at a high frequency. For this reason, we have added {class}`~.NoDB`, a dummy backend that doesn't store any information. When you use this database, nothing is stored, and any request for results will result in a KeyError. This obviously prevents later requests for results and task resubmission from functioning, but sometimes those nice features are not as useful as keeping Hub memory under control. ipyparallel-7.1.0/docs/source/reference/details.md000066400000000000000000000514611412533144600222040ustar00rootroot00000000000000(parallel-details)= # Details of Parallel Computing with IPython ```{note} There are still many sections to fill out in this doc ``` ## Caveats First, some caveats about the detailed workings of parallel computing with 0MQ and IPython. ### Non-copying sends and numpy arrays When numpy arrays are passed as arguments to apply or via data-movement methods, they are not copied. This means that you must be careful if you are sending an array that you intend to work on. PyZMQ does allow you to track when a message has been sent so you can know when it is safe to edit the buffer, but IPython only allows for this. It is also important to note that the non-copying receive of a message is _read-only_. That means that if you intend to work in-place on an array that you have sent or received, you must copy it. This is true for both numpy arrays sent to engines and numpy arrays retrieved as results. The following will fail: ```ipython In [3]: A = numpy.zeros(2) In [4]: def setter(a): ...: a[0]=1 ...: return a In [5]: rc[0].apply_sync(setter, A) --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) in () in setter(a) RuntimeError: array is not writeable ``` If you do need to edit the array in-place, remember to copy the array if it's read-only. The {attr}`ndarray.flags.writeable` flag will tell you if you can write to an array. ```ipython In [3]: A = numpy.zeros(2) In [4]: def setter(a): ...: """only copy read-only arrays""" ...: if not a.flags.writeable: ...: a=a.copy() ...: a[0]=1 ...: return a In [5]: rc[0].apply_sync(setter, A) Out[5]: array([ 1., 0.]) # note that results will also be read-only: In [6]: _.flags.writeable Out[6]: False ``` If you want to safely edit an array in-place after _sending_ it, you must use the `track=True` flag. IPython always performs non-copying sends of arrays, which return immediately. You must instruct IPython track those messages _at send time_ in order to know for sure that the send has completed. AsyncResults have a {attr}`sent` property, and {meth}`wait_on_send` method for checking and waiting for 0MQ to finish with a buffer. ```ipython In [5]: A = numpy.random.random((1024,1024)) In [6]: view.track=True In [7]: ar = view.apply_async(lambda x: 2*x, A) In [8]: ar.sent Out[8]: False In [9]: ar.wait_on_send() # blocks until sent is True ``` ### What is sendable? If IPython doesn't know what to do with an object, it will pickle it. There is a short list of objects that are not pickled: `buffers/memoryviews`, `bytes` objects, and `numpy` arrays. These are handled specially by IPython in order to prevent extra in-memory copies of data. Sending bytes or numpy arrays will result in exactly zero in-memory copies of your data (unless the data is very small). If you have an object that provides a Python buffer interface, then you can always send that buffer without copying - and reconstruct the object on the other side in your own code. It is possible that the object reconstruction will become extensible, so you can add your own non-copying types, but this does not yet exist. #### Closures Just about anything in Python is pickleable. The one notable exception is objects (generally functions) with _closures_. Closures can be a complicated topic, but the basic principle is that functions that refer to variables in their parent scope have closures. An example of a function that uses a closure: ```python def f(a): def inner(): # inner will have a closure return a return inner f1 = f(1) f2 = f(2) f1() # returns 1 f2() # returns 2 ``` `f1` and `f2` will have closures referring to the scope in which `inner` was defined, because they use the variable 'a'. As a result, you would not be able to send `f1` or `f2` with IPython. Note that you _would_ be able to send `f`. This is only true for interactively defined functions (as are often used in decorators), and only when there are variables used inside the inner function, that are defined in the outer function. If the names are _not_ in the outer function, then there will not be a closure, and the generated function will look in `globals()` for the name: ```python def g(b): # note that `b` is not referenced in inner's scope def inner(): # this inner will *not* have a closure return a return inner g1 = g(1) g2 = g(2) g1() # raises NameError on 'a' a=5 g2() # returns 5 ``` `g1` and `g2` _will_ be sendable with IPython, and will treat the engine's namespace as globals(). The {meth}`pull` method is implemented based on this principle. If we did not provide pull, you could implement it yourself with `apply`, by returning objects out of the global namespace: ```ipython In [10]: view.apply(lambda : a) # is equivalent to In [11]: view.pull('a') ``` You can send functions with closures if you enable using dill or cloudpickle: ```ipython In [10]: rc[:].use_cloudpickle() ``` which will use a more advanced pickling library, which covers things like closures. ## Running Code There are two principal units of execution in Python: strings of Python code (e.g. 'a=5'), and Python functions. IPython is designed around the use of functions via the core Client method, called `apply`. ### Apply The principal method of remote execution is {meth}`apply`, of {class}`~ipyparallel.client.view.View` objects. The Client provides the full execution and communication API for engines via its low-level {meth}`send_apply_message` method, which is used by all higher level methods of its Views. f : The function to be called remotely args : The positional arguments passed to `f` kwargs : The keyword arguments passed to `f` flags for all views: block : Whether to wait for the result, or return immediately. False: : returns AsyncResult True: : returns actual result(s) of `f(*args, **kwargs)` if multiple targets: : list of results, matching `targets` track : whether to track non-copying sends. targets : Specify the destination of the job. if 'all' or None: : Run on all active engines if list: : Run on each specified engine if int: : Run on single engine ```{note} {class}`LoadBalancedView` uses targets to restrict possible destinations. LoadBalanced calls will always execute on exactly one engine. ``` flags only in LoadBalancedViews: after : Only for load-balanced execution (targets=None) Specify a list of msg ids as a time-based dependency. This job will only be run _after_ the dependencies have been met. follow : Only for load-balanced execution (targets=None) Specify a list of msg_ids as a location-based dependency. This job will only be run on an engine where this dependency is met. timeout : Only for load-balanced execution (targets=None) Specify an amount of time (in seconds) for the scheduler to wait for dependencies to be met before failing with a DependencyTimeout. ### execute and run For executing strings of Python code, {class}`~.DirectView` s also provide an {meth}`~.DirectView.execute` and a {meth}`~.DirectView.run` method, which rather than take functions and arguments, take Python strings. `execute` takes a string of Python code to execute, and sends it to the Engine(s). `run` is the same as `execute`, but for a _filename_ rather than a string. It is a wrapper that does something very similar to `execute(open(f).read())`. ```{note} TODO: Examples for execute and run ``` ## Views The principal extension of the {class}`~parallel.Client` is the {class}`~parallel.View` class. The client is typically a singleton for connecting to a cluster, and presents a low-level interface to the Hub and Engines. Most real usage will involve creating one or more {class}`~parallel.View` objects for working with engines in various ways. ### DirectView The {class}`.DirectView` is the class for the IPython {ref}`Multiplexing Interface `. #### Creating a DirectView DirectViews can be created in two ways, by index access to a client, or by a client's {meth}`view` method. Index access to a Client works in a few ways. First, you can create DirectViews to single engines by accessing the client by engine id: ```ipython In [2]: rc[0] Out[2]: ``` You can also create a DirectView with a list of engines: ```ipython In [2]: rc[0,1,2] Out[2]: ``` Other methods for accessing elements, such as slicing and negative indexing, work by passing the index directly to the client's {attr}`ids` list, so: ```ipython # negative index In [2]: rc[-1] Out[2]: # or slicing: In [3]: rc[::2] Out[3]: ``` are always the same as: ```ipython In [2]: rc[rc.ids[-1]] Out[2]: In [3]: rc[rc.ids[::2]] Out[3]: ``` Also note that the slice is evaluated at the time of construction of the DirectView, so the targets will not change over time if engines are added/removed from the cluster. #### Execution via DirectView The DirectView is the simplest way to work with one or more engines directly (hence the name). For instance, to get the process ID of all your engines: ```ipython In [5]: import os In [6]: dview.apply_sync(os.getpid) Out[6]: [1354, 1356, 1358, 1360] ``` Or to see the hostname of the machine they are on: ```ipython In [5]: import socket In [6]: dview.apply_sync(socket.gethostname) Out[6]: ['tesla', 'tesla', 'edison', 'edison', 'edison'] ``` ```{note} TODO: expand on direct execution ``` #### Data movement via DirectView Since a Python namespace is a {class}`dict`, {class}`DirectView` objects provide dictionary-style access by key and methods such as {meth}`get` and {meth}`update` for convenience. This make the remote namespaces of the engines appear as a local dictionary. Underneath, these methods call {meth}`apply`: ```ipython In [51]: dview['a']=['foo','bar'] In [52]: dview['a'] Out[52]: [ ['foo', 'bar'], ['foo', 'bar'], ['foo', 'bar'], ['foo', 'bar'] ] ``` ### Scatter and gather Sometimes it is useful to partition a sequence and push the partitions to different engines. In MPI language, this is know as scatter/gather and we follow that terminology. However, it is important to remember that in IPython's {class}`Client` class, {meth}`scatter` is from the interactive IPython session to the engines and {meth}`gather` is from the engines back to the interactive IPython session. For scatter/gather operations between engines, MPI should be used: ```ipython In [58]: dview.scatter('a',range(16)) Out[58]: [None,None,None,None] In [59]: dview['a'] Out[59]: [ [0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15] ] In [60]: dview.gather('a') Out[60]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] ``` ### Push and pull {meth}`~ipyparallel.client.view.DirectView.push` {meth}`~ipyparallel.client.view.DirectView.pull` ```{note} TODO: write this section ``` ### LoadBalancedView The {class}`~.LoadBalancedView` is the class for load-balanced execution via the task scheduler. These views always run tasks on exactly one engine, but let the scheduler determine where that should be, allowing load-balancing of tasks. The LoadBalancedView does allow you to specify restrictions on where and when tasks can execute, for more complicated load-balanced workflows. ## Data Movement Since the {class}`~.LoadBalancedView` does not know where execution will take place, explicit data movement methods like push/pull and scatter/gather do not make sense, and are not provided. ## Results ### AsyncResults Our primary representation of the results of remote execution is the {class}`~.AsyncResult` object, based on the object of the same name in the built-in {py:mod}`multiprocessing.pool` module. Our version provides a superset of that interface, and starting in 6.0 is a subclass of {class}`concurrent.futures.Future`. The basic principle of the AsyncResult is the encapsulation of one or more results not yet completed. Execution methods (including data movement, such as push/pull) will all return AsyncResults when `block=False`. ### The mp.pool.AsyncResult interface The basic interface of the AsyncResult is exactly that of the AsyncResult in {py:mod}`multiprocessing.pool`, and consists of four methods: % AsyncResult spec directly from docs.python.org ```{eval-rst} .. class:: AsyncResult The stdlib AsyncResult spec .. method:: wait([timeout]) Wait until the result is available or until *timeout* seconds pass. This method always returns ``None``. .. method:: ready() Return whether the call has completed. .. method:: successful() Return whether the call completed without raising an exception. Will raise :exc:`AssertionError` if the result is not ready. .. method:: get([timeout]) Return the result when it arrives. If *timeout* is not ``None`` and the result does not arrive within *timeout* seconds then :exc:`TimeoutError` is raised. If the remote call raised an exception then that exception will be reraised as a :exc:`RemoteError` by :meth:`get`. ``` While an AsyncResult is not done, you can check on it with its {meth}`ready` method, which will return whether the AR is done. You can also wait on an AsyncResult with its {meth}`wait` method. This method blocks until the result arrives. If you don't want to wait forever, you can pass a timeout (in seconds) as an argument to {meth}`wait`. {meth}`wait` will _always return None_, and should never raise an error. {meth}`ready` and {meth}`wait` are insensitive to the success or failure of the call. After a result is done, {meth}`successful` will tell you whether the call completed without raising an exception. If you want the result of the call, you can use {meth}`get`. Initially, {meth}`get` behaves just like {meth}`wait`, in that it will block until the result is ready, or until a timeout is met. However, unlike {meth}`wait`, {meth}`get` will raise a {exc}`TimeoutError` if the timeout is reached and the result is still not ready. If the result arrives before the timeout is reached, then {meth}`get` will return the result itself if no exception was raised, and will raise an exception if there was. Here is where we start to expand on the multiprocessing interface. Rather than raising the original exception, a RemoteError will be raised, encapsulating the remote exception with some metadata. If the AsyncResult represents multiple calls (e.g. any time `targets` is plural), then a CompositeError, a subclass of RemoteError, will be raised. ```{seealso} For more information on remote exceptions, see {ref}`the section in the Direct Interface `. ``` #### Extended interface Other extensions of the AsyncResult interface include convenience wrappers for {meth}`get`. AsyncResults have a property, {attr}`result`, with the short alias {attr}`r`, which call {meth}`get`. Since our object is designed for representing _parallel_ results, it is expected that many calls (any of those submitted via DirectView) will map results to engine IDs. We provide a {meth}`get_dict`, which is also a wrapper on {meth}`get`, which returns a dictionary of the individual results, keyed by engine ID. You can also prevent a submitted job from executing, via the AsyncResult's {meth}`abort` method. This will instruct engines to not execute the job when it arrives. The larger extension of the AsyncResult API is the {attr}`metadata` attribute. The metadata is a dictionary (with attribute access) that contains, logically enough, metadata about the execution. Metadata keys: timestamps submitted : When the task left the Client started : When the task started execution on the engine completed : When execution finished on the engine received : When the result arrived on the Client note that it is not known when the result arrived in 0MQ on the client, only when it arrived in Python via {meth}`Client.spin`, so in interactive use, this may not be strictly informative. Information about the engine engine_id : The integer id engine_uuid : The UUID of the engine output of the call error : Python exception, if there was one execute_input : The code (str) that was executed execute_result : Python output of an execute request (not apply), as a Jupyter message dictionary. stderr : stderr stream stdout : stdout (e.g. print) stream And some extended information status : either 'ok' or 'error' msg_id : The UUID of the message after : For tasks: the time-based msg_id dependencies follow : For tasks: the location-based msg_id dependencies While in most cases, the Clients that submitted a request will be the ones using the results, other Clients can also request results directly from the Hub. This is done via the Client's {meth}`get_result` method. This method will _always_ return an AsyncResult object. If the call was not submitted by the client, then it will be a subclass, called {class}`AsyncHubResult`. These behave in the same way as an AsyncResult, but if the result is not ready, waiting on an AsyncHubResult polls the Hub, which is much more expensive than the passive polling used in regular AsyncResults. The Client keeps track of all results history, results, metadata ## Querying the Hub The Hub sees all traffic that may pass through the schedulers between engines and clients. It does this so that it can track state, allowing multiple clients to retrieve results of computations submitted by their peers, as well as persisting the state to a database. queue_status > You can check the status of the queues of the engines with this command. result_status > check on results purge_results > forget results (conserve resources) ## Controlling the Engines There are a few actions you can do with Engines that do not involve execution. These messages are sent via the Control socket, and bypass any long queues of waiting execution jobs abort > Sometimes you may want to prevent a job you have submitted from running. The method > for this is {meth}`abort`. It takes a container of msg_ids, and instructs the Engines to not > run the jobs if they arrive. The jobs will then fail with an AbortedTask error. clear > You may want to purge the Engine(s) namespace of any data you have left in it. After > running `clear`, there will be no names in the Engine's namespace shutdown > You can also instruct engines (and the Controller) to terminate from a Client. This > can be useful when a job is finished, since you can shutdown all the processes with a > single command. ## Synchronization Since the Client is a synchronous object, events do not automatically trigger in your interactive session - you must poll the 0MQ sockets for incoming messages. Note that this polling _does not_ make any network requests. It performs a `select` operation, to check if messages are already in local memory, waiting to be handled. The method that handles incoming messages is {meth}`spin`. This method flushes any waiting messages on the various incoming sockets, and updates the state of the Client. If you need to wait for particular results to finish, you can use the {meth}`wait` method, which will call {meth}`spin` until the messages are no longer outstanding. Anything that represents a collection of messages, such as a list of msg_ids or one or more AsyncResult objects, can be passed as argument to wait. A timeout can be specified, which will prevent the call from blocking for more than a specified time, but the default behavior is to wait forever. The client also has an `outstanding` attribute - a `set` of msg ids that are awaiting replies. This is the default if wait is called with no arguments - i.e. wait on _all_ outstanding messages. ```{note} TODO wait example ``` ## Map Many parallel computing problems can be expressed as a `map`, or running a single program with a variety of different inputs. Python has a built-in {py:func}`map`, which does exactly this, and many parallel execution tools in Python, such as the built-in {py:class}`multiprocessing.Pool` object provide implementations of `map`. All View objects provide a {meth}`map` method as well, but the load-balanced and direct implementations differ. Views' map methods can be called on any number of sequences, but they can also take keyword arguments to influence how the work is distributed. What keyword arguments are available depends on the view being used. ```{eval-rst} .. class:: ipyparallel.DirectView :noindex: .. automethod:: map ``` ```{eval-rst} .. class:: ipyparallel.LoadBalancedView :noindex: .. automethod:: map .. automethod:: imap ``` ## Decorators and RemoteFunctions ```{note} TODO: write this section ``` {func}`~ipyparallel.client.remotefunction.parallel` {func}`~ipyparallel.client.remotefunction.remote` {class}`~ipyparallel.client.remotefunction.RemoteFunction` {class}`~ipyparallel.client.remotefunction.ParallelFunction` ## Dependencies ```{note} TODO: write this section ``` {func}`~ipyparallel.controller.dependency.depend` {func}`~ipyparallel.controller.dependency.require` {class}`~ipyparallel.controller.dependency.Dependency` ipyparallel-7.1.0/docs/source/reference/figs/000077500000000000000000000000001412533144600211565ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/reference/figs/allconnections.png000066400000000000000000000766201412533144600247120ustar00rootroot00000000000000PNG  IHDRN0*tEXtSoftwareAdobe ImageReadyqe<}2IDATx x\u6mlcldÏ/1N,!!$p|Dr44+ ?%/Me&Md@4H4hD6_lelcݳhhf4>̜g]k@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ dQ8,j9Y]DMk]Wx46;K-SV"qH(e{|(Vks\y]llkr֭[+&J V $ʂ)&Z켬Ăvk?'%ClsǵdQS-t^c6:;mζUkGD%L<(: p:NhpS@i9oomd&d:tZ;&HAۜe4:ON@p Ua|Si0j &MbPU+rQ^q1Cy-:S}h| sQBg b)ZZ%o' L m4zfbkt,0fOԹ"~_NҠ 4\:*a^Gۺ\*a4Ϲc5@ !0~smjsuRF%0n;k5q]tl5=6aWdvzL[T"*۲+ʣWc]~:TǢS3>Or'zi_cEd(7EڟC: (]O7oQcM!Q`@ `}O90=qa1+Oȏ<22,ᨽt 5gž` y$&,]&,U\ɓm0AmՒf"3#|/aҤybKXH~A %/!0@ bU B'(F_KTWy'JLu#m\XhZAR8 Q!\xX" DUW E!*e)%ҥ穋.>W͚5Cm|izMw^ =k,[^9|zjWf 7} >>!τ81n `(0Wg*B8*򷎕 ĺdBK#r-'Q)mt\s ;nT?p&1,z=K8f_sñLw$? >@F%:NR #Y|"f00,wOX6d(Ʒ2D4%؛HŹ-I o%+ggiw_ k: ~Jz0^S③=J R_ʨWCqC1 #>z8':@56mܡ>ݼ\YJG=r^PNKY?=xڊ\"Q*3I}}XY) ej⮯?vڧϪ ܭ\`,+Ji3מL9ıYs}% 2&k"#bcyuKkT"#s롂: ~/be Ps c:ԧn[>Η.zGݗԣs>SQ9j'%p*R/fB"&+55%l*ӺCu5l/4%EZVӃԒ(<|N yx 袥穝;6d@e4?{"yZYMF5>1#^P7F{PT?$aRYh \ 2Zvݑ93yڹ_O84ژ>얛su&*Rq@quSC̋tR9 !7+l#/8H-وڅoW .S3Бa|r;a1/U|>Jڧ~t\~gU/x4k>>|j!6ÌJ]=V'V,ΏWV0gAS;;GT"B,+ 2M0Bx}L5u7C-#}xާ*ĚvI|.IQתD"Q^Au vӶxDdh-2@PʾV'硑~Ш0H &L} dP.:-Z?ԑȬmȔ5ԈQEdy6U_xq?{s|T糭hMd$rQ`%edSTD#t mM!0(68 ˤ2nWE4lE#^ J$lc*"l)oSbdՃ䵁JwfV(MͬxJ ^9!LqğmW6} Wȸ+!a4WDGPـ?AƋ"462}y{)0j莞H, Ql r;).">oς $&*`Zc5@W|&$9 @CB+ZAbR+3p!2xEa<΢FF%ɫgBY:-DH{-AMfBWcDi,5rv4ND!:W,v g; @XgB_ Q}~\fO|^TsNBW 3d1ml( +ف2CAEMHQ03s/, acYk<[q7RwxSWWZgl+֐HfkH$cKܺ˄=1Z?qp?PpgO<*@LO6g q^XbJ$ӨUވ"u8* L_TiFcߒ`-% i.@L`1cq1H\Qyd O[- |`Y%&LWSIzj=eKw@L-_f.[KPЬ~J;õ(_  p]5iMvB::+r,JL Lɓ:5,J(hSe%W@ ?_y]/@q+<Qd[$Q@9`͵L>q}C_%`G0ʂhҥGԮ_w^_wڗnڍznƺ>U;#ꖛ=l26prX1¤X$U{v9Qv۝?Of7Cdh8` d{e]XEɫ,=e,X8W-7ߣYo5kFǃ@f8F}./D(2 d"npRl7`(=yȳ ESPHAK_$Î[I@\PbT%7Jj@p#5$?X9+wS+>zztnukkcNtS"ӣzM(a*-}lTE%`dBws5sUvJN^*K5WܳO5С]?lk>g:  ,f\JPvEWZ\v/%"%e$Zjp ~%//`J9j~lFQ>I~߳^Ӽ YDfBȰp*6:`T *B@GH &V6A@Xxr.ш#aLJx;d_.yPVk2Te2XZSMӣ,XKMzlFPA (ĠWBQP{t(=́^ڡ́Za8DRCL8D!%ۼ>c{=]h~͈B7 $b;WNʀ؀ZX'eKh0*~0DuxQ*TX$!/?i5Xs]F ?̓ptT9^̎T(ЌccY:@LlFPʉF;:}]t٤k Ոtpy\}M䊦\xHE k*|<6õzȯi\sH~B!0!/ * j .]syB`B^Ap匚<4JT@ *L?BšWNz;*L~ÏQF^ooe5yaur$y†j =㫜kmœ饚G0ļ[Tj:q[I<.LQ a7W Sb憭!خ_LCL[ "k.Lj|@b!ǑLb-oj-B ]0AZBFZvzCroFS` ]jśH.};j-+׊kƵ+G֫߬fQbmo*tΧ2&[vQWK)pҩ0S75-yxlrO5" fL%Җyo>|W*W&[̐H>'9`F|2 fUd.hVlC`)ܤTkaTWH)8`ʼÍq hMfݼ08qI7!e~ooS,t񀩐b67a%CdsjAo:#?kHdpؘs\[A39txfEf&61NYP|ݼJ=Ch$iԼƊ*0Y%CA9@}bXtF|Vފ5ƠF 27͐dZDLx.~hݓm#$ra m:5vB$$ݤ=F&vZM3HەYib&L-mC7/ZSkcBRg5S+"D ܿ"RAdw+jDEJA%ꬭ"kt\<>*H$0/=S (Ys THnFgqt[؅c{ &1(6Vnna_&ݻv&m~Z-\Ia""f:G=mO%xD"OQ+Y-1cd׺Dʬ#'0S!Ħö"ѣ# &HwqXoyM=/݂5N6hG\c6$mdyA &Z2Kt?=!Rc- yY@ԡ@7%_^]]''NCy@~8) [3ks".g$C4l]$H}U~AI1vSW 9r2tyPhDhC&, Lt ˯P!H8:26ޖ*RO劥\WUc|~ŷ:F5jd{~$ˬ0oPϧs5sisYKX5ѵѹ-fGul&bD-9• ^H5a&o]D :x6ܰ/ZS[[|m˖,O_?zl&D`lIŘJUժN}qG6K>뉰%w>ȟ}؎:*`ޯ;5D3oڻgCVO=xr$!&X-^D+ ,[Y )xi"d ͘2 ~b6=Ÿ ~o&DY9.1O?fyZ[ iU`+[b:.Q=&# *o<1Ɲy9~Qy=x_=!as 2Mn92º\<QK%+TW-V¯*<0t`br@>՜N^]'F3봂D~&c~/F(f (G`Y=)R2?D[2lB]ɘ &{)9l|^ '09Z|$nLF9c!H#E _n`sRha?, 20!œlq~0HQ^W/elQ̈0*Y4QQƍmIq/\Pz)@|律yBAW*y @\u{qo7}c xV1FV(+:I%^b(Y"n4fBKU ug#>KFbDb@vԳO1)V3o¯4YOZLegnO9Qj溦o犖 \+[IIXV<}n'OS&cz~_4&BQ<0!>8꼮 rRLgݰ~N`<v3^F՛ .Toο@G.3~5uڬ,2䱥FW Y78W8QN5xI.y \m޼I4 ܭzPjDnӯ sݭzw~7mW~բ;oБLVLX&9}L+eB")܃ELB%0uexvV+"QQ`Y*0Ͽ"6{xze^Ykuuy=O͙;7lYQM7t$U+l𩛾_έ;nT~<8dLMFنޛa yK("0f(.5j2etL!P%m3@Pw@tNaB`0LE,U_C׎W_M.Xwμyj9" +y6B^n >" +dd8c`Nz`:rQG=äBMj<0>x&1:oTi|i1=iQ"AB`W3<O?*}աÇԈ|s"Sw_o9u6 >fuɟfΪ$xߖVBՖkW.;0)yɗ/*;U\\wk!"7s]Jq$D%`?ͅ8Tϔ*r0ek5!/6j" T%d)Uʍ:Q=OklmW^Y<,G˒߿9J:9Qѷmqjƭ~轉ӊ?r0H1MbT_񓵃>:qzPpt@g)S֭[ºw G`|jȯ6ڵ{~QhǦB A*ME: 4(30qA}qWSd+~  gܩ&vz Mv7Wc~&OA`i>'c[W?8[kM &Hp]u 5쨮Î9k&ٳg'H(iNl{C^.9kl$C= $X>A 0«&9s=`[oK]ίwz&"|6zK yXTV PWp(W98閛I &BCym>1cǫȑM!eN&ѓ\uX2UWs,XmX<0AqjݛTRcjJ V10Ip,e'Q $ɠ6I%K1yN?2mq6aE*mKJ$p3:~v!3ܷo^#w`, &M!ԛ O lųuPP>+IfmZ&<ܞ+L! G^ǫKx1xF2&aE󼮬 ]Ni{uǫQw` پ>hB&ƚS'O)Ae%^'X.B %A\  kD2|6οmu=[~5@dv:ke(ÞLPYM)85>"bH2O diH5fO+-r@h ?iy^|AعC{ybj,0Rl COI;}@uKCzlGP[fFL:M`^!l@rdE`y,4'(oΛk?n$o1g_ߑ-~j?!`JŠِLfN2c,}EޞݻWd97WLƤlmaqwOPLNv>z&-'OсgE\<qB[rU 2 :Q+upTߞDG3mڴCbk~oЇVG@&OVO>x0#3Dy1u&04 td6m WI00|N< -Lia :Abٳ>+89X )073v )#a3oye4^._ СgRNf)cAِC: 7̄ 1 Ai2] 7`iTGS>saJ1T94 Iq,1H7T5 a'te OFS$` gc+@ܳg' $l_o=1*Qa5Q{r:DrwBʦ ӉB @d؎yގഉ*U@Ṋ .dF G*X FzxE灡ݳ Tfp|_PD"ƓӑghƱT, āHCqrgqt䠣0 sR1ß3QIPi!0/N]g> 5R*"9{Ft-iLFFl gĠpH iqX&yWr5 %Lf &Ch~HU0;8xL>1wXADd|4le:Po t &1vجq`r'M3,l޴)ΐ^"B Af*&MͤX6 C}} yq@*eC;N:d%Afld6Ռ3(j8gRDێ<($6GT@s"Z_9 a L-:ckXy`>ttA$TaNJͮ˫9:ʒ:ʜgk@s4$rY~%?Z͜ǷLiRUxO悲#vg+2gRY +8x3"Kb> t т *I`LpL: A!$kZ8aܨ͢$3L])FEZKe4Ъ>|H"=u'uj8T@C=Wj J cVr*^P_@'@G>J 隷nTjҤikQ&4&f. I{ j;ۜ<6uJժ8> |kM_\wףyː&.@mP~"0[*2Ù{֏;ԑK#z>Bz&A3eg6`Xy`obeH@6uURNPgu$Cݩ(JAc¸HMg`+GUә ]԰=f5F%%Hā*8 t9a3Pah!XEqf?d1Q(^W i:~"-MjG HS u_gT%>?jCudxr>o,EnO s$0[*2Ca# _sv#<鑬`L Ld\`[xnUmd+wE `׀<鱕ў~ȸH UuǑG29}PmB1!ü̐;,XMp(}(h+2+`?wMCPK/D<>D.rw>sb& `6F"@y"sCd)V(}T8c*r$;nȟ}\L ~K R+0eȰ1z9ȥ8}cT ys1+" qc R{>T֐4#II.49> &UM_ƪ,ȹخc">ߜ. L#$#+'s xn|Eutd4ibD8>La'-Smުu,< PEsKe^єsOQ=UYk9T]SAl}Q 3uy9z`F%&HY PEmUfD`=5Wz}yɄXy`‹.e0U+o fK]ffzЩ,[VndxȬ\UX i ̑-辢K¤eWCLyW*HDZ|݁<jE(TTd<@`XS5&iv8K:ȍ5~/trjԷ`B>]dGs~_-~x!?y`[gVTdhiOe{I%t[9K4m*1px5dj(N~{ߦxё"Ļ~us0!VU?yp*R@1,Weћ>2 2uN 'L*sO}US3XVRUjSJxY([]jsvY\fbu[y`0ꪭ{2= vdDq<ͮfTlY7H2ZA&c]DFRT)ۍ]JF$S@g%DAa"WE(0$i+TPbm'0R_ƪ煔 hUOH YDz K`CqRaȓ+ fӀ!GRgarWRB`W~@&/& V anA=we O'?&*Cw4( )6H嬰Tuxv\y`!BTYeUYߖR :m/QB}C4tO.$\"8B bHE^C_UfF5 bDljRa JDĀiTģ΂ Қ54j BN&bs@^ӃP)T'ݝ jQ@hEdjXy`Dɚj\Q&h}֔"*3^9.QcCCk.y:<4^ 3 >/?D!Gԛ+B ]z+B1irht(sn I_ k+p,嚝~)5=*(u6zεdʆkꎈY<0Q_ȋbA9ֹlҹ@R / BU&}x焵_4? jJ(̕AfA6G+ /TB'fxe֕d{馡ݳ$s, AdXSՉ*ș S2N(¥y!ն'6h!j5ql:(DAzC^~qOy+ښXj >W?ۃSK( C%RWUrXWG TLm%JڼDRꁁ8@_GH U_|\U<ӖyɔPJ_8}Q< $yUs *c1|`ЛGb -bU #vl,:ri$ t(4 2hqyͅ0 -TJnC1(!AM4+Pe?fΚE\x!Ln0y9zoUxEX #00QR HQvmV9OFFAhKUxA$s玡?\W>OI]YR_ s-.Yae{nVytcxTŀokjBݨ-x+#+1,^}u{h޽IgV21~BMA|Bfa C 9uοmld9[o ?j7~\ƁDyc,Hl78Q}(0q_t<&$?<̆cؑmC=Qmsғ%J:`2 kC\۽kgj6*Zq"3(3O5|/L`$؋gjBA@V -m7C{Є]KL%F!AveGrK#@4PUxE#J!1Y`dX+@S!1KWmSG~_r)+/(R#rɲ m)VL Mg~Tag@ڍS! Гi 6U$qC2=oި !W4 `x2}dK|D^CA"G ϟ?_K`=t.}M7Яpu2~`OHb $L LqM:͉ m |oBh hXN*|e~9AF48%ƣtuƙg:1צPcKD0 IfԞ3'}fxaqAr5yڂ2CYa6 eLUAd(('*0D8~ѝ7$͕|iO;-dF 'J)0-O<-YhV ,JC9&n-s *bb @N l:?kY̓R;h'OgF* eʣPt:$Qu}#o҉q{ޫzV&^U͛ߢ\dq)&Z`bd^%f2"&FΟ.@D iѸjŀ8*uB{r^.YՐ 8' z`L=\Rh=0?NdBs)VU6b>}ZzɥyYT0Eh`PgDhV *.(/{?|UZ|矙#q@dL^&烰 }@h×d!0 >6FYnBT`{ Hq%(u_Q9'%qNAQɌWSIfnuVQBA{ :LEC3qks쬿Cl s XV uYx\Fu65*Ck(Ř X ܏;jߙ4 N:KƲI\ z`3"j0% Gꁙ,Ehi}t Aa49M` +@^'Um֗iu=0Az_$7*L|XCBòPըIC[6oؿZ9ù[;"ȩPxu_)zЛsI` `üt&b Ƕ[+*Ont$Z&Rcc` m8O!0H΍wyO7VRD EAV9$/q lxAbwg>ԡY0Hu|Qc|OVY 7Ac=J,+#*: AS<3_Yv4\\XhE)L 47eˆvG& gJ1RׂcJZCU{=,sc2E`~kJ TVt;YPR?iYah`DlNL3ZsQo Bqf(=UW, E8qf?Nͅq ls"AnfFGo~ Q˅&ƩS“y`LFӄPéSq2)K5vGS*#NOô$1yp_/_~cȘ0 # K#ͧ)PӉb|CYjݫf4N3F?Sv^2A%yKasDWIy(p !f)9?p:QL%K|Wi? m ^V b2 C.'@^ 'r&7.zO}x&%O͈[Jm]r`;!ҫC4 X# HJPb **M+9gi:Q=M!ڟ.JWm{!</ 'g9–6Lkoz`s`.c݌y͡$×ըY':ƺ͇//06,+@B0 K neƑZIvTJj{mB9dժs&(äsBIi xrX P wXjlw5 [FZ_QmMlG\ @PG䟂+kڔs" s){iҀ||AZV (g2_[vM%VABHZٔr 5Xg+wBpՔǴ APbgv5h0:{iH14X,Mq\@D8vS m+2AxaTY?] D E=~ iS`/ {|FA_l{65of#XY}t쫃'~ۨ kk+ira ##/FkV!xbX|Y;VWX 5LvſzGAHШkDCETcδAȋی qk}(!8_` !ӑi7^  ^\;)cmU]&i5Q`G&d/ТVc `d1>TO6m-?*JmMtL%kW+NV59L.M[[_t=-uPri/8_`ۅ4ش21D:.F^;u'i2׆侔EߵY ֵ<#M`N)KjqCj8 ~ U A"l$)D XF*䞔"3rJ7|<&j &;<0$bPLM5+U 兀9XzSas՜. N`C^h,,@?X*J'zy&Rbu/\)G7-S='v͆;V:K?}WΖ]D3 V\]@y-s nmD2VWP L 'N0UIզ8'F2IMuo1: "\y+~f/!*:l%j-zq1+ NRGGFLċktl&03DL5nȽK>j4uTVh(8[~ee5[_mbݵ\./ܬCbG-#o~iEmר DaSc. ȃĘ '@IO7^ V6pņ ߄N?p6sz :|dXXs% ,lٽHrx)#_f.Aº? f̬s9"#ק|/AP!_ |+qIFL_u,;z`eADԗ3#xM<=w{Cj ʂzW%V!5I5'xX:P`Q!dOzMyz=7ۼ,>x$a U)^S7y@<Υæ^TK.I^6ާ)>9 J+0cTǓXiic܇A恕4FBlz`A ٌ^CƋ]%m̈<0&0tILx% VRD,%%d+6#7<0@@;Ryf6 1/6*Әm5q@YD`6ԨU@01xU9VOZ=fs G?s<0+0  5-F&@4diy`,N*L̈A0`Eԗ Ta[lXj y`G-%UR0Be&n48Jk])˼#lDVVa}3gx~};~A"\뀅Dy|wM2?绝oi:sQS,E-&}VaͶm[Do<}D>AŋF\4ɾ1!"LP7a" t͔3#Azôoo0Fi~5#h#8}{{Թom))Svr{qG+N\<OޭB2yd3G=(a.sdWD K Q^-J":0JqO;{  D,:H^؀1.4^j뤑p +jeLt^,#qޛq713+g=84~Ǹ-5*FOas" `O5* /[x!+3yo\~ᇌQTI ^_tpdQ6 ݣZVK2g ]އ{f UC`$yoqXF!XV{HmSM>65:_+Wx,|.]#f0DKpF Rђe[viTA5&P?g惹{:9/ )w xcJRsx.;+0~Țrҵ#/ P¡QjyroS%hȝH`*%V:@ X%4by݇DXTC1z7HGdn@#]@̇S`S@ b>! T$G)Q +7|73Y:ђF飘X9Kˈп ̔^.t?bJ CʫQavId+؇ A(0I G}@Kk`+Ȯ>'0ӻF  1\?no4IQ^ TeLVf(Cxf@Bfe>(F@LH(0*QK T\r5䪼#>Lu"F->Pa(CWTa0 r 0V>b 95Rs(G=WLK9F-U `-~N|1IbBX|QsZF:}E+ [:KeOśJtQk ,Ki10͇># _Uٷ0 )@^#5Fld "ax8uea-^|$J5 *ΫY@P6g,fZCP"F/JK|$0_  / Jz[^x)CN=F ɧ25h-?t9U:>ڰ/ UvuK}P9TOWs,^_s |C:1YTj]qA[3"4Lj{U5Wn]1So:S5_u&Ϋ`rK̇AU`6#1Pf4p mrQWkkwي|5chJ Jk>UT•H4Ui`kZQ(0(*,9هjmj 2,(@LX6lQ_ @ HS%I2}4DʖhBpF$AP`g?خM͘v<͂\&Lom>d#{"&r &B6y_Y*0i62|c'`+v;p g4;2!b`Bh9}Y~Ywa?[pÖ(NٗtĵFxꚔdSu| < Y* ;!/>0"( bNb_=r{rU8 0hͱA1KRGajNX`P)RS ɯp`HQB`!s|mPöl0SƖ|a\{V؃ %V Qs¤N~gisvgVZA $xL:Ґ|!SϳַQ4*zI :A>yymSk}[IEz6Dy_kp}GAq!R+I|09[R1: W0 zmS?@>0&NZ tu˵RCT> /՘LAϜ[ǻF世mq}ҹ_mXC% EIΣSʵ# L!Ipr1cv0>+"-ԉuZJ7bw'Uakn"bkYPN;8U_ I-+kC[K>@Wqs) th(oQc7~|0At8 3R7M '6X}ˢ S4Tvyzh&t5I)6ň9"b! s-smK5׷G$sp@Rkv+zG9#8!@RHFKMT`#oTAnj;0b lK5hmq9Π ױ?~S/h'*dDր 4-a/5Ws,?Uszn^V) e8Uy A2.] 053y IX)TX/Y!%cԬF#ZH!\1'ɶrߡFmF)$c?٘BJoE:|oDz\1hfu4$*sV`_B`MZE羊:B $" gvDƍ{UL`&uSy""fyc&ĢeEV/&AA\X)x䔀45ԡ6P&8ǏLQ"(̎EĭL3;dzJvFr!Jh2b&* KT{w!ɰMt4.D0V\"f?!'Y$/ ~A>/T"~N3N2$#V# +Hvx?vk.j5;+y ks=h3k::EyTYYD`R܌)x)yu.>aw/=Dh:x<+uԟONl5!EwѿԦm^w:qN,U[_O~M`ig#o}7,thMrvl%(gk IўG|*;#7UIKէ˒zQYPS מsq{<˙f*9Usmn 09?ܪɊ: lK&<'$m .!YF+/H $k%/7C Г%Q[76aGVK^|zMjmz`\unTw}Aqz?QeZpކ~'ԮvTST$m?}w}6ت1낄~0<‚Rg]$6EL> 0|kN?[ϙLlXϦH)1 [\ϽgW ~2X8r6l??_vf7*F1OL (/(M[n'aFn&Ar@XƑN9\BGT) =A\P[#jNM`_}^&4i]P,FgW(3dGVHC;B`vTs_ک6;H 2.Ɍ.GaT!dWOpa,0Ү:C.XZ@ fk[uVYEU"#kqs/xca!@Vڔ?j"8rY?8L1M@Pb[&G9!a 2շէPPy(c6!酏-3I3 ?X %6 oS7|.j Pi efD>fU B`AåKȸgMz׃ꢋUX(.v_.EMd;K]&+wu02:M3#1rؽ@ & L&@Z9mdZlXD>++z\r @ L  ײ< B`@mȬa'+)"3mTW6@@[-6J낓[,> MO z^@eFXm\k3NBFp*'*P@P;/B=_3wai 3p 3<jakǷ7nurӱ?|p@\d@P(09rحS βYזkF|α<X.DDc375zB`ЈVI:쳓={*$)*8i9 tHUJ*C* @]p: *s̃Y`๯k/]vvyKW7TĚlΘV Vyˆu<_q2c c;! +R^,(̙+=)<6 CX{y/uװ{N(t&=7hشݶs?c*,GyqxХaǍS(Qޣ7f'Yw]g;yz"ʁyw|)dq/8Br :\ Z|:aӢZ`2GiQì #)*C;P)IŽ_{>cT*d3>˞P0ka{:JO=+*^N .=|^JVmXuugp0|g%$Cw2v=h )1Bԅ t0̨.5 *I˝9!y5=n<{7_FΓya IU^}A~g(z 9C[PKÛƅݱa>%a f4*= ӆY%2dBzxgi(Tpw(gawxdPɜde#'}Dzg-xn+-9̽]HcO%;p /爵2}$#=J Bx׼s 0?Ç?7)CN|aQIZF)YXsIm2qʱs 1g@oT( ԥ`:sPwr 'sj%!tCF?nf.o˧K6$4py 33/TĆh⩝lJԳAbO]-$´,J`lb`GKl2U(\GCMm 6ѺmO]f9]ԣ>)lmzv-^<YxppCVseO㕰z*=f(m,^t.=7_r-atV<3K./X€<K(B+>JR`N$sw[rc&{u"faKb&,m>ڱF&FF"<̳y\bwqO/q>n_ 쏨ܯq羌yg^׸-ny<3 ~SG~ 1vmܾ[3nfs؆wMLV>ouOQ<Y{zhfaG_o( ~[*`q_*ܲT%<X=f]QVSVR&ϑH~fϊx+)eimRmZ u/DųwVy;\ Ɗd ٮQoa_iPq&[nk{eL)x QU, Ƣt~Uw@0XÅ6 rݠlR9d'\[eܫqHBy`bO 26.qE%h7d YJOB L3鰆0+ ]> PUB SUB IOPub PUB SUB PUB stdout/err PUB SUB Notif. XREP PUB XREQ SUB ZMQ_FORWARDER Heartbeat ping pong ApplyQueue Direct SUB Monitor ControlQueue Control XREQ XREP XREP PUB XREQ XREP XREP PUB XREP XREP PUB MUX Balanced XREP Task XREQ XREQ XREP Registration Query XREQ XREQ Client(s) Hub Engine(s) Schedulers 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 XoKqZd0cd9hVSfcofDZAYzncetKJqngPEwjGZcNTF9CNbKaq26Lat/SaOiNCkumrjFaeUUh2cP7Y GeVHJq56RcqIk5Y6g9qla1pKzdAaV9wvJpf3x5UCytaPvjGu3zddCrg0KGlQrB+SyVrIMizUMUsM aM9C52bjIylL4xYdyf1ixwCTa5UzMWZUy3MUxrQTCI6epPrO5YkYYzRlxEG5sGyH96QqQLPWofKu tOOqAM3WAKqfqqEF4tQb9Q7uRz6qClA3niypARQk/0lVgKZ8KK37kU1XAZqtATS6I1VjhY68UU8t V8F8FaDZGkCDGiuDKkCzNYCaPrKpKkCzNYA6PjI3nsNZHX8p3xk3sh1xKZ/ObVkXvJTPOKpwiUv5 JPfx/YWX8h3Ksb/iUj4+OnoqCZi7lM9ieH/TJS7lYzVy9ZiqYIMhqdnOp91feeylfFp1SXBQOrYz OxqDs6HEW/0Ut5hd4GwotVv9TqnkutjZUEff66d/q9/Z2bbcvX7n5SmZvddPv+ruMmdDwaq788+G Mr7Xz2R95Zn3+sn44SCUoUrJJ9zrJ1RgqR4MLvUszrnX7+hKrgtlK0pv9VPJVDnpXj/9qRncX2n6 Xj/9qrsL3ACC7vXTn5DFBOuaudfPRIbqBe7107/Vz2I/KudM814/fe6VWONn3eunr+gO7LET7/VT pCgpbvWTnalybHxBcq+ffuqUxUzylIl7/cxkQZ9/r59X91a/Y+/jO60AV+U+vnOvslW51U/zvqQj 7/XTTSczOJ/f/L1++hsqXFTh7Hv99D01KPkvca+fMC7VW/1U6l5PutdPf5+G5f3z7/XT95zQPvIF 7vXTypNkb/WT7b2eXvfwoL/RJdqW593rdxTvn3yvn5TQDm/1O/0+vmNO8dC7j+/se6wY/la/M6s5 GP5ePw0vl9vVRfcmXOBeP/3EVoixS9zrp3+eh+y2rDPu9ROIXfVWP3nF0On3+pmRY+ff66en8npr i/0y9/rp2/RH3Md32mE68ozuM+/1k0BR8aWPPb3h8F4/g6v4kGGvXZF63L1++sUW6MacC9zrp0Fe 3K1+51elGdSvB/kb2S5xr5++mSOrrD/jXj8Ztg9u9TvxPr4jq3A17+M7O5olvdXv7Pv4TFXh6udc qdzrd1IxPBtTOv9eP/1b/bhezr7XTyxm0z3p6Mx7/fQTnfjzYM+910/fbRcxdt69frJxHdzqd0zc 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 qNer1bW99cr/BzhdosE= ipyparallel-7.1.0/docs/source/reference/figs/dagdeps.pdf000066400000000000000000000410171412533144600232630ustar00rootroot00000000000000%PDF-1.4 % 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 8 0 obj << /XObject 7 0 R /Pattern 5 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /ExtGState 4 0 R /Shading 6 0 R /Font 3 0 R >> endobj 10 0 obj << /Contents 9 0 R /Type /Page /Resources 8 0 R /Parent 2 0 R /MediaBox [ 0 0 576 432 ] >> endobj 9 0 obj << /Filter /FlateDecode /Length 11 0 R >> stream xˎ,96!/f7Pz`Fz{dD80ꃪtFo?o-n7so=av[ʘm cPK|9zϵVBbrn=C%X[Xy@ ˜!ǭ{l3Tf6>~03#Jl 7q. q/-,_@c`5C `w~j-tom¥-b# >G` tir- /=Gq^g۬6qV ~^}Ɛb0Mdq|b~m~{[3[jO8XsNe$VW>mcjԚrݭs/Ӎo-!KaiK2C|)fډwFfᲉy RYG] \v?~NxrG jC.1gOkDuCN._Y2/0߻Oxv],pi_G?\@M^ @};|W _)I__ |%- `K_ė33l(h1%<|`I9ā|~VWd/|!~(;qW`4=t'%.]&sJ%|w-~{UbuB/:˻O.N>92>H{024!Zb+.?+/|>+_~_pUNإ<`y@+deczї|Wɯb DGFu WD~E?=%> o~I~8a~4^5_Y//_?_/ȕ LS\n{I#dtćL8Rd'ǭMs1>b(ڤxy5o1u Zx\8^ Z=M+H5Œx5@1X_0ZY[5׃zze)exI@ʔq0(9/{n:1cuʎU(_{Ǫ&psZ:DRAsc!FpZ1$*U&7zN abɅ,+P4 <7!yslZap.ϸ&y #>!:sx!"[֤Nհ,b1vj!f:J%S:Թ_!)|WYѢXџ]1.d]#~]ڨ]s_D3+_D-G N_Iu;[w9z_ V~ΰsU ׄKШ3{ K]pζݥ-JJWm?Ov>P'\& d>HTGkdQ2$e,h2Q%W,JM<$hExəJ\e[# b"c%߅2 aN~(t//p_$.ίS;N2Q w T+^`!-֭pۮ!FϦ(t5r^Z-:]>$B;=WQ|7!k' FULnb_J޸E)J]A ́F,)ˌI0zI#ߗ:_ #ZN=)0P M'Fr~ M %F>9+& 7y"huw۸:.7(i{p|)c Z:?Y'醰RUIi錆oq {ǥ f\/\G#4ge$ᯛM,ݻ_[PF̀r=v-'qL JȒlno^ޢҥ:)ҏxkak_ӯB\uP0 y+ÛbyE[ ~5Za>Q%qm\ Kt*G.$1 eRh溣5bh@Ni} 6s%}B0 HCRc,5gPNj ( RnCp V$B@j*0"{1[DsãrZuTMsmnPco+';z,ʍ\*suVstȧup1܊'.&v+OWiq糹Yb٭OLKɞ ܟ%({p7_(򞒤):h:WdEYB,[s^Mm]v" ;rZr'l{[ܳ\+PJwEq-ґД!_R02 nJ)F< 5iRmvz-D>D` 4w7323Sc_w)= 5[˦MVwx%|E&9gU&߆\!Gv" ێX ч-NTTb=(u2 H8` vj k2kee)7T,hef r`3Ѕ!+S#xJȗKaoU) Yh'!ʎ6n\a=<#ףZfǼudݴ_?ٰ;!%XWsSZlR%CYlZ3YW}868 Ds29eEAp@Wj4e9Q~)_|wmaNV בpjE CN'Ero "˲(1j2VǮ.9_Oaݪ#$#O !l߸{7)e[U"&ՠo/1{ xI18MEJS3VX@S8*3+R5FNm WBh\di̛TdC,İϼ[iJöLվ?d>֕tN J,ΔpΣ]a0_&DDjaܙ@{V,?@}qٝnG%2ݦ99H/=ai$\,2y1)јȔ8eǨH6rN4$&&S!P wr2KEDq?A䳇Xg)H2m~u^][ϖ-jꪳB]|Sey[^_ܖf%)jyJP,D֭ʼnEk֭Gz$;;ez_8^j O|5) *GuO\1%RQscg0N/uB*m_pұ\ϖrO\q3ͥ;,웺)(k`._[kP%";de`}Jl[`sRƦ|y^e*(5xm}fW$=6^NixYvuLg7O@Ct%΃#Td!eySFUwE~TbOk(Jf&e *E(zN%` bHufKIrc 3گ{$U&Sk애kʮ$Z9 tc:ϣz7LE1I.UoӚuw%vE<vO}e'[iSg)3de71*Ĥx ߔh'?1>s ~=My %y2HQkQY`Af&O$ MU tspheʋXTμܕ*u֍mpo/T))1fA>Sz+AM7Z5^+ObyS.SiIgpuכJ+]%0#y+ϣ3'{trڵWm?s \7h"Y,nPwoJW5x*̪ Il#,Cf* %؟&MRUB 9>z/wn.Wqͱ̴3[VdU>"eĪ fG@Fo>wJ0NG'QP9]HμzE'PK~b%]J sV~B ʖrߧuoNNvT`ls/#P8Gd"hӁ:$f#8$,(H÷U͉(Uo1=gR欭g r{k8U$@dVGTU"Vq^7 a" l_Xu)Ae)TBP@{h)`%ݼ <36Vm~(ܶ٨N9knq2ݺOTvJbJ)db{b](ꆔt+I&$8icn#g@OsxQ}͑-ԤUKr:C4+Xf,5Hu^IGbR"m*]iq糹Y{Xv)jœOt ,g& ہ7.u\ԢEb\0H[1TYRQF3eiQtskzo\effUFn'8[`u~ BV *Ⱥ8d/E*EV]"Dˤ /MEƇR#ܒڋS[l7aŮuXV搵B} ^ϻWImїCJ)J>PrȩJ:\ ܪӫCbf8t'NTgBbMQɊO\؟P(8EMs`*S_gT%g~v l<)\S#3Y5gЍ)_i!"$LATehLOgChwTnfw˺];ssK܍F(s/=w^$-_.m4xJJI_fYmb6+fdž#G<@Rt,'eV2܋<]*2vP TLDV\2mZvIh60FHS% (+!^ЏrXUXl6*Sr"uֆKVz-P_8vfv'щz%2~WtoMzv`LєSk ߐ޻B Ndg.5 1,^"cd)u3m|CvHX-hnv2Kq{+ӊ[f6jHb_wVP$ܮ'?8FQs&WG ӃsV栂3ݼ,3Tnjk(P8{S1@7qjtn'*Yv}a+u~"OCȨA1ul5 LIQ>Teb%S)P\)KMS F -,5MeYJ%5hVU^.yU‘NХU8Qf1Ԗ([ 2(z/ϔ]׾/ wm9*wrЇe fU˔Pr̋m*N"#q, yOjb=NY-3٭D%ˮO4 Q'~-%f#K)Rb?+ؒQ߱`7<[x65Vĝ1ݩC$ Qƙ"9'W[QTnCRsd?+, .Vc݊HTj[v+z37xRum*{Fe:̊`eMwk:ZS$ڕW@겋պU"vvB ]Nf[g\SòKOH+z< ~ ?|c+֩ kʈ_GT,Xm뇒5o숧=,thKK5k P=nYOXkQq؂jcWu*|L t{HϣڐlAhٲMu[xJYz[ww W%1]}NNzYkGe 6GQ(_=4eFS\en5z&K:V50zI=4fmKvuS"TtXS19$t횃G֭r,lAT5Z_Pi6f/^є ֧T-CHÊ.^zNnonю:u ]9Lbyu{ ܢ-X #mOMbVvʖ<i&tA2g^YٌnoS9u+H~eܫ$h5aM_i (˺eq:/MzektaŚ(q]NPM/=Nk{5?oMΫNsm<qٝnG%;=-sԻb{ss%!^S{mm(l=J+wPvڲEGAfP3+\ ;AGm)PYVb~%N%o;l\ZU19f4^[ pփw*}nӒjW|Y&(:=IBhO֢\lOn`9ͫ8cWmG(d͈@(rKhwV̧sv>Q+m_+~&` ꯤ 97$uMÚJ(kUK69N吨UEAՁVƪn$ 6<Њ[Y%vf&ܓfk7jרiY,{Pq[wm9馆+rgPY%9lP$L 5: BdjΫUO. '4fݳ|Jw_V|P+LC=Wϗz̶3MYL;Y8Wk$QnC/ENehYƮ="y 5LuI;f|O2O\'֪yO6z%Ϣ?w;L]O8ʜχ~ղrl{±$}~K5W`L6iGkEl6=(τH>=[}q2e[cT#75a6$Sl{y2y<ȩwa[*8ǰ"qR!|g3k]uӟȥ#1T@*%m0*|VJmo}! ai endstream endobj 11 0 obj 10705 endobj 16 0 obj << /Filter /FlateDecode /Length 68 >> stream x336S0P0 F )\@>,́,# .C c0mbl`fbdY 1 r endstream endobj 17 0 obj << /Filter /FlateDecode /Length 320 >> stream x5Qq0 54sۿ @;a@dJ\UGM>`!S֖{&UF!}W2j]* UYFp&I8d Rӿccz endstream endobj 18 0 obj << /Filter /FlateDecode /Length 317 >> stream x5RKrC1ۿSpΘ}tj'+-@B./YK~%ۥW%B>R-G- Q=2'":xa>N)x_xN;2$KMH=I+4t~&+s{rj X+)$=Hr7VސWg%&&MܕBXtLX㰄*aՃM5fcdxLP} #GMv²[6!D3,($Nc$ Ұ9 9e, mh%zМaמE[{ endstream endobj 19 0 obj << /Filter /FlateDecode /Length 338 >> stream x5R9@ } ] v͜~߆_ CVie!U-.Im W%ڥ Pt,6˯JH+kLwIi"Eo7o}=@.^ AS(i|Ъc(ew 4<3}(~_K&(? _osџa`Ś}@*z`yT endstream endobj 20 0 obj << /Filter /FlateDecode /Length 248 >> stream x-Q9AzBsˑ C :-qPO+Uwu9HTM]vf5,?c 7zqxLu5{kOfP2+qSușO \ ȹeƌ#M!RH&3AQ~#aU#j \Ks4;<9GW +ET<pC7ҹ^s0XM7/=[ endstream endobj 21 0 obj << /Filter /FlateDecode /Length 90 >> stream xMA "OPDtz_NE5jK02kP)U0\ 2IL{qIqzz"X endstream endobj 22 0 obj << /Filter /FlateDecode /Length 210 >> stream x5P C1g dVukm;aBXȔy)K>:L." u%ʚ +`p&^7`i5tႦ.B%|u{OxjrvC` jMX> stream xMQmD1 \ky R]oC /)%K [UC?13,=?TPbht/"+ߏe s`&4`oI&ռ3d‰ATwM,3V7: lx%D`r Z`Q+ tĺv7C/਺x} K{,|BL;wI#fR:=b}@e+ (\* endstream endobj 24 0 obj << /Filter /FlateDecode /Length 392 >> stream x=RKn1)@Mr[T /1 %?ꒈ3L~r]Qljg!.6Xr_rњbO/ȴTXVݣC(-װr{d`Jn@CHYAaPl( WԬtb ) ٠[]aP[[xfޑ3qYk?=Q2QMg|2RCgB'`$Ip#A 1qOl)V;ޒ{,\L'ib?lK\+E(~Aq|XdDw#h% 0xyDhDԎ=(ͱ&{ǫvzcw. endstream endobj 25 0 obj << /Filter /FlateDecode /Length 80 >> stream xE 0D{`~&f( JpO{:2Sa ,S`5FR죰n_uzS*Ovvq= endstream endobj 14 0 obj << /FontDescriptor 13 0 R /Name /BitstreamVeraSans-Roman /FontMatrix [ 0.001 0 0 0.001 0 0 ] /BaseFont /BitstreamVeraSans-Roman /Widths 12 0 R /Subtype /Type3 /CharProcs 15 0 R /Type /Font /FirstChar 0 /FontBBox [ -184 -236 1288 929 ] /Encoding << /Differences [ 48 /zero /one /two /three /four /five /six /seven /eight /nine ] /Type /Encoding >> /LastChar 255 >> endobj 13 0 obj << /Descent -236 /FontBBox [ -184 -236 1288 929 ] /StemV 0 /Flags 32 /XHeight 547 /Type /FontDescriptor /FontName /BitstreamVeraSans-Roman /MaxWidth 1342 /CapHeight 730 /ItalicAngle 0 /Ascent 929 >> endobj 12 0 obj [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 318 401 460 838 636 950 780 275 390 390 500 838 318 361 318 337 636 636 636 636 636 636 636 636 636 636 337 337 838 838 838 531 1000 684 686 698 770 632 575 775 752 295 295 656 557 863 748 787 603 787 695 635 611 732 684 989 685 611 685 390 337 390 838 500 500 613 635 550 635 615 352 635 634 278 278 579 278 974 634 612 635 635 411 521 392 634 592 818 592 592 525 636 337 636 838 600 636 600 318 636 518 1000 500 500 500 1342 635 400 1070 600 685 600 600 318 318 518 518 590 500 1000 500 1000 521 400 1023 600 525 611 636 401 636 636 636 636 337 500 500 1000 471 612 838 361 1000 500 500 838 401 401 500 636 636 318 500 401 471 612 969 969 969 531 684 684 684 684 684 684 974 698 632 632 632 632 295 295 295 295 775 748 787 787 787 787 787 838 787 732 732 732 732 611 605 630 613 613 613 613 613 613 982 550 615 615 615 615 278 278 278 278 612 634 612 612 612 612 612 838 612 634 634 634 634 592 635 592 ] endobj 15 0 obj << /seven 16 0 R /six 18 0 R /three 19 0 R /two 20 0 R /four 21 0 R /zero 22 0 R /nine 17 0 R /five 23 0 R /one 25 0 R /eight 24 0 R >> endobj 3 0 obj << /F1 14 0 R >> endobj 4 0 obj << >> endobj 5 0 obj << >> endobj 6 0 obj << >> endobj 7 0 obj << >> endobj 2 0 obj << /Count 1 /Kids [ 10 0 R ] /Type /Pages >> endobj 26 0 obj << /CreationDate (D:20110217132425-07'00') /Producer (matplotlib pdf backend r8851) /Creator (matplotlib 1.1.0svn, https://matplotlib.sf.net) >> endobj xref 0 27 0000000000 65535 f 0000000016 00000 n 0000016070 00000 n 0000015954 00000 n 0000015986 00000 n 0000016007 00000 n 0000016028 00000 n 0000016049 00000 n 0000000065 00000 n 0000000315 00000 n 0000000208 00000 n 0000011095 00000 n 0000014749 00000 n 0000014534 00000 n 0000014154 00000 n 0000015802 00000 n 0000011117 00000 n 0000011257 00000 n 0000011650 00000 n 0000012040 00000 n 0000012451 00000 n 0000012772 00000 n 0000012934 00000 n 0000013217 00000 n 0000013537 00000 n 0000014002 00000 n 0000016130 00000 n trailer << /Info 26 0 R /Root 1 0 R /Size 27 >> startxref 16290 %%EOF ipyparallel-7.1.0/docs/source/reference/figs/dagdeps.png000066400000000000000000006133041412533144600233020ustar00rootroot00000000000000PNG  IHDR XvpsBIT|d pHYsaa?i IDATxwXSo{w8*jUnuK]jZG[+ZUX+ZZ {\UHHDH <}Hn=ܐ% "c1caI1c11c1Vl8a1c@c1cņc1X1c1Vl8a1c@c1cņc1X1c1Vl8a1c@c1cņc1X1c1Vl8a1c@c1cņc1X1c1Vl8a1c@c1cF\`1>IIIq5A,|hԨ7nC({1 cVDӧOcuҬ,u!3d'?Bֳ\U0n( 6 %]|+u8a14x95q6 غj4Q4X LMMU4he 1Ƙ782d6J`TP3lc+11{D"_~ 0+XFW@<u?amm߂2c,Çkn5nDE~n&j#000OAct0cINNW' ߋH,Y1c?&OFא)V9t̟˗//_q,c  h5yEɏC3< pmU韀 P7c`R]iK?AMX1Ja1XM)VD <OFJֶny@ENL.ϝxYc&\cGD?*VUJ3U5чWO@dgv#ޭ€Xx!&MTc׀0!19P 6ka$@rVwt{~qzSk*7ATT c@c}Ν;xP#s@OebCtB1o2'9{pf`1J x񈏏5>~`hX:α~ UDd!իW(.l>_87Ts-jU 5 33ƺ1@cLe-@"󬬬+,-Ǩ6$6S\dmrf[<#:ccc1!"~:N9/_tyHKlT P,]3rognHMP k' sONDEs1Vqc1|p#33SpO.;{>::{[s;J&||MЌ1a1X%] f G5{"UWT 3;5P& ϢiE<1VJqc,_YYYHHH7޽{%Z>P{{{888|(_<`ll#&&7o;g]*սq@+dr)pl%t_ù0ON m  f 1""hOHJJʕB\rz/cvP8rHxB"ĉ "w\x| =6~X#~N8ʕ+C,aG@:cHMMEBBd2@PbeT*8o߾U>AE~055Un+ahhL|9&ș~B" ""r\7 l7swF/?'e˖8y$1k,|W0 -==.]ӧO!Jabb5jݽ9cz0ڵ gΞ;TZZ۠ڵmCbŊ%XZVRSS"11w9k෵-ԏahhq?666z)99wD"Axx8RyD"TZwy< Dѫۡ/nCF5k 66Ǯ] Dxh#q\zDGÆ aggWcL+ ?pu|27ǧ-Z`Jy 33+VV#S*7rs( E!>^9DޢO>X`jԨQ҇T*EBB^˷IRaoo33:Ң={@"`9PۣGؿ?'<[1naWO ^ˉqy+_x"sNb֬Y8p`@(d2ߏ_~ Ǐ4\*:w~=z4Y+L:DrB_9Z0F@  d'ؿ?w^뜇@ @Vヾ}޽{Gްxk gtM\hSo8qqMv%"449ѣ;wbo#xWf >a!dBHG$( Ltk~Z77>Lj!CY2k G9 ߅xeHHFΝaF899CsB _}*_ 2熺e-p|z ;B`ll\$eeYKM'/k)^Z->ddd <<wFjjj􄏏'''r,_y6С:vs@nhci&b <ff|1nvebǎR2144~LERRƌ IHz ,1l&n9v IH t=z4c%jݺu;v,-MZ#Yf4o0V@nT YPC[_tja =#");ybbbZ SS|;fKRCVV""" Hk.z@4jVqHLLkV `޼yo!f?EV[ZĂiyqzSŀغu+ϟٳ0+W(ʕ+C s>}mٽX#XB0 /3^bܸqXjU>6l&QB?9,t=E9NEEjuD8a^AZ->~ncbŊWIR$&&jI]-@ PRhXp-Eѐd8~8$ v܉SNeQZ\>}y6Pmۆv)5jԀ|||PvmZ|9fΜ:tGhhwR+WĐ!C`eeL&СCܾ}@\v H$pvvV"FF.}?Y%T~5Hx<Ν;%O򓕕f#UWtawaڴiz7?0իW'&\7s 5j7_e2)>>o޼Q^ Vʗ/2+]ΝCpp0BBB :ׯg{|m\ B̛7x8;;#++Km۶M,O4xIU,9[ $UlcRpkbw~lK.wpZJKKzYupppЪٓ-O#"\x!!!w^qvv7|}}Ѹqc:UN˗/ǔ)S.- K}}޽;.]Sj:ׯ_ǂ +b̙>|8"P 䌖ANH@ "2̅BBKۋg(ǎªUGPVmԸ;I1b7d%>!r$4 *M6!`ྚt"püE 1}b9.@Axx"(ppmX!DmQ|EՊ+ >px2t(_] Х +cʮж/Z mj(`ffƣȔq׮]D"D"͛7 ͛k]cP&Wوkƍ7.q!iO+Vh޼ykrM,X۶m#f̘#F|ԣI$ hhcT!6s @XcY>X:qZnèQ'>4n0?))l5Rߛ95B츖Ν;H$ƕ+W -nZ>:mrɓ'ѪU+߿DDDŋкuk<}iܺu , *`ƌ(v[N9k \! #F́0`ܹXh ⥮v<8<>nx۷ojժz+&|CLȺ.>\!+3S1VÆdHJJҪ">>ijfͮj׮1077Z 8 88.\(PʕC>}xӧO'Ͼ%5iÆ : ;vGPrei  `ذa?]^:~w̚5 .ĉwaI^o߾ӧRoF^gh]Fr>>ܹs-hcǎغu&W9%''#$$Ddhh @6mB>}f?^}iRzul޼YL4I9"/_T&Cc-&,,#|?v9&_P-!Ab8ŋ)P+ƴ[dM, t3@x\0*}o #3QJtEm' `">>H$8qDabb> ֭^.*۷7FCLL zg޽{c„ 2e >Sxxx輿TV 2>>p,-7\lUbӦM@dʔ)*i|@RRR`"D^C[CЦrŠ@ ȳ`^`!)#P'`(FIU)ESdR2RaaaQ}1d޽;Ç|bt7oF||:vX?>4F=Dhذ$ 1r,i7nĭ[УGL:nnnXbE>$R1d,Z!C>C/XoRH@0M!z#?\˜D"լOb1Pb(ߜ_IPn]15RSSw^c9P}􁭭m\MG:uvd$$$h5I]ժU~z 0:teօ0k,,ZӧOǒ%K0uT=)DEE!22QQQ8wd(x"(bzcǎaϞ= ÇE#wYl~'"^ :/mp˜7m4܋()X{<:@Pl'qIOOǾ} Hwwhժ|||зo_:wFQ6O @EMJNگ/9 &ӳXn<_ŷ~Ea̙ njS*7o… `#222N1xG:ԪUKLLľ}jqo!AHXˑH@W^_Hq22 ;Pb1kz4ic:h׮~7lv9پLS*p1TVE /7P /nWYfM^܁m\m߾m۶-n޼5k曮G4hh޼9&LpܸqCrl޼C 꼽<|/FPP,--HA;dee`#22@a@"k5"K,ׯ_ þ}\G%tk% o B֧5hʔ)X'$~ȿ ?,oNg7c |0=}xYQ2̨gJצMʮiذ!֬YTk=M6E5t^_*W~ 3gw}s*f7.ϋǏϟ/P?xEey},xSSr=zYYYڵq9C7+=ldl^XTtkDdh߬>Ǔ 2W;wh+|||z.!!ƾ}4 ̙3 *[ff&*U|Ӟ={7oT[;q9xxx 66 6ɓ'ѢE ˓ƍa IDATfģGwaÆ 077ɓ1n8D"?^ed'Oa1X {L/9eLY*\l ~CV:r;waaaسg.]Tjժ///tǎÊ+`ffsssoNo5i޽Zhذ!]쀨v000@LLLsssiӦ ))) D"FM[B>>>h޼y<\-[X,η !ԩԫWV¨QЮ];߿yTbb"Ξ=و«Wb <T*wDyWo+JY{݋0>|P ---ѭ[7xyy[nI8>}ʕ+/q|h(xM[ xԨUM1d (*ض2᫭eo ?P˔X4&YO/zYI&QHHۗ yzz?@?&"ڵk"""H*ps{u]*{ Xd友#@g֯_W*>|85lذPeMtB啟L:wYHժU=)-X,&???wqSo//?XSjNUQS BߊJ\./R`` 5mڴПMWWW0a>|222Tu%ܹ3VZٳg)--zgQ C9|?ǙI;DDfƏO%.q˜.]D*'{QM m! HK  c'[ @+T[\./c۷o鯿ǣQFd~aaatA,jɓl ȑ#EZyi}!IDD?oܸw͚5d``CW^"777jܸq&ÇB'O-Z(Y}/FFF$,000#GdJb 264$7 %zlF`MB!լZΜ9>Nƍ#B}&yzzҢEjߟܼy~9B2)g:@eڊ lȜ ͝; ޳+!!lB' B}MLLW^y+55͛GdkkK?ƒf͚!hƍ̤P߯:Wʵ؀<ק &ŋ ʕ+HժU8wlRm]髯"*`,:|01ByWzu3gJUihrӁ4u]tQnj*lBR֕+Wڽ{wd2]v6mD_5կ_?`KJ_~4vX޾CCC3f =|Ǐr$Hf&Կںu+}a͛7iٲeԺuks]GGG9r$ݻ޼y~R)#ԩS˗/]D+V3ٻwVǘHW\qjNM666T^=Tr;[&"]vdt13f /_E3fPLLLiWZ\ԯ_?/6-ZnŊjӼ7F4n8U.Y[[Oxx8͞=:uDVVVErojjJmڴӧΝ;iϞ=4qDrrr*p <ƎK=bu4n8ruu>XʢǏӔ)SF4hЀ̙C$ɴ*Cxx8իWEߍ)9rDY˭nU6,K8aLcǎyzzRRRRoݺ1uT"RԤ'NuY)'̙3*p4i$*3AQjr W9ՖԩSm-[6M  @-Z[RJ駟4lժZjСCXʢǏӴiӨJ*[Nsv_cǎ ╜L;v적9!u֮҅]Ss?"˗/+[EhBmoiΥJ**}@S&cccj߾=h͖-[Ԟ@BCC͛76mT%g\.ϫ\988иqĉZM&W9i " 155UiwߩͥK^ԩS$h̙DZ,[[[ѣ͟?UtΝH:JޞRBbtx8n8'm߿(J˗/ӬYjժ#Gڡq46&56Pw7cǎ*/\P*:Dz3!! BC&Wb4iBƍ[۷qKKK#777cggGF_~>3gbbB|M^x۷OOz? O&ٳgi֬YT~?W}=D ˊw LM6w(00ښ~/r9͜932oߞ^zUطTH(_|UM4Q{/{v[f̍7( @9 :8PG>ӶUNJl'N-~,XզKLLTI@h*gFFEGGի/Tف!M>o߮}vqq!oooZb>}Zќi׮]o kkk>|8SddNjܸqŋ_4YLL 9::ҬY Lwo޼={UP!Th΍~"L%|n.rœEB!… u,F&oFNNNd``@&Ml;33g}}}?8a??QF&==]mo޽UQFYlVJܻw/^L 4 @ї/DŽH[ny BZ`A69tSΝ6;*UiӦ[&\Nqqq$HoO?Ffh„ m6w={]v$Jɉ̨m۶4c 믿 zrM6@###՞L/^nɒ%daaQ:=zD˗/WΖkb3444a˚'O;"RI5˗j/dlllM6֛ g͚իu):r-^zrGՕ @+WH#GH(R@@I_VVښP͚5iܹt5;v$___ڵkF̙3j!״Ѵi*yxfΜ DM@O>jƎKk>vr9]t,X@͚5-ͭbB2탏(9T'k;{U[k׮Q=yzz RN||V9e˖}T\.I&:̦4 nΝ ׏.p￴zjjѢt^hZXP֬]T7iݺuj7n8o>Wo :͛Gt #GRzsXXXPߟt>7̛7Ba;TJGѣG+pss3gRllƋO:E;wܜf̘o3'sss255S}:ҸqcٳM#{rrAlD}`GeA*{Ϟ=QFH$"WWW 7h{.UVMcb1mٲRђJ4b@W.T^_|E@ 5ƥK@wKX _]v$ I,Snhe64uJG5jgϞ+V事?\yL6MmNiϞ=: [.16l@/_.@RڶmKZr:}4M0A9#zJh'NPǎu RFFP(͛7:?ww\':uJJOV^|I7n.]H$"PH;v_JxJZZr#=zty͛7W &t!Zj]|9W>U|yԩSO*ԦM211[/MߦM˾?vr^xA<ǏwZZP߾}}<==~e֭[̌jժEW\GCjr---M*}wjժE#G$"E@?ТE+ϦBP;uDz*c1cD"6lլYPrCOÑ#Gv״XYYܹs5C/III4w\"ccc8qVsH$WII,Y_;vРAֶPuܙ֬YC:bE;|ʴrJʢLڷouԡ Э[J>MLLLhӦM%]jJ!C}*mkAիjs\iwD;G˖-ƍ+9>>>/5Ks\.CQ˖-`` /_*Wф ɓ'ҍ921\ҥǨjdB>{g!bQM:?s-]޽IX}ɵ@&7!##C`49@7*@Xw}rww'''\\.'\'֭[k&44leT*e˖1h5&+!‘<3 !$75jԠ9sիWK>L[lQir}N@{Vo~٣A7n^O<+WR͕ANDjVZUr{!W/g!9rr.BZnMV奂S%E} inڔXrOJJhv߈pӦM+uRK L~:999;ݿ_ߺuKfԩUΜ95iiiԽkw@@ПҰ?4PU8HLV;WF.ӽ{h۶m4aj֬r΍'|B#Fw`YXXhL/̌.]<֭[Gmڴ!@@gі-[8q"9ʎ\.hѢ"K_^zEdmmMFFF4vXz ۶mkw:p@ txmذz,j-[&P9ٗ'qiF0!,֯_=4w?x1+bbbйsg888СCptt>Ԯo֬mo߆TVdddW/:r]Ф@4' |)_ɓ&=zsiˮ7n{.6mڄ!CtT~шDTT"## @ժUѤI\z*aܸq/^ի1cFd։D"B!4h))) vD"ADDC B޽ammoK,ɓ' .Jx{L1cBmJf͚ &`Xb֯_k qutҥ8\W\={gς гgOi:瑚wΝ;sG)UY8@" \umtS~w|e L:uzjժ-hnڴm;wIʊ)S/аPy!4/_DժUTҲk֭5j*Wh|'%ZLk׮׮] ͚5èQ鉦M;wsշo_(?~ЦMhذ!>d޽dh۶-֮]?+'CCCQF9r$C@@^xT~-&L@,[,WWW\D"jԨׯG1KLff&? :}ФIҲ.qttsUZ<=zk֬_#"l!ԓ!pQB]ּy&L@PPk̬3DEE)hB(^zhٲ%Lf͚Fj6lPǎ*Uo ] 4ٳGFFZh+V_~uwwǯ ٳ8wVۖ/_SNѣKo޽DoDZd#,, ׯ :v///Cg\5ُ_|Y =H3s[JԧIyf@~TjCrPߏ \e Lٵk|}}ѱcGĤ\5k]Uʕ+EUL&h-z&(vM0|p_To"&&Fv#.."ٳ .>|Zѣh۶VeiKH$ٳhҤ -Zki`dd;;;[Nm0m45 z+Ki̤Ip5̜9...jʳCrme-ɓ'ڪP>3xyyC055\.Ǐ6t^h|I/Мb<̙3!J&VB!vL4hś>ADؽ{7$QB >}:FYl3gƍ1tPjBBB`ggW̥-L3g(7n*zG ݻ_w{@.H4mPZ=7'#C34L0uTI5k{~pSRӧ5k1bmժUjGwې@PÇVՔXv*sjH`HѮ&?!yB'#Id Kj*ib)Ǐ9GR)UT)S$嚨?$ZOBٻw|r̤СC\rQFt d2ܹSiժU͛")SinSSS唒BK.%{{{;7 n8Łu+*8p[ֽPqu[kݢč[p֊{"@y;~8zO|۷og=$Xrʱ~lР˖-KX64 TZU  f{ң Q"<&XR)p˖-k# $ f8$''C__Y&j׮̙31n81˷mu VZYM7{V2m6!** 1Lq4lo`J UHF`8USD I1޽y;r7o*I7nݺ077+~'N:y СCxɀ֭[?~KVR $ R'N@hh(8::bCHH\goR ɰcZ)Qƌ~0WBL25j KbܹXf ɓ{ɓ' )ѣGs$D BT;wΝ;5ԼH\85'o~UN5^ի/_0P/v( Wd28pK-@sT=dx' vŠ!?G#Icǎٳ1uTL81_iʕ+k2$ _Fxx8pqD"4k 'NĴiP,b!kpP" "P'A/_rܖ!6lf͚Yf\2L "`S060Eϗ{Ah" 4(R|ҥK1"pJ,q?C d ,>|8ѷo_lٲVB޽q&r?^m. GGGܽ{7]ӧOصkpAѱcGXYY) ,$f0Ht2Jpn,b@of8,{dWxU"88{,^4VZ󃫫P{zݻ7$ɓ'A2K I\z8~#$$$6UAplT[bڴiZøqгgOn݊:u`Xl7BS|$θq߿+V`X~=wi}:&NAhP q\؎_/ְ-[7F9r3$ݻطo憹s%JXW&eJL5*{4F W!C~? 6`jU޿+uݺuѧOԭ[UTnIjTRwXqq١tj 44aaas-EV*^@ /_F^ Jӧkΰ쌅 hԨ˿<`e.bŊx1d21`Xf† Э[7L0!D$%%>Tj=-7J*GGG888066qI>|o߾q? ѴiSdZpA{;h̳78>_z̰k.4h {m@R<Ųez vDa|Q6{*hX'z'''ٳ'7͐(b$&&f͚ ƽ(K 2!3l,8>$_E|HʉIII2dV^,X}KѣGLLHj.T?% +nݺZjÆ CrJ"FRR߿6>ƣGX,\d$eʔp1DDD`ڵj1]a`` (H௿BϞ=QHرrF-opyRK }HRɒ%~8;;f-7۷1|p DX@ vލ6mJZ HZt:::͛7xݿj>|aaaعs'>~gggyvNTDgpn*=`x=a},ŸJ+CJy{­[о}{֭[˗/ڵÌ3?(ԗEBOG5g$ozƍjlٲ?~044D֭1n8lR}N HBBP\LdI@0atUg… c֭pssѣ1|NbeeSSS5ABPT)իW*_~ @nSt 9U"Vc?!O &0D]Tt;48X!7ݱb =:ԯ.d8u°}v|e˖ŏ????TZ5N---\ oD6Q9$"!p+ԅ pB)|Y,_F_T4&p3>&z{Iܽ{W իW!J!H*U`ܸqhذa7l޼?Vѣtŋؾ};E;;;=mڴrC 䜀˗3% /_b3d#&N`޼y:t(<==4Lϟc|ìYkQHR X[[.NLLdxxx(U再޽{ѣGԩSpuu۷oѩS' Ƒ#G`jj*'=";GG2#SI 5]vزe~X>ڴjr!R%buz'=QK]F=H4KO=ќK[t gPF)@#>> p9FdJ ^`J^2)jfI@a ::u꨼/Mw2/^QhooO!CET/MM9!bc7._]0::/f=X\9faa-[2((wHׯ1+U7n|/n")H(ؤIX_)rJVJ?Zo۶-5ks>FFF,T'L7oP&͍˗ƍ۷SRRBrr2oݺ={pӧ7'>n:^r>}յx)׬Y.]2W\X1sڵ|i7#HYnCyb0c t֊{B&=9QjX/IcΜ9Xl?#00PI<<<VVVy,)Rĉ u̐,=Ɠ'Ogt.\*T;Ν;ӧO+]OIICpbذa9/11'NPUi23~ jժ{044ĉ'P^=ŵ#G`ĉj5h_~]m9uqDXl뗃@h>`Xt9~␖ V 4~7o^18WTҮ$"n)jfYV:!btN5\6ptts:,qm!44077Gxb4n%&&"::Z)g֭:5jT866Y")) ;vʕ+UҥKj:?%Jdlnׯ_O>زe 0zh 6LSꂺu"88 <==U7=I3~7 w(^8$£vڡdɒif1G޿)S $$ yq,d2^ Nq@Tp4h 40ydEX~=,--ן={.]% U~]vM mlXS 8@(7"\#.!ԜRbYE&`_1]є%J'gm,XV\6oL|}.TcppB}PB֭w/_yѣG,i]$e rzcƌe(H 4/lR\v-GV^bhbbBOOO3'or̝;wK.juS~4QvmgpEZ})ԩS*tR)[hAKKK>{웍cΜ9\=*=YzuÃ;Nʎ;‚hee޽{sΝLHHP;GiӦZkffI&۷*m={k׮?++󴰰`Νzj>y$^x6lHH`ߔ֫WOb1;T>55Z_OOQQQ9( R?3PT(su5hBĈeDF Nc֛QL'{|[ cڻpŋұe˖|#tuu?'M3**/^L&ǏYlY/^{oرL+++/^ǏW[&00P|̙R>&&F5677/ghL&c A ڇt#Ӹ;X8ȒwՐ)IgNaJNmMm .;۷ghhhmT3gϏMIɓk.=tؑ"h[Xս܄ȄK.UrԨQ߲m۶:yIII h"v֍NNN,--ٺukN65)WLL +WL##T:t}eѢE ˗IxMdllMEժU5n(G}vUtiڵkG8p@hѢ>}:?|M}??p1D"N<9OIKKÇya.[Fwmn&+++^pAA*… 8qXY*T{r<﨨(6k,H 7l0ݻ7׍ݳ4N< K4z֭վ}{zhhhk̵kR9s!:#c ‰v*#t>n%y^RݻwڵirX@cK꓾silAb΅(OG"|-܎ZI͚ԩS 1ܻwJLGq֭1bihh8MS M6޽{yrnݺʕT*9h ZYY˔)1c*.:-\PcGM#[[[:tTÇ0`haa`%IiAAAGQ;_|۷o>.\CeVX|yDє-[͚5SHM ޞM48gر׮]Sx˘6m4Ƥ$ٳW777?lӧ(%O?'hѢU&>&MPiݺujVQ&~OÆ ;>}r2@-]FdL4 #[56 ?>_$]5t2 @zo߾ի٬Y3bD"zyy?ȱx]qQ,Rʣ7RSWXc6Xt]IBbh/fáC2&&F9;vTѥKݻf͢7/Vti1$$gΜϟss9U_WJ;*U*˸3_|Qo``3Ν;g{c̘|||H<`~GKKKΚ5Kfׯ=FZZ7nL,yuܹ?ȦML2Dug غukrѢEܿ?޽7]ꕞ$ ˕+-ZpȐ!\`[ni铒zׯj*k׎Ɗ yQ:III?̪U H$lذ!g̘ .|39mpQ:Sʕ+!\R'555K˝:uRzogy/XPI Z:ͧne!m7KCu 8 ewuuO&ߢV䄸8~ pAAѱcGXYYh,A\\y۷Qe2Qi bC0- T82 Flơ}«:H!<Ǐ#)) VVVpqqkL&VZĖ-[pгgOԭ[J\vZ[y t°uV}bݺu(Rf̘jʕ+F\WblܸիWGL {KdɒZ{ 8q" ummmuЧ%իI&m۶PZx+VĠt:ׯ_GDD:'N 99Yv pqq#ЦM*T(m%d21yd4l7oڲ$1`ܼySKZjJyIIIÞ={4᧟~‚ Zjŋ٘Qo =ȑ#SAGRYF^&фS02'v &0]JMUZRTIQҏ𡹩Y>y*(9:eZYYiȰ0vAS/5\4necIrb*6y?mhhdÇS* 1DիWܳg'L@///)8TB???%z_mQ* Lk׮qttt$ 0`###u>դrQu8@<tVVV;wV4sܹGnC&իW>6l`icc+RMĄGz>}:̾{Rtؑ1:@N&=G@1A!¡!?kQ aVJ [z{Yryyԩ<]L]7n$%[ϟ??dΝnFk֬ɹsfPŋ\j:.i_Ž%JЩ#۶iˠ =zT˗/YҶ8VӋ쓏9 ,&F>}zlG2˗/<<.\Ȯ]*ZYYM61c>rʔ) t<}kTbcc9eVX\w|g>/V_ݻwOrR%$%%Q,~S/Z3CZ%&yb}h岳(s.~6460Rx(@'''޽;ON0ܹe6jԨ}9ņz޽ѣb#3fdxwD"XI ǣ'ÇMEXD,ϩ#76|:BOsN>$$,,$ӏHj2>dhh(N777x}}}rذaܼy3߿սr*U" p̘1s7odʕill̵kY?y;3yd̙33F__l{۫W/駟Xb{KKKy!.]#GdvXJ),بQ#׏gyT[{.Wzzz*ԬY}e@@WNAW^G׮]Y^=e2__?Fk*W]v)~ .M6ZallQFiHؔњk1ױxqXUq[NT*r֬YJjk٩W*eN8OܲeVU@ gΜ?/' ^' R =#}j ?ʩt&!Pdr5F)^0%KsoÆ j_f9pVP'Oɀk$&&200 ARb _c5!']&$t/;In߾XK2HS?2223gdvhmm(SLv҅ ,ٳgsD>|ݻE"+UK.e=M\}wؘܱcGƝIKKSJ,g1!!D)hfuu _N%$+![d޺u{ 8dhтʕS"HbO?1$$wfLL꫏?fѢE٦MoSN_~QH زeK.]T%^`` ˗/O|]ƟqFh*SN\bEF/^d۶mu""?sDd,jVV\E`(`%; [Vy^~͖-[ǎS*UwNOvR*Q]f)eʔ#crJXh17Y72 C$fbSvilgϞK.)Lh$YNYŵ& 2Oa. ԫs%:Wy}3]/_rҥ cV5̓'Oiݙ?~<+s.]P*U-R?u*X yɔH$=z4CCC9}tՋ*릦^:;uѣGsʕϟ?Yч2Df{Ac؎v%l9aJ$֫WOC.`F$( .XVZeٳijj]//@ FS)$a?&uz ۗDqJ8tPOǓ՞dX&ŋ5jBF"**JiO5>~ȲveXNbxc~PAK3>5!rFsکbh^ u qƌD444>͛+]bŊ>|8 @QDb 37+~贔Bu_ =2000_߾}UV)ΈbzyyqժUjc5d2TŽ;2""Rݝ:uʴ̍7GAhkk%KHQmn q/^`TTׯ_I&ߟPj‚uԡ?'Mu1**/^*O֮]eʔ4{sҤIQy|ƍ3$$qqqjD+cc$C\lYDN /_f_7mʷ8'%Eqȑ:I=zWZ5xzzj=X,ƸqдiStO>ŨQsN]OJJfx{{ǘ`02jjvڠH9 ĎqZxxx)) ϟo7۷K.EϞ=a``Tڸ?ƱcTcӧOrŋ*V6mxPHvG#,, 5j@߾}uV3Err2"##k.ٳϞ=Zl_~͛7y5QJ5kDxx}bYfh֬j׮s̒zǵkd)RE%LA@a#ǡ1FtӪ^JJ |}}k۶m5RSSѷo__^c[]tTUZ5q.\R<ķf@3noݪj~Dz@UD;/$rO% @???FGGSOOOET[ǏWA`۶myڵL-X@iJƓM7rL93;vr4+=%`$%Q>)\Ԣ+CcAC+PfzԷgx8' d'UƏOfؔ',y,CC}%\Pz{{+<իW ,qg2O>Zׯ+_zUqmoo+Vҕ{)"bkJ ޞM4a9gرW^U|/\nضmpҥ:}W^+-[8{,ht!JyeΞ=[@A?uIPNqUvI m'qЙ^EKQ)-!b +4 G JMMV}3fPB6lȓ'O-vq\s3}ʕ+ٚS-YC\RejQÐ5Q+0Rlˆ0ex>6ۃP1gzf`Qs`&ϯRM[/g֭[ifff奢֭[ilZ"#Z)D?uϟc)6jyѣZ\Ǐibb©Sj]gĉP+W(TVJ.͕+WǏݻytttTrjJ;vdllN/mk 4Y?ݶ"88nnn:Yx>>J-Bs IDAT)@T5d,R@t1p?f5(@)=V1-ZuSNd|e0cΝ;.==H?ܲJJ4/jA4鿏蹍ZN@s1;OX)D,ֱոA2, Ko(RaYIIjՊf;$J/6;+_tmU9|_x1===p\v-ثW/r/W-]bʄ='4 ;T`_|ݻ٭[7Vʕ+)NСO:p]hQ6hЀSڤrl׮G%K0""111j[ٕ~ p͚5٪gVVʕS𤤤ȑ#6lƒ f͚޽{tttH<$ 7nܨnʕUUVMu a@{`ԨQ!f')ZZN<AR7ߓSnd4Q)oBU;nX@ %3)!D"|! .nݺ||e˖)Őppp scU72YQS`0~PST'PD#Rz.*g%rE6uN_y k~\YFZZgΜIX0,T'qws|()jM=MNMMٻwoX"L^lՕ-Zx۷< ޽{c˖-0aݻݻk4"7nroFGGG\ŋ0FE`; RDsHQh0 {)ircOwڏ 1z#lX?UN-׿1b1ƌMuD.\ƈŋb;,iڰ0,4\ڷoT'O",, ۷oׯAUTɑq~8wV^(%toqaffT 011AΝѵkWTP:=!ʠ%\\\plh߾=6n܈M6$\\\mۢFrÈC###jժشi*UX5^P &&_Nʈ31Kmx EP-Z(]#~֭[jY|9퍄eo>ԬYS)֭[jѬ QzuD"\pA: HT>}bqVn Y/_^ϟ?K.0557u놮]b˖-Xx1F ٳ;lmms<B x)H"--MmYur GGGܽ{Wzg/G榒ORȐ yÏ{xd*T8,!)H>bRAb6x Eh;9`Tc$l|M4䤄rRo$'%%aɒ%xʕ+;wprr5nLrMT2n6#ضmagg^z...zd2<{Lţ!z([D 8::۷#i&DDD$Kh\:vڢFػw/d2YL~A*ܹsصkvڅXTRx%ߏƍ5\ϟq)z*zYfW*V~+gh#666m} \~ói7fa?(oˠ8 Mq5h"H$B˖-OnF<44h/7Zlr2)H`ffg'''8qBzO㟡,DV`@ CwJEQH^QA'%w&0^QX۔ģ@!}l?臛r%[#jlp FjEGAk׮:˷BLL |}}]G߾}annMjLs,Rpic7<}]tAΝݑTS@p]+WrIIIETT݋ׯ_~~~>}:Zl Hڵkcذa8sL Y4籱Ǒ {{{`hѢlll>xFRJ I|J*}M05Ll@,a"mQ_8VJMl2N:~A|OOO{zzb߾}Ph5>CR 033CĦhn H4V?@w"wزeK &`p6oތ_~1cr9 6y SDrr2݋[bHMMEz0oEKU Ǐ(>|pwwGѶm[ԩS'yo۶ ^^^>|8V\Y9x"[Xճg`hh&M`񁇇ZѣGpqq)\Tt&kB$*ca Xi_ BM{^z%9aaaիc̘1jЪU+DFFD1W^U)>NeT ݡX)Z4moۆPuy))e(R7mT*^r) OOOԫW]tI̟?3gDݺu퍱cǢEV)"&&[nETT> OOOL:(]t>wK#W*di T^o^Ͱ^!s[d GLL moA144h3[:tH޼ye˖(5l5 6t2 tF:xŋSH|IP*r*pF Щ\]]Ѿ}{u_z57nM8y$&O:u$:qtV(kkk8::ҥKP9-ĥKЪK.A"qƘ={6ڶmoM,YGfRSSqIqeYEH{4lPゴY= -Bjj7@$ߩ( %j"##L4lK.EN`llt:t[nŮ]GTZƍC@@WgϪ}ѢEŔ)SpAݻ;wDfE .ܹsGB/_Ftt4bbb\)$ܞPx ^1[{Q|=z]5\V\.GZZRRR?~˗/U-%%)))*+W==|uɑJc&7XnP'a2e۷osy0˗qfffbQ~} {{ݽ{Gƍʕ+c۶mرcPcǎ\P %"..{ATTPo?֭[k]o߾8r OO<:$q A8vRRR`kk hѢ t~݃H$Rid*(*UB۷oZ^.7pF?yiq0Fh'"E*x&O`SL=zTcƌ̙3];^~gϞ]{TjUH,bP#_x{{0|lX?Px/: wē@¯xˌ!ɐKKK|prr‡T7`޽˗/ϖ\.JiӦ!##o߾ETT^~ŋcڴi Bpp0 $%%bŊ1btJ4g8sk׮ '''ٳ6mpIF;v,:tզ%JƄ;>>RNCbb"t;r:W-; L|F*>!pc?E)X#Mۡ)Z6Ґ88\46YdԨQԩSUTRH$BXX ѹ <+W.)bX`bō7?^(p MyMHY3g^^^022BRR\\\s4o}j5Bn-팤 O;OpǑ#G\>Pw/|PaܺqMI.*1޽ϟ? ::: ŗR(QD_@dff2 OsssL>#G\޽{d }Pn]m۶jK.nݺ߿?:v숃"66V Z*|||*LDѵkW<|8.] wʕ+\&M`̘14h"""j*TP666ѣG3֭[x)J*2e )) 7nGyfDDD`ɒ%pssCѽ{|=T.<ꘄU7n̙3yf<~ ȑ#r"::7nKk׮< {ިG4c>3JMnh!80JtDH!As>#$2`pkׯAl.D` 8K'Ǎ7йsgܽ{044!Ӯ>w `20|pAwӳjb)ShРz-(e˖-͠ 8 :o߾E sNkzwY,(/uddd >>QQQسgݻ### <<~~~j s1aٲeXllllÇEZ%:͉V&rE,sW6$ՇBqבc;wV|%SSSڵKk*gU"@k֬AJJʿb-_DR C UTv-[`֬Y8q""""`bb۷oLDN@733Ê+ ͛FnPn]<UTApp0"##QfM̙3qqqؼy3,Xɓ'v޽;t[[\|Qpwwƍ1qD̜9ׯ1n8̘1 !C# ::gϞ:G!'d2,Kލ쌾E_ @MApGg 8Pȑ#Xv-Hm`o,FI)eRL>]6l؀ŋpwwu I ڲe -3R=-WӒ?v54><[j͛Ӱ]$9I& byVVcǎL-9EAPT HҥQdI\tI(`-޿hDEEaжm[ۻȒ_~-ɓ'000@Æ !:uJ޽{yRJ8u5OppMP] GpvǵcCtt4W~^B\]]5f|2]g:W^$&&Ywmٲe$ӧO:De˖ rW^ Ϟ=z ǘښ+W&]6[۷$m6SOO[ƍ'E<#Ghu߿AQ*R*R,{4izzzl޼9Νk׮ݻ464f0I%"&#0@8W^Ad```w؃[W/` Y}uhGkkkW.͛}ZsMTJQ'NSPz^Ƹ8;jբH$"V\#GdLL{eʔa:u^$Q?7mڤs .333u.e4l:^hȑ#UP:7\N##\2}HKK>/^\ H1Ν;]D/_N2k3Ơ ͛7UoH7oIXR%Agٲe)HKlѢ˕+#ЧOݻj\b6lH422b`` Ǯ]暛H$Ǐ5VΝchh(k׮-| ӜݻwgSXt)pfcX6`i'~A*(Jٮ];ʲKKоX6t 9xh(3}N###_PiekG4[A8T# L0!v$F_#~%R)={PP( ^^^ܻw/ MF###iiid;wNzeϾ.QFL2yɄOtww'u\l>|XyB͛7h" Ykkkv֍֭'O?}4R)"*\rxI>pΝ;:k2$$$3+ш8,Ĉm۴L&VNzxmr'NX`^^^իW#o+ 'Nw!R9i$ 7oROOOP rUZj@SSSٓ{eZZɬ ܌3h``@XLooo`DDDy~e˖ey%&&rƌ]rZ]vJg˖-`޼y3_K\.g64c 5TGYl9̲xܹIDُ'I~n:Ãׯ_׺B'Ox1Yƍc =1fʄmR RWDP(ܿ?ԩ#lB/&O;[=';׮]ʹn:=(_s`ff&ݻG.MUff&yAXFbvXJ\!#DbEtYEŋM6BXvZ>~XczlԨΕÜƏ3CdB)رc:c1X)xF2FKR*rܹlڴ)E" rwYԾ}{+Wlt֮]nnn*&!))cǎ%ёsevGXL/ gϞ=Ԕ۷ʕ+Y\\!Cp¦;))ͣ-O'''.\@Vǽ{Pj@Vb$&)"UZ.WNDoK={2d<::ZLMMU/pB_|gN6e@@[h2ed{^ XR%mۖ#FҥK;wT߿" Qž}cGJԦC)P({n!aÆ>^ AȯD"/:=o77\xyyJfFFe2Y֠bbѣ-.k֬!I}lڴlْCV֥K֫W666y&+qٳ fff_E#,N $$+VP((yAg\cǎlܸ1ĄժUcǎ9f\GÇڜEFF!%KNpC#S^pvP(ܹs'kԨAlܸ1>3,,zzz|&$$VOMq1PżK]۷<...6l:`Zyq?>!FJ8l0۷i(Ģ@J8dȐ"?sL(QȘƴ!Ma.Qzz:###@=V(" MO? ϟ?ϵkܾ}N;))IeA_x4h@̲9QCc,\Pc6mGHĦMJ*d t97qqqtssc p+ C]6ԩݻgYfZI+W$޽[U{͗/_rӦM޽;ESN3gǎ+P[ZZ2$$'OB7ou,D...FFF>|FL7'OVy۴i~W^ ŋ6ӧylժUcjj~b8tP<\P˗LHHƍ9qDvޝuu]6}}}ieeE}}}|y78pf"CSjbB{c@4fI,QDCt\۷ M6e\\ =<<عs?sLI葳[[ _Yf "u3xڵl۪U( P(_qҥ6u҅EƔ 233٢E ڪ ,,  b] OLN4I+E###>|XL\_F(Ʉw+##͚5hLsss?~\n?pB6lt0bDR ѧOsѢE¬() s%K͍.]6lg2:$N +^s߾}trr1/^̌ *_FL=zb¢^F ;ǏWjs=Zpqq?7nhu?N~:ӹ~z!lٲ455T*ePPZ B0ݻwE.e˖*L&S;:+1 l?r?ѣGzj;;wf5XDlcٱaÆݻ7Nʈ;wNmJ׮]>W^͠[rM~{2]aqZ,Y"18耀BQZrZ*Yf42GVغuTy/+#GpĈ#۵k<7ϟ':uJ9}0`&Mpƌ<|UϞ=-[h=}k.n""䇗/_Go3&d}>\_sttիW^ZR7@CGR ȯ8}4iIr۶m*J$sLHH!8}%JرcYZ5lْdO e'>>e~9ׯ_,̧e˖\v->}u匋cif'[fMΟ?_#KE533uuu,ڵ˵YQ&lm۶>[TbE:{*)8}t=y$ Ƞ>,YRdc'N6333>}:_o޼QH$bxx8٣јEu:tquay?dX)FPVdQWu6((nnnT(\jʾ*U%KpǎD:t(kŗ l||<+T@}}}C} ?>GzZ8~x:ڛ۷CקX,f-vZ 8o޼\ܹs'k֬)("JK&MMBY{ ϟeС0[|tޝiiiDR ˅ roX\lɌRnnnlݺ5̰0FEE͛zTAx;w&B j ={ b5)UqmJw}RJRAIKKcٲe|>r%JÃ9r$-[ݻ:/޽+P[kkk#pr,Y=zhm׮9BPdZP6ѴiSvIg򊑅b+BP2dծKA#s/y*s BBBbVer8x`?^eÇsɒ%|-GAXgyVϟ?Ǐsر^:eq%Kuі)S#FTGqΜ9BO\Ύ;EQ(<|4VLtvRH$;;w=L,Y%JКY 2_~: 2MZ>}r)LJڵ֖\f  *#336mbŊ |= JbW$ܹ}AKKK~71ex~ɓ'9qlʗ/sϞ==zDKKKiF'RAAA]f7&L@;;"$/\GUrUVZ:u;S3j(1j(:;;L^1P|( [+f)8K朏I܉܈q@6ע؀zR=v V*1!!Æ J.QFܹs9rƋ]^,|٨Q#ӇӦMosĈ}WN333>zvy)bڵ:ڷoZZbU@g/B===Ë .]*ٳG5jT94\~צMYUaС-prer&P(x"'O,)ϟ?_NIǫ{ܹ!!!;T*eF8uT9s͍K5j :99qر:z\tt>}meʔ{ 4Q&j*_Kt62M]b H431c) 1;OCUhCG ok_1\rƍ>VVV ѣGU*: "===UyUV(\JҥK3::!D"a||<_z2e֖~~~B2 {nݺQOO<#{v.lذ!-,,XD q?~yܹ\gddpڵB8?ϟ?P,Y20w\d2RSSA %J`.]iӦ|gtym222ɓ'AB^rsj(ʕ+aÆ{kHKKc:uR`klV(]*5innnbR~4mZzk.Dm} p IDAT@Pdɒ:udX)28qFFl&wZ`Ćl̻/͛79i$a1377g~oÇU.8*SެYƘ/[ժUI۷=?| |466ζrĉ8߼yCLiӦq…H$ynYD vQ(gcc߿pz{{S$:uΝ;s=C QΎ]vEح[7jĨ RRcv]q_\mذA'2lli+ :]A\^7%%ERjՊqqqY&% CCCt vŋ:=uPֆ)Li6m̝z%׭[:ʔ)áCСCZ(C?r&߻w+V` 333vؑ退6nX/!11Q(ZC_Ey%ʖ-~A27n(xµm+V,tqݻwk<^G]AGesww8-[FRx)-J:S(Cَ"b8Pyܽ{3f^PSSSٓ{C I%o߾=OyF$|y QFFFҥ wQ $9uT ڵkUs|e׭[#GY& @HĠ D">Ν;X/ɍYGB`.]hnnθ8hBVlMe˖2e { 65300`uֲahAO333iFme˪ Qu**Yr6eH  Er'K @߾} tjժ~J˗/ٶm[Yc"Æ ”7n, .Ǒ#G L&~իWٳgkff&O>-+ê\]];wr]$ f <Z+˗/D")w^5j]\\ -Ν;͋f͚:1^nF]^!Ek׮Z+ą,o+ :!݉:U>8q,S9[ӧOh"!a۷֭[uR_cǎ^p!=g۵kW2Uupp`~A7776jԈ ƍT*%sŋ W%چ I&~-K*%(?޽cLL idds"BgϞĉ\nǏϮ]ˋ%KvMYn]у&Mƍy)z rRyMۧrO<*n2>g344UmСBTs׮]yՅ Ar#s30s"u֍ 4(4._\255M8zhD"֫W1YEss x={IX,?0`VU!11+WdN ?gE% %߶mt.)))Q˗/?@SS$9۹sgSμ yEOO:a,+("""T-**Jc)#7 Z (V@tz'ʖ(r \|PNOO~~~ܸqckmLVժUV{ASRR#JٺvmnذiiiY&+VHooolN#GX,Φ(iN82,_FӧOBڵk @.<|0W\ѣGCVZ.ё7f߾}9c nݺzҚ{ʕ+jܸ1WZ΋,X\.Wk駟o9שSݻw|8۴iÊ+fctqqa˖-yq׮]zp!eEhmy^|IDe˖ɓGhB,Fe6+Wfu^//(7ڄZpҥ,UE"yu響dݾ}&U(>Jڵrs9!y)7+WEVxb(Q'O.233yN67sǎx}E:iӆ[.2-PXfF׬Y/YwP_W=z$D.ky6kL'$gi&_Cg5d+WN'c ߖ(QȼоoDG+ :Ĕ)Sh.1e:jL.pH8qBd4}&M!Ck===z{{sܹzP!g={v?~LcccUY%}ܹH$Ңbmm-lL4FFF|njC<ɔJttt3ׯ322>>>466c6O+T@???:/_('@цruŊH$ &=} ,`͚5EZ]-%KrѴT*e@@hddDL!C֭[tvvV…$>f(JEF uQ\}3gΨ udG+R+ ΤptttX,fn+VP9sR,ŋT(lժJj^brX|y~Ϟ=srҤI*t׭[7b*U*˕+'lX5kƨ(޿_牻ʘӽ{jܧYfْU͛W{By ^N:￧DȲe ֧OT3~lQ& kRY!W9quVRE+k7x௖تHNNE@X=zh\իWo ӧOٷo_Yucl'O믿O|UX?#Ugaaa400ȓhӧO!C9X̺u244'NЪ=OW@UرcW2ˠA<=JoDDDW]۩S|dhhh@^^mX&'w"sߍ7@P-$$ܳg(ɮ 0Ovp6ȋR\/ќ!iU)#h,qܹE~NWoݺU_BpIUNOO;իG\'OҒ:t-,,Ν;f@133}`ɝFϞ=M&HjժsSRR HoΗ\ƍcڵ$-//d^P` ͛72ʚ^~Fz-tB77oބrg|Һuk-ScѢEprrH$B޽MBѼujժD Fn@ATTՏ/>~@T2yHJѼ\5R`kkSذav ;;;wٮ];Xw5}bÆ dq.6nBoz|Ϟ= Y\,_sӜdŊ;w^;vsi9 <1!)V:ҹ(ȄR(EKA,%*]Tg N;@$*z+KxѩSRm B*`(2Ӣ,Ew(?׬YiK[TΝ;9rbggSرci>4E!;9,,T<1!l-m0c!/^`ҥNEƍxb3yl!+6JRaΜ9D_>/_θ-ljg_!۶mc(WrD\x9Zh@I&i[Am۶nT\dggcܹaYtt4oktؼy3,--ĉ3f chaavi3#lܸݺucT "##YhhhiuXۡUJKjߊK.LڦMo߾CQ,-- .K!B武:d>M. 4@QJRoСC   1j(T8$=! dw`7AJB,%9'@Q^yiӦ e#GġC]ƍ: c=Nseff?Ν;1{l70~x"-- =n.((@j]O8J*VZ̪Jb, rrrpM2&%%q]{>>>hذ!o7o3MOorFǎYjժEQh޼9) f¦M-s*U0ru-7H@`$&&ɓ ]KR(SSaٲe077/vhP=aժU D;)WJ[ All,VZeԦMcѢEz7nW)br)uښa8C%fjnќrGybB|w&@jSfBw^!Zr9vB a#ŝנ={_~pss!DXB~Vtp]Z"m2Ztimm`t kֹF(B,O$tKʨC!tooo&hٲՓ3g@( ?7oɃ>!%j(qذa/]4Ehcbb @ڕ7>>5'5uZ\霜ҡ\1FxxVЕ:@("%%ŤYYYHNN-R) G(( ݤpLJӓ(TZAŋ1w\4oޜyxx_~عs'õk@]ŋ!KBXXX-(DEE T*R'{cccakkwOثT*̚5KSL1y𑟟[W6[|pp0kZқhjp-~UP兄2i g؋,aWUyKx@k{@ "hժk(¥K0{l-G*U`8r/pXWsk IDAT+ThԪUQK*ZZn3f`ǎ8NPѣGF(bڵu޽acc8s.Æ cC5]i_~ \T/_! J+++Ŕ)S w1c  QV-\t* fffz_śL&cu ! % m۶M6z)#"" @(|7T*X,֢ƙC:u Hh"-JQvmJ4333f̀ R)8ӨSS&QT8{,&O$ вeK4mO<{ШQ#3Fg_!cǎELL 3rm˖-í[xMh!S|`XZZI[z9t[x{{nݺb̧x]#^8::"-- E1>V\MAxx8$ &M9N7n5kꘘ''2ˋHNNf7JS ** M4)Q4Au5 `KK3{uQmn` DX[iڵ }a$Vr9ڵkիWj⯿ѣGzj$$$M6B@f0`R:uJo{m۶չ[~ѯ_?3:\.עЮEDDTׯ]FʕY'py1|M1b5j`V5Aj֬{i&Η)#x9%'mmmb \yar$%%o۶ *Uuʕ+QAai۷>}:~;W"|=РARo>׏ܶE\\vޭotq{Aaa!.\333TRN1zhS N:}p3F+Y"]tav/ z611Df͂} PIvz-M)i֫Y&LP#G=w&Qre8::ٳjd"p qM!M<}alQ5ʤm@H)<BRAiL(Z"QOD,VU:`ڵxrrrpUݻ ,AТE xyyim! =z4V\Ç?dfϞ:@^k.sΜ9>y6mld2̚5.,,DZPfMfª~ud2ݛ&%%BBcƌAXXPJkׂR"s7n0CT( aP*:4yq֭[ǻ .dחS+11?X[&BIo>&ׯ/ŋ\. iZ B\͛72e ,--annQF缮L)˗1c 5GqƼ]i<7oFHH#iff6m <<b&7 ~f̘''2i!gϞ-( +W6ݻ5j &lll?P+dcpE899RJ?1zh>̟?NST.]vl&M2yÇ*={,i}藽/a &ݻwAAC+^"+Q2iXӧBCCuܭ jԨ(L0֭c^ۆVfH$Z4ou3d(sMi%x={+V`>b7mڄ[7P'* bBZh=C?|uEQXXrsWZX̺ 9fggc:qtCeml@}LbpqqAN  pR4AQ.] T5kL ȑ#?UFFmaa1c0|qUBxiǏ8|0 }p+ yyMGBT"88^\4Z[ՋvڕAŋYpRo[+WK.a &m^D "0wT' ty^W5ԩ|زe Μ9W^8O/hnhm6Zm?J+߿?qjU(GGG ///٫W舶mۖ^?|[!jĢҤG!Z6ãX^.\!M4H$B 83vef\4nX"ɚuNЌ3гgOH$X[[#11S28==19rINNڵkP5^~ɓ'CT bsss!~z^nݺQQQ v #Gʿ( W\ вeKW{Ǝ;~HLL@ @ƍN:1n|l.CHJJGWZ5 87aÆhժD"L^^^F7 z *((`^H$_mn߾mڴAvv6n߾)_bE 2o9ۛ1cFm޼;>|T-ݻ zjłby~I!^I4A_'wD d+ ќVTM*رu ЗDRXW /akk:///4l٨^:|}};ܼyO5kX*O?BIV%)֭[akk {{{l߾i󢒹III֚<ݛsKP5k۸z*mڴAzo6#Rԩd2t_1?~ h<{ jՂB(S,McĉP(d7nI]ooo;۷1|2̺u"99W\5Qx+1 cɒ%qzN<  _QreXXX`ҥF'sRGG2|paҤIePޫW/Nr)S`&=~!!!zB(888Pt^[H$:{-ҳgO#'' \Z l5Mz񰸞W|A˩- /E%/<1!V\ xWO˱TX_1XKjG;-EՆ4zȑ#lԃMKjQhYfݻP*֭(B-yA(B$1gc0tPXXX,իW m۶x1 !HVi +׎Tk 8Bľ( 6g>ws`ggqaJL0boo BݻwǠA Xd˗/:/ٲC.C.c„ {E6mQF߿!U٭Pb=z|@P,S/_yzsss (.r\g1x JsNC*f͚Ϟ=cFS% ]tUdggO@Qsq,!C|PyyyH%bsi( \+TsW2>S7z۲hٽ{y3g΄\.7z8tP(JoM?"88,̙3AAf͌^19džL'Nʊ˃EQW ze5A(M6  ڵk޺hWQ.qnn. B ٰaC&9^OJŋ=z4,,,`ii$dddnJs!11ժU!jx˖- > ?cZիWǸqpbѪ޽{ `۶mg2e B!5jPO"]\\`mmm۶BE&L(>A||<˴J*b H$g/P(4jl>y򤎲dRR%ZwyR4e,^|Zjǎc>5$!ָ>g'uk,H$-.l D{`Pׯu&7n@PP̰zj˗/;"##1vX-;LX))?#GVVV/4V߿?3څ|׮]wz9!k)ױxbiӆ!rvvF\\2M؜8q"ӧQBXYY1 ĀbVќ;w0СCQz2mK.hҤIo_Raa!4i7774HJsLh(q۷zԭy&w...Z]JpξP7!u1Bpu|?.R%7/_TjR Ah:˽ԁ%v5Zj׽=PJ 4##:;ݏjܹsM΁SKSt}}Q}۰aCNp}# Mt۷Αظq#ӧI(R1D:|0ppp!Æ ;ߠbŊ£T*yokRT+ĉ}Ӝ,sRJEQXn,,,Cjj*<<<@Zrʔ)>|8#GZS3NӦMӑ4 ydd$C_رcqI<;vwL>9Zj իe w:1|ps޾}ݻ8fb{nAwfڵDf  ZjigφѪatZb=Sۣm۶.##zDe˖qtfѢ(&ȖnJL:Q{彖gWGṰUN` BJ `ccɓ'lEV(Z،֯_zk {nBpܹsB'ʡRРAk)hS;AO N=.y1<\S:t¹!Y:qFbBlZZZV!eڵ BșP}%֖nN?E;Ƙ1c- 6 fffӋxо3gDݺuAZ.944 ,GaĉZ;ժUرcqsJ=ѿ+J㫯R7Μ9@M/i߾=!ś7ouv eNNLPzu>4ͰZq+VM.pArӇLo֬/AӧO^gaذan|ЩS'̃K DXfͧʿH)C eīֶVx=z1c@P@*_~yf>i$ց-yݺu~}5d,._ P4GonذVVVdsi\077oƻ] ^cƌ!VVVzsF\ڵk(,,7|@6O3h%dVV )))T?~CB*3g,.//Gu\ȷnQ7n`ɒ%gGGGx{{RJ"]RDDD5I믿P~}D"L6 G\]]akkqƕbO>eK3_EQP*7oƍXEa:;EKxx8ss^gooo2v[lX,FDD΢̓'O8]\\x/cccK}sʅ^:X9<)P:NvwM|l`C}c8qD(lQKKKֺ֮]:H .\KKKo߾ث#F\. zLo ЁӇ|F`oooԊyFFТE R%*Wիɉ5OTͨ(]=Ur9 ̶MS}}J9s0l}tBtAG@RchO?,̙3666 DMDhӦ mۦCi| D[[[̚5څK.oÇÇu\i믿F>}Baaa?>._ Jd|>|eiO:t!f4n߿zC5$$$@*)cǎz_}v{{{b˖-W)BFPZ5 }vv6@?*WEϋK9ppp3snݺѣN?""" zj^ߍ~iiihڴ)3o={v _> ǂ:u cǎe\TVzEQu-[m2*n7oex5BJEPTB>}JTOVV󻋉Att4ԩ#44F$/ݻYfenRRʼ􄙏ݦM81ҤINJ{ Rretαk׮J*Z*(IIIP*^-XѢi•YkcǣRJ֭[???lٲ͡wЬY3XbE|ZuVVvڅ8ƅ Ǿ}X__Ν;ѯ_?f#JѢE ̛7.]2BBe :N&/)vKKKD"t zxbϏ5A42m'sT3g} <!~ȹb AODѶm[K4~:D"ۑ X-HD"ƈ033VVV3f 5/V(vU+RO>T6 `O\.Dze8!,,LwI zE ޽{Y{I:u@"`ѢE:oz*##/^+<==M(syhтRܹsq)DEEA   @\\# ???D={V;ĉHLLDpp07Z*ce<$$$Sk׮0i^ɓ'A_2ě7o"88B'O.3MΝ;ueҖ&//!f͚ǟ>}Wώ{z5N+!;UVA,k)1gϞZj浻yV|ctRg1 P/GNfU*uիקƿH=Zi 9@&aРAr F%z=ʊ1I&`])))JiӦ:f\PTpww;5XXXÇZUMt NRR*L&c?]%Kn{ɒ% ѣ?犙^׻w0l0qq9k֬@ ^פ\ 0@'ɫ;E/m۶APbŊ\^A{ԩSݻ^zA$իW!X|9rssqbfff@ `Jhh(lP۷o#%% ݻwǦMS|M|0yAzz:RѼsCɓ'C$!$$k Djժ-):t/) `mm2osEXX:uyÇ}:M/^ȩ.EAڵuT腳(S޾}*T/YǏZ2 9sX'B:/4)`gg3fޒbȐ!Zƿ'Baa!nܸ;wbÆ ؼy3ߏb@#22;h۶-BUK%ڝXl &[5k4*0almm;CXx1͙)%V#xAC.#11kuWWWddd{νfKfӧTӧ31;;NNNc\iB_9 DFZiGzEA&A cǎ&yݹsqqq pqqҥKƍ@ 5ի'''۷/.^T&wH*LJ R$ 5k9s?(uѣGk>%/^ DbhٓYRt%A" 99(cR_5kΝ;?GTXcǎ{Ύ;Xwi:Y;w9V/ Z2{o;;;CHH<<4x7&Nȫ?}MQ|7 DՠAsMEM?FBB,--JB׮]ann{})lllGXYY1-Zm۶5> 11b8wN 333<|EaÆ +M<|Mcǎsssl޼Yo 0|pV)ۈP(+-[пx֭CvrGFF޼yYfUVE`` 3133!<̙3e&KsI(B@@M^1++ z!]veϏ?b 6/:t( a̘1$mn(,,X,ʕ+YT*̞=B"DM=  ݆_~f+xEƨhԨŪ#F@"4jߗ Q/},W\!ȿgٳgFcB&ol۶ fff 1:Iŋܹ&tX_x1 #mmWzuI]c@Q uٳgEaeeÇ 4G`ff~tCIڲeQL@bˮ^|yH$5jAʕ& %Ӡs8C+޽_|H{"M@( ˗/LJ@Q^dx! DRp3e`` v8? D-䐔Tj`'N!bW_ArqBoSQ:Ξ= ___c&߅jݺ'ۅꫯ@a]yÇ̎uQdddm۶ u  x=kǎc(ly _";vF/X|AKj@`8n ̟?rwEAA,,,LN;_Gya׮] ~dff8w\\\fV6sҧO nAbhۘ(eT`033+5_~$6Z]uְc 1vX 6 Z[G`wP*hj4:tHvaذa}- Bz)WJ>Q)'Oz갴Ç/c 5[npwwʕ+#G >>*T! QQQزe}͛7سg%)иqc9ryyyؾ};Cc JJǏHڵxb"11X\q4c޽Ů۷/L>AشiGϕD^{,q&]9 +>~:Vرc DWܹsPlmmq|V:7 зo_>|Xk'hiӦ zp̙ `s8n޼ \ݻ|u:Y&UСCKVqqqZ~] 6ˡFy?!Ν9n:Ooٳg9kϟ? *` XgC4999 `ǎ% 7T*eݛYڰa@n:bٶE᯿#BBBx퍆 @$AT"==˖-Y'x7oބH$_|w٠>-{%mД+i? *TP!h۷^׮]c4=<cfᣌ :oTt֭[AH9- !8ehح[򪋢(̛7ڵLCr֥ncc:HJq)R x˖-ЭؒO>ͼΜ9oˋ ُ?P{Xs3^4ܹBҌؼy3Q'MkR>}ۃN:I>5jPUh4)’%K йsgVjD=P^=\N:yjUDD֮]o߾ŷ~AۛYlܸ1qy$,3331{l3XkH[[[L6MGҥKz';t177G.]Pٓ>y{o ,;;1ҥ +++lݺػ! 6˥?v ŕ+W Rݻw]_,X=zF.iR5ųQF?`T7|cuƀΛ,+V4Br̟?$}i熛7o_Cy"$$={<ޥK~(JTZ HT]x5j` iӉׯ_YYY077ǼyVR ɠ( mڴA X''N!j's[#W!… P=z0Ç1b,,, 0rH&x埍)4hN:sf͚kk2S˗AHcҹDE8>صk5qy ءC-9 A߾}T&,"$$ 69U[D7oֻЧObkT*d2,ZǨZ*lmm H t 4 h믿! T* E&U !bccѰaC<c7oDŊakkcǎAѻ*ٳg֧O{A)6k׮ 2[nzݻTA#//nnn UB)0aٳ`ܹLݻ7#ZtLJL&4UaWׯ-ZΝ;puueM>e;2/zhw+={,[5akL0q!>V^;29ֈBjj*o' 77˗/;C_~%lll ѫW/l޼;wT*5:!B 4iӻz|Lx899Y[ǏۤIv[888ޞ3OG?<aLx~].Z*Q]͚5ѿU|wwwxzzԯ_~X|ѠAPFm2hɟM41Y_@ybڴiZ6$&& UFFZhHn:Bރxrr2N^(3H 4H9eHRt4mڔDP3![R[n077E1[f !SfMVJMVe2ro>|0I ߸qAAA033ի)))jPP,--ëT*?&MҺ=zݻwC AŊAV3gܹs&q6yyyHMMeh^ڵcl,^ n۷o#G`h֬މRzu$$$%E[ͅQ>GG6XbΜ9ŨS+tbQV- 4D* >|H5k|>|JܹsUV!؁]ʕ+CTH=-&& {boDd+`)R'uVbjjݳѺh:[wց[VօVU!$4GHI-ܓy>O]>|^"lҤ ]]]K$Ǫ ׯ_qԵQ UVl޼y)IN__W=׆2x/l˛dxQV IDATm:~kٿ`llIIƍ nؘѸqc ;w,XqJ桹*)3@J!ǎ#1|CjSw>>^^^ փ6xKtwwgttE۷K>z(7oAWoErѣieee>H /0iӦSz *h\GT*,YB[[[̗#v R"s3ٳg=Kjj*kժE\ PRqΜ9FERTO 誥XR4} #55lѢKu^L74xÇ @46 /)T*?/z!nxARSSL;v7n Џ7orذaAouCPT oPGϜ9ׯRś!դR8aCk:t P&رcd\`ξdj+uȕZm֭[ѣ:MZӓ}ԩS)JopΝUAu*c,nkksŊ5k5o߾ܲeXJ;vVdd$7o,.0Ξ=˄J$sԩo*++[neDc[Ŋhȑ#XbJL߿My<(?c0ׯ3&&8h Mʕ+s%67C8pݵuMl߾NNNI''={$N4R/6CVVҕԛL#GgpBdO7o&۴iZPfR"## Nҥ ܊ :vX;t@ eܸq3h3k,ZZZ3qDd2 ܹsbBQw:U4h#VXueʕCDW(++KxUzz(+ڻwosңG|T5J<={6vwj*.\baD¨(.\W^KR4-T*ݻW;Vׯ_/W\a>}P(hooD,7/_F/¤:/^{! nܸDWT*9m4rFFF9sҒ*UҐM \s+ uZCBn"ZZRļG}M߆WvQIZj֭[3;;+Wcu%:cÆ EMHHЪ^L2..NPз_r!NjYfimڴĉ<ժ͍&M*rwYf^T>,_qժU௿bbb"P(ػwo࡚۷osƌTI _mp-[Fl^tgΜarr2ܹ5kI5:L̙3 5,Yq |2)H8rHfeeחFz)s+:UO_T*_|3ZjEzȔJ%Sw#GP*&?xo5 ڶmQ(BLR|ZI RWZ6m28yܸqtqqy%^'Od֭ ~~~\dcN:nnnH$رVHݻw %Wm m۶Mw-[dTTŋ2dkER!ӮbgiC;[;\{외Xm۶FÜN2rիWѵJ~D^炙;˗/WdERښ3f;Nnn.{=uDbhԩvx<55AAAtuuժ2zhwWZ[[k׮Z\jlT*[QT3($4pMe),[ OFFMNXrr27Ѹ`}u+jPzu0@sG;wk޼ydީeׯ/t^jڵkk,ݸqàP,KKKSJ-,,xQ^|ٳGZV- 8݂!Wj4Cr k֬IDB 9<}6mbn*z1ذa"Q5r eǎo$99:t 8##/?Bi9ydT*9r~!M2FسgO۷Ou% +ۉ'[?/Mhy3'0Й P?Se˖-tqqE)h 'Oÿ0W* /_#GPH.r7DnkkKB7aݻ:*Gehƛ7oZCv1'' - sUgee1&&άZ*+W1jР5kׯNg_JJ WR*dttQ 5kΎtuu~.\M6VR:t`4B\\\خ];BqfgKi03/^d.](H3f/''֭õj͛o[ndgg~`ǎ~.E5///~GLNNĉimmmT^Qff&)$V:|l&v9Ǐ)>\CB5;;666H$Y&/^R a^byy~iʎggg}zr\bwޥ☔D{{{FDD,;}4«֫q L9zhQ^zLHH(?~kL(h.7onT*eH)Ei_YjUdKts٢fСC5MLLyq/*ӧO7hn?_|Qhz-Xvmc|'tss3V|JIIaVVdHdy葸#w+V IZ'U,--}}C~PΝ;իŖ-[r#6lsssg33@-@9 \r={L& 'O,.U*g1ޞ~~~zO<ʕ+ٸqcJ$\Κ5k͛ǬF*$rNARGv+% T"~[j J3f̠\.gxx&@g̘azqsk|;v+VJwkTA#8++zܾ}{o> @T7|Pcٳg &B-{nӧ ( ̼)5&MDGG駟/RfRJ%tV.3gP"=3sLZYYyT*Ο?RM4GHW9*,$ATR.YĨUXڵk iܸAy"pH1-00Z--ieaA7ggǎ;mll4j̞= BLV999*WgϞ%eVV}||۷cښCe^ FGGvb ;;;iӆ .ٳg٤Id2.[vb:u@}tQU*tpp00УG Eפ ΁;6ݑ';]ޓrb{͍*W˖-Ӛ̙3)Jׅ-'GRiӦI[iii3X?44}1+WBmV煾bs^'+ :99iIߩ땝M_m/"DΦB0  e[lI@3Arι/@|ѣGtrrlMF/&/T*-292++2-2x'O f]0k"͛7ńC\rJ0>(dL^C_K#$V48,,8p@* Ս5o1#FFLLNNfXj {g[ge 4n} mmm k]>w]A`vv6?3* V\<2P(LkoÇ7n))'O,>?ΔJڵAރ/^p|wheeeGPfE!Q"^8_qСZ;wB ʕ+K=Pb`i [xxV=cabb"=zThY6,٬Y3r-! |iD"=/MrrY7{NJ;;;աǎ3^&[fƍ_4J5eH)fŔd&e&MzSW5\֬YS0>L@w{qͥԩަM=(aQ ,`9yN]veJJ BWի^>}0<<\7P(8~x>z T O`xlOQ"/홑_vm޽[kZK&EgRZإK?7LBDBAأGE޳g8gQT!>>'OӓF%_r5  8iWME/".2zÝюQhȠʯ$S&}T( y[ަMB%e˵k@]i޽;>zH/@Ty1>f9F~xK2suKT*6jԈ*Uқ7h V\٤(H }ի ]uϟS*2((qմkN \4BZ2z%g͚ELJJ./h @LF; 0tfeeojj*5kF|w MT2TdȈ#ό Ο?^^^oFRm۶իWիu>}}}Lǎˀk>[u J؎ p5h<>z<8|9zPT 2b IDATedd-[jݻ'vԩDZ!֖_6faCZ8p300Рdjj*[oNO>add$u*ڜ7*UdzƍTs,U!coE2J-+V\.իWMz 3g] sy4jժOOOd2X[[$֯_5O<\.I,pTRW$''CTbڵ8{,֭aÆcƌAZZZcΞ=Zjx Vp=>={Dݻ-Z[nBCC`<$2épi^pA.#$$AAA:`ǎok.`͚5 7::pIп ܸq `v+9shӦ N:D֭[_~.]RJXp!222O>ņ гgOH$ʕ+c„ r ?P :!Nz< ۸QqaIsRR`eec8y$BCCsNrwaڵصkV~ɬsٳg:fqƀЩS'ԩSZjǽu yooo:tHPooo=zE+V֭[usCBBn:\x6mSXdffn5kԩSpBCCvIrc?ɫ(J*qС_~tppڕ3~dff8=zh)SյX+)SʊO>YhiiCchL:U3`o8a:6tp-QySϟ֭[\p!֭KTWjU~9sP"|w(HX\9N4gϦD"1x7t֬Yz V )ZMFO::~L ;;riiil޼9pРAZJo'[hAѣQ o!f8ܸqQQQT(bspu_ʛ!Yr͚53)YyaRu 2)%\.gll H777֯_p ݨNt`#:i !a;:72b~VPZJ\͚5{L7N󹹹\~'˽{j|]ƾ}‚K?z c Hw8Bt>5X ׅJܹsiaajժij5([[[z{{%… `Ze׮]:ʕ+%\h̙nݺ\#++5j`@@x<](J1TqEt @LL ._ / ĉ:C;;;~T*EBBΟ?-[ӧhԨ֭;v$h"ر* w܁z㓄`Gx6`>RkA0h 1ܤf͚?8GAгgO\phԨϟaiisΙ ȋ] `xa*2LCq+؎h[~BE܇3Hc^k?$.ŠBRi}? _~%84aʔ)Q>|aܖ `ӇD"[oӧOcǎ-[f͚زe T*lϰ}vCz2*~zt4G؃c?SXژuTE jWSNO>4hZlw")) òe_~Ť9d2䯿B\\fΜiӦa֭%pyL0D ÁS(^x]b֬Y7oL"o*Xt)6l؀ XjҥKeiA*U 6@VR&22{/QfrXr̚5 ͛y@Š}UT: 'ƍcѢE/yŋVQAдiS$%%MXO<),cFf55`5 #Fw}gŊJ 4Ta7ȴƵAP6r .ސ!ZcD YYE.n՘OLL RRR0tP;F8qX ) h޼9={ڵCEo5/(4nIIIPd;n4HǛHLG5{WZI1C ܹsc={իWH@H$8p #11YYYFU$۷vߏ>]>} 6T*h۷}ӧOѲeKlڴ 6l6;7@rr2&Nu0@ny^C_t5NNN ,KDCRQ͚5C-0bdee!99WfVA?c߿?Ξ= ;wG/c#""zj\~>͛7"#pwAAHKKɓk׮8r2e˨R"EGOid*<)YRUQ*&{@cee?'OC#fΜ)*5bLh qaHR<7n7|\XZZw޸|26n܈qƈ¦MT*8 Ta7# `oD}`yHIIAZЪU+ 8PTĎ9)S`Μ9YDDD.]1nIR0i$4nXԯ_Du14jAAA?>k׮8z(M7-- 8y$vڅ:kCɓ(W/^ HfffUFEE!%%P[T_EPjp)3@J9,̚5 oƔ)SjL9s`ҥ{.݋o,,,[ 7fΜ[n>áC2b?D"``XXɁ X|9j׮6m~C^3qqYR%ͅgM!"o! 텔3 r6R>|]T*X384$$$~طo 0!!!RR(j| r5dee ;wN#x5eGiJKKK0Ka?-WغP*tpp%GmqUNuS;T 1*MGoT֪ ۶m#֖XR%QlرyW}RcGCrs){==zW^ӧOJm~2fܹT([.W^|ُr%}LzN\f x#(!p*y"Cʕ+Qn]bqVZG5jRRRc$%%!%%iiif Ƌ/0sL,^ׯGժUm۶bo 2<,[ NNNh׮dߍ7_Ell, ???h׮uJ bXA}Pbp/<ߖcqB&+:ќOժUq C A-pyB &Ddd$O^BBBJ$$fϞ ΝC&M~5?3Zl㏰\~~~~&ܹ ѣGM -1cѣذa߭[7t۷oǓ'OLL,J" ȋN -41… pww/1$"" ay 0舰0T*ܨ U9ųg#Gyh׮&MdV MubrqM=z,r tW_'[bMq mHthK$`a5,5uy?,؈9@k׮EDD&&# R JP@Q WdA`_w^[_|v튰0HR\t 6lٳgqU4naaa(_< ʕ+UaÆԩ I&믿ƶmp1\vM#,?8u>C\xx뭷6_D.``ʕҥVx*U7૯RĊ+4 b~qރ>P}4TCuloKP5kv܉#44?Fu.ȠABf7@|HIJM0ءCU\۷/T*U8 >d f!H Gڊ | Ǧ}XcX)oU`ۛ`ĴS>Be:R[6k҄bw}uej?R"066V ɪX"kIl4ƙOէGL=!eztT@(h_J$>ܠsֵkW֩Sׯ_?pҥ|ڵWfժUw9SKNN܊>322(ɸh"ŋ 7o6ŷ4lЀRk]:.74jmou@K$xdkG\p^|999 fʕEAةS'vUc'k kJI.$Qq|6@Hv-wWZeԩvϞ=cǎq۶m9i$4111 ʾUٳgO5sڵkĔ޽{%>Je˖Q1YnݺED%K[֯_`ˋ5jz\ )`5Cm|{QB:;;399٬PG)éP(8sLݻwˋ\|~1o5Ϟ=-}affcڣG?SK0رcGJ$.^؜ߧA*ڵ_ag 111ܹsC+++Μ9SǏϜ9S"~/d^n^& ON:Oӧ#$$ӧOM&aXXN:#66_~%za\rwUTAdd$V^m=.|ԮzT(*0 8OU ´i@ݺuc;uƍcذaAI 8$x R:c? O~.رc>ԟ{дiS}1j(,\ј6m IDATp=ܽ{ݻwqA wg_AAA +W,ib XYY 6ā zƌ+}Z+S|8v܉+Wиqc\pCE=e,YǹsХK}%t7nիѹsgs5|۷/ )q[8v6md.-[+V`ʕ}V#G\rHHHѣGaaaQb3dЙD*Uȱ_ӧO@YB9vX5l09r$---M0`CCCuV{f^C 1:VW?vx֬YT(|Qc pe` )(xYΛ7bǐ!CO 3pkkk=zNNl'SU@ڷm;˗/7|k׎M65˹/J3'LPd#G088r&L(Rk׮رcܺu+,YI&qLHH`Æ YJϏlݺ5?=suw^^p+7n4R;=== xxzzriee?DذaCT&*q,q&.6hJk%eRuqx###%. 'O,cٳ˗ nݪ϶mNggg_۷gÆ >ڵkiccÐ^xS7ٳg\dx7|CEN c ={F]~r2gh0`@?x` 0(u#44zz(U k4b;>}Jvɤϰ"u Y-XRM41pHHH`ݺu5믿4BJCVV}||3nO4/\H&QQJ$ZYY[n<|Ã%K_Qܖ@YyӦFPd2#l3f e2U'Nm999LKKgYfȑ#٣GjՊQQQonnnV5jwy)StRdxx8]]]yIqQ'M6bHСZjF՚b߾} ;w~iQD:aҥKY\ƣGHȿ ΝkTXˢr1bD-xaǎK츯={2""UOTQf&{.8 +Vy!ү_?~XsΥ&%%ɉ*UK z&MA[CDVh% FAN( Gг䈦aQ;﨨;} E:,RDAŎFb4c[-v&!1b=hb cӇ7ֺk齧e޳{?K6oKpBpQ3J BD "aʕ*5kΝ;!!!8BxѣG:߳Xtر^%QNх ШQ#X,5`شE"oɓ'sN]3gĈ#УGDEE5jP:A755'7nN:aРAX|ZjxǣN:Z?vYXYY׿ϔ2[JL+++p\_aA"//סSNtEѰaCp\̟?_齕UfeڵkYe9'Na:m֬zݻ! ѴiS۪cΜ9 "zկU|!??nnnhҤՁ+/aaaQ;?A௿?7|4@>m"R-w)]iA!CШQ#ƌڵk6cǎ` ,PZ&lwUK“ zK';J &.Ի[uɖS9%\lto3{lX[[233f$#F˸+^4mlx^ H]_8}Igee!**J>Yɋ1dee?@߾}1}t >111r l2ȑ#z*;&Oݻл@ 0.**… aWٳg_>p5_gQEYYƏ/weԬY077k͛77n]t{E[$ F "ŋuɓ'+_~=l6t/R:mi߾=:t ɓ'f1qDC Ҙ7҅ Lދ]3rXbEuLΝ;j… dmmM """W^4qD***ҺMm f҄ϔLƍD*-UT>}4ۓ5333ҥ m޼YguԿ"" Ռɒ~ ˳DQ.Z5DohтT/^ϩ]vzjZz5bt y4kL!PM3TN@/ILS1}M)գl:låM6ѮݻAQD"1Z ""[[[3gUۛ>L֭;v?Oe0 75i҄uFeeeԴiSz |8-^V\}YY=|jժ%?f͚ESFFFtcCo`zGׯ_?<111Z[B  ,,L!!&&*VWΝ;SK.IBw6znE7w={NȻbIWǏ> D (]u=wɓ+|g;9 +[syhѼ9 &ԲeK m">>^o<|]t!..N ,]KKKL6MێD"Aaa!826`ffҵ^^^hҤ t2DvcϞ= ;"۷ok!==?FzPfMܸq(z 6665kVҨ uZMbbb'hզ:Μ9'''899E b1,--ޟraZux<|>՛ڟS\)ԍ6=NdwǎqQKD-[.H'W=x vvZlٲ.\0qttJmC_hH$Ȁ=,--~* 547n@||ֆ02eFll]v(-- =|ϟi&,\ǏG~Ю];٩lb H5 sŷ~={̙3غu+ADƂ GGG!ߑ2w@"`011//ddd'w"Hmeea0v* -//G>}bi&ۑݽ{ňun͛7駟0l04 @hliȨh;3p8?@ӧprrBttt ȷmf~޽ "ݻHS2Ĩ}V'ׯŪ,s; P_I&ׯ_Ekkjjզ&=zP~:Hc _|5kj`5ԩ Rm|ԛ"5.BV*~ ]FzN. QE#G 55!$$(//MV3!::QFWvBdd$ڇL=א HLLEj6deeo߾`XZp84iƶ߿mjJg+HSNrCLkjj*  ajj8z(]|m;}@DFϴ+7oDpp08<#-- [0r_c ޼y=zf#==RmZ O>EӦM! Wmy4oM ױQBЧ@=׃5 5k ==DKVsȐy5(=z,K+}#cSJC!H`kkTy>FBdlmD||<5kw}Y6#eBIX611M4Xv3fVcՆaбcGiҗ|;* PYfUi0Mj|NW YpOrIPVV0#??{E˖-AD@ZO֭ *;"##u'ѯ_?Э[7lٲB'NM6҉/.]3g`߾}شi,Xq!..N. 4˜#Ѻuk$$$`̘17o;ٳ7rrr;:2dnm>2L0DfffSXr 6l6~K.rعsg;v,:uJX, G1X"˖o>%q|2H[Hyy9,YRCy/h|@L:Uӫ+VME"I&_F5}iӦtppJM$ ,X"yaa򾾾';v!%%EA9[G ݄co}߇ bcc+ΝC޽0 l6&MǏ+sttDpp0^z/ TExxxv%$$h;-/_ė_~ aRw(O۬ 3f܇=88XN˓'OԦ GQQ^OsppI⢴Ι3gЩS'\|SsÇ6l: -- ۷oǪU0m4$%%[nh֬j׮2%|||pƌ+V ##ǎjwٓH$raAaTyӦMAPP }سgAڌχֺP^̜9Sj4MPv|x|~'|Bݕ^D#>}j>Aos15uTa˖-Z-"~脇w=LI\_Ѵ"00PիWV[O>Z|Z r[FW3fL}|lllЩS'I%h,̐f|,".5ZBJW%Wϟ_رc!`ff~9cǎ(//ǢE@D>|8|}}ǩS)S(W%9992d3f ϟ7"337 %%ްQ:v~k"QQQprrBݕN}HppjGh'ͩ,>B*`4U}ak'a!*?Ä f+H9o<899d{kD"h+Cpp0lel޼DdtR ??xO >*w9?@mVӮ];v}||q=dz*bPfM,\Pa# "mV!۷˿kϟ?Ǎ7pqddd`ʕHMMERRv튦MWDB/6m]")) Xr%222pqܸqC2Ww@ @tt4Zcǎŋ/;Znxߥ055ѣGu;vtALs7<>492/_.` "̛7iW;=aÆtQW-Tfē'O@of$]@#aȑ*#**Jm; jfgϞ6+V|Y7i۷W8W_a3F.Pp"0,gz`XQFOM` *&ϾG\a1jwѬY3899ɷϞ= ʰqF5kwPƌ>}M6 R:. gggm۶۷/Ǝ wl6  ;v|f޽8{MAAaaaa0|`Xܹ3tKC#[Zi0b IDAToCv,(%T:o"͛7Ֆ8q"lv-v=zD*0aBW\zD/H$033 t+6{lp8zR-;94F@ŋ!J9r$C QXjlv\'l6 r o_O1:[r'h>|vvvhݺ5D" v>Xoݺ.^nF"ںJ3LjD",Y&&&pss޽{ Ǔ'O0j('ƽ{*ckk[VM2ФI}Ԯ][DDD(Bf7mw +++wjx{{ سg5k"һFWFQ-IC_wXm\~ǎCFFVX :qqqhܸ1 b|sXYYfffrO+۷o޼xBEDS0bQKnt4+ϰƞ`iHD777Vː<~]D")Ph|`'{֭ =jRc={TV.!p~Æ rTeDFE{]ĭ..XSN>^͛+]8x57o///t!88XYfOnÇ5jB!̐\J!gϞD"ݻwJ{PA;bРAZE2};wTIx<;66Vk7|L 6dzNhA[^,A"a{Vz%;11!!˨Qr* aQiD " az_h|`|7`X:K$٩<Җ .b)w mڴQ[W@Ӥ)+witz<Ռ3 HP\\,OYۮ];+gϞDM't  \G6 թnNNu2qUmx_;vf͚5kT|RRR`aaDaK'-l888Tp{///|^U'Oh7ݹsG:.\ "BD4sL@)))s4t ʪӧ),,L104uTڱceffR:u(//bJnJD9tP:qD2IC.$zuOj`()эDOӍ_I-Z+ЬY3>|8}: ۷Ӂ(44$ }'AG&777A"* SSS3f дiӨZhAݻw0y&SPPƶb1@:t'NdrX^^Ncǎ8jٲ%={V011!?? 2\.͘1;&n۶Mކ;YXXٙΞ=K[Ǐ;v|hٲe4yd4huܙӸ\k7xN"!wJ3=(Ϗ_uOOOZ~=7ݷ*JKKAcs=zҹsH$aa RTGtG,6{lXXX̏z`XrΝ;k9r$ ҿ X KKK̚5Kׯ - u͛l&Mpi 4BiTS{1GL$v"XJUYl C t7LMM ݗ.]믿=zJuMj,]|>9 Mnn."##ur#ܹZn]aݺuTmiis<>(pe:u,ڍ7`cc#ԩΜ9D#::Z_xx8vڥ%K OHH@XX_x^zB\rǏH{7n 8;;Rh\|Oݻa̞=#GD||<6m "i@:{}oVQzg"X~?jvqD]D"tIII5yJ%хD$|r[$''ťQ|4@>@0bCR.-ZD"ANбcGuu ֆׯpA 4jTjڴi;w,^AOŋ1uTt#$`ܸqذanccKKK4nXI,~PPŋ*䤗:u Diׯ_czԱcGl6 sss_&o?:w1ܹ̐spppmڴ'Hc*eRw)kց`kk @bccu:]N6 "ܹ;vDNԖC۶m :7 H$tVVVpqqQ:Yspy(-kff& ҖdѣG:ӖƍCr*C"O>xDlll#Cº5kuJp\XTFqq1,YBuvpiG,&&&/UT>66J0vXy*[qWףp!̟?߿?\_777|jӛD"ai j~iNw)Ϛw.Hj*ǏWZf„ ffgX޸qCzj (*(%GRdAVĴ٢z͛Uƍ055E~*56l ֞:w(*鷺w^U>ƀ|x{{۷*kuA?ަ}ԦM;v,b riSN~7cGiիW3=~bccXŢ4_ҥKiرu=]Zp!-YvܩS}ahԴiSD{ ~<G.]{ڀuX\eADj)--˗5rssGiiijqoTTTD_+-#hԴiS]JKKIsrrJD$?z(~.]J;wRQQlْVXA І (;;FMɡ/iƌ_1ӧԶm[9s&͘1233ֶҟG.'O~QHHM0,X@͛7w*eŋD͛7cn唘HsΥE… &GGGE|||̜(G_6?+Q?DӉq>*|)mj׮Mk֬^?UTe ȥKАL`@X\.WU;1|]6:ufQFz)kjSUk׮A(q*W(q5#44Teŋ<}EEE:ՖiӦݺuF:e^zr6uzo+[[[cҤI5jhkH__*+W+-Zcǰ|rw6w+++cxURQIʂ3ԩǏC" 33S:QFعs'b11c ԨQC뿿)Fĉprr=:dRV-x= SSS4o\?PV-XXX`˖-*D",\|>uѣGѶm[p\Sd'$$ ""B:۴V݋W@g ?d r ?!lS+L8Q)#==DӅ߿?LLLtNϭ^z!::h, | k{E۶m5/ٷo4k_իp*/l6[KƏo~pj*ʗ SeW_} G`F1֭[AD(,,DAAѰaC2> jaWnnnqb "<J+**H=zBC۶mADիR_{}y^.k4G>T:yȌ*N˭[???amm(ի4D"z ˖-So`r stU0B(UVxukӧj(^wL&M !!A:;v1Tn< 6d jW֧0A9/]WիWCvWSE\\\ Ԙ "ylԘ"SLA?$>`}RjuE3vjjj*X,vrdiii~ϟ?O"´*?qDv)M@M4CѩSё?hZrV&99|||h:ӕS5[ZZRFF]zFs[w^p8t9>}nnnh"z͜9K/_7ұcnj^ř3g?sss X."":DG͛WNyfڿ?֭[ r @D56MԱcJ+RQU5FFKū :rP-(77$ B:r8qO?uO?DIIItmڴiVib1gÍ>DΝ}̤;Rtt4޽LLL*\M6?@+-@@^"SSS@&M˗/4wNubccəCsT!qhN2I7M'i%zCcu:Mbv")++FUT˗ao$ 8 +$$rssր_Fl6[+… `ؐݻwΞ= ǀ>}V{KR ah֭4`:rm޼nݺ-ھ}\P[8@s\WWdL.00/_NW~Ahǎb諯R9x+++?~<ݽ{x<x񂢢(,,~Gz&kk*yxJDƍӁĉԢE +0@ׯ_JHH:PNNc[l޽[5E[n8ۯ c@~eI'G/^P˖-HߴiSڷo9sj׮M#___ڸq#ٓ.]D kܹ7nLѴ~AAATPP@WY.]PvhǎjzE/^$WWW sQxx8X,t9sD"5jԈ̙3?~$Т2.f=Es6Hs|><==+eիW/_Nk׮t۩K.'P(zg"3W|He)F"&&&XpQҲeKXXX ""BVbqq1HϰS+'OkV K[PP.ERm*b1Tc`ccJD#n:QXXu=@Kb޽hٲ% :]ڴictǏcȑRHL aff+V'vZ{ai&e(Ο?tl!!!3ȷ~:j֬ ;;;*-st ˗/Gqq1$ ?kED wU՚ IDATێ;^OKKF=tJ\^^3ffqƸ}68333Tp%,--ń b7nmի ";vLȐH$  {{ڹUis ?6xjXdH$ի0 0cDVWܠAРAI&U0J6m4~oFK˖-iii =z"2bm۶=<==[j%߿|>_DFziKlڴ DT\W8Knݺ ~2by x )qϝ;x$%%F1rss1fBXZZbڴi(((k0 6lݻw :D HYRUǺu*=*ۻs΅{+}5㩍ӹz*bPfM,\? $5k'HTtӦM`Xӧ48u<==!bжm[ 'OB!.]Ұ{^cz \=jeJ_y˓` -ЬyǏotӅFFPZZs|:푒Rڵkf~G}hƢu=j2l0ԫWOm4Aȫ#::zBΝ' T^rssU;oxbۇϟaÆ^5k֔|R,7o uVZ\\ WWWt֭RYWd rU 11Qv% """*ifc +={ $*ٳg?~>>x5kU[Δ999;vL~ djѕDl4h@5++  Ǔdgg?@ qppܹsm۶ ׯ00`@wYf͚%gj4~_|* edYUǏasAҌW%k⛠IfFb([0P gϞ×_~s]___ɓ' *͹s@Dߍacc^,T5 ŋC(N8FKTT㑕. :'[UOw,`ΝѣH'Bp8`X,D":uR—=KJJ_&LKq޼yp8znUTԬYS5َ08| `ڵ*뫰gϞ)]Mׇ|L4 fff055ŤI*2S_,*}vpcҤI "Y`X,0;&-WTF{c-fݫ8DH$ݻu=;; 1`ѢE Jwлwo HOOT9 m1ydYfӧ޾C&ˀ2bbbA`bb7n@"o߾055Uߪ|{xx40233aaa/QFL>Jdϟ?zqq1뫷;-[@DpqqQiD"w[${ƍ\ӦM%B!ƌS-GuysχΝ ˜?^}vcU16l/_-;pmB `Æ mb„ rϑ#GJKK7GkCbgϞ8wqgeeɍ!nEeBj <ONH$W_}PdeemoϞ=pttlق  ))IɞOD3gϿ|k֬Aܲ8k# 1u ʯ NNNg?scѢEǻ$&&L,QBՉƍWèr> (%%%`FN_a4m} 7zݺu`R[2b1,--1k,e @DؼyB٣G|CX_X[[+(gggVxU%2#u[n~ޓ޽{ЬY3;eee "|z_ـKk0ZXX3g #G4J!yBww'qqqhժU5Zۓķe:vQ2d6l(KPF 4l駟`aaooo\pAm[HMMx<"hԨֆM68rZ$mL>RǏ qO7wߩ7?? "p8<+W@ cǎ*=yyy8p.\TL>˗/#GjZl]SN 99YKÆ g)qF.ZtڵJ7> 0*ӷܞ48&MTjD BHHb1,XKKK%W濞kkkt .]@ ޾}666J_<Иq爉Ɗ+/^z`"H`ee9s-'seSRR"W0`BY kK̙3666x1b ͱc  UNFGw8q4UlCk1cU Td .\@5`oo"'|Sꂂ̞=p8oDw=°sNbH$L0DT4nBZ䤵˗/gHnw}7,,,TVFQQ||| PV-B,ťJw ???`FWr)6$ʐyxʜ9s`aaaELTTU̬J )_?._2e җle*"puuQTTڵkA Y^x:u|io޼YXr5 Dɓ .\az#Fi… w W^0%%%r;뎎off{/$!P$!z . tK(JPD@"$`^BRvo6fwg79vg޼μO.$ Fagu_~J{-wUw+@fn:a4m8pVk_^^"##AA@@ gϞ!''i}O<]U9թS BBvf'N u1W? VN8B'ާO)0̄D"L&ۂG֭X,J˗ZFvf0RPPBn*XD}Fyy9T*yEK7UR1x`|W^hҤM摘sۭ[7x{{cСhР >|8ԩy@4> aЫW/T*-2Ak\QGFF}pttD:u,"O =zx9d)''+?yL69B,]2d`" ɓ'Z( NNNظqV* ˗/9w] wd l?*ׯ~ WWWcӦMpvvF||AZc(**իu% >:ߛ1chFV,>OQQ9 BƍQVVӧOC&TNYfއqLLV;DOƍ#::vvvպXڴiIm6_5!fprrbRGXg}O/^ 4-}.黖֭ '''w^CTbŊ\,={Ϡ,XP 3D=дiSj 4McZm:N5jdV*ʰi& yeb޼ypww(J=`a,\ Rj 4Hc ܾ}HMMH0}K[Iՠٳg[e/~GBp꞊*0*x֒Ӆ 4 ,wU#\JU_|co߾1n³g`gggc޼yH$:;v6%:3 Dn޼ 777m֬Ǐ/o1ysPVV?֭Μ9 a۶m.` .׵ڵCF U\\ӧsѶ".NH$һ\-AСCBm>_6m*hT݋ƍ ZDݻwsٳIII&"H$6i[nqW=|v͗(曼=LAn̈́ۢj߃( Bv![圬J$2c/Zjqm3x3g΄+ú-qM.ҵlEEEMӜ 1^[;Z$yٶʛRԻ?k4j_pvvݻѱcGFǏ5k&xT1ĀiBP~}l۶M#stѤ@$22[liζ߈D"'v!HЮ];̙3 |7 6 l{!DV2}>}:D"9Bэb59C$~7Uc֦N:R/^ 5CƍjgGFF5kb4Mc۷/RapmۆiӦA,ĉx~3g5jeݽ{͛7D"GHݑlֱ3gMf;O6Mwc pyy96oތ0B3> B~~>BCC.5kgφ-ΆB@PPNOko߾ZY,VA<0Xf R)Eavk<ޱc`oo$AߧO")) * ?4BM6i,O VlXnMZ{<Bv܉p(Jlذg#::Rh3g Dy 4CRaΜ9g׮]! BYY5kܿ;w!6Baȑ/;}͗ }5Z~*={6ݹ kJUEH,~ϭ[֭[P*fIO?+3 Ν;I*A>6%RVV///3FK.A,JJJ0tPB0fgO???-//GVi⼤!{!霫jl۶3СA.ˇ*;kú-pԭ[WRyy90h 㗗C*bժUr Zl Bh`+d7CŠ(w<}=z!oFkOZZ^1/G:=zpttıc4_ѩS'.ذa9"""зo_P(,X"EEEؾ};&N͓wox8&4ƍO> !ݻl4 Les曠ix*Tj5 -[fsy&\]]ѱcGhhRRo7or\2*m/"^ 5͂]- P^=s7VbܹH$&Θ1FVVb bXaiȑD8pI竊qC+`ҥ M ]"mڴ,!|G D1BrrYߩS' 0Ġj1jl߾ lyY7aڸaP IDATApLLU]Cz8 a{qRTB]Wނ bTAR;waaa~6mЭ[7?z(T*ZhaBGff&(V^=zpO>bbw booI&tև?B#""B… NEEE 4:%%^^^puuŭ[pqNEXbM:.:uJ,Β錩yڵk[-~bƏ?1c 믿_8::K0 v؁HBb2ygϞ튺Pa:=┍WmDMt֭[k^!|&Ty 7xLzzU7nB@ J6lbݑ#GRlVݻoß>}is0ѹsgܹsK./@D*";;WRaWE䈉;.AvlD."G'-ܠA3=QLgϞC =]c Y$(e bDGGJ ֯_oܹsg$$$u)3g\\\l^-))A@@(2i gϞ]-Eu!22C iaԩ6Cxx^^{éL"cIIIYf;v0ر#\\\Jppp@zzeCT# ;tEałhJŋ-GV#==}K޳gѩS' 6KfC쥥>2ǎ!V4kL/44Tg5"''RԨ.^:<Z'h߾=}sss!&(( m=,6m*!O?;;;nݚWrMlSrZV-H$899a̙-j;v@||<@PV5GLj *{Bnv1B@nnӧO1dR"o͛A1hV[`( ]v|ѿpB8884޽{m" ++&>0/^ /0h uV?44ToO-[@>}Ea...!ǒ%K@4޽{ AzsfquBG!,,LвZƛo Bn ɨܿhҤ B*kR!|Y888m۶A(0طoWhڴBC"`͚5foMzaa!G׵\01 :uh`(AP ''G𖴴4=t!:Y_|{{{6_KLLDVV ÇCTM6dׯ_Gpp0n'***kH777L8/ƍBJ)Sh,~CjR /3:ÎVƢݎPrtx Æ !<3>~-|||舏?ؤ{Ǐͮ6rli̘1nnn n@٫W/ˋ/Xm۶ :֭X,t U{hѢ'](_Ԯ]'O^nn.D"Λl0s;zh4`=z"HJJJмysUŲl冢(^FJb˖-HHH0ܩ hժ c/żIOX2J7{b <j=4iB~3pqqqZ9Bhzqq]@@_XcǏAH1`ر( qqq8{YԅdՋ׾/Y3TV$?BT"%%EgreVZf>xM6B]йsg{Ý;w0uTT*( 7N>t)t$ _B$ >m;yK}7pssÇѧOB8 _m%%%D6s & <<#GL&S~M!00,̚V"JMƫ%+tR( WBZ0e-\...:`ooCUG=燦M̾3 B*rJʕ+AAffqIM6 ̬˖-8pI?gϞ!44 (--ƍ91o;rHBKJJ8wm]փ $$"///[?v""" 1c ^bиqc 8׾B޸q5D"իMbbb0l0ϯB@ZZFŋAxxىȏ7u_nnnU}Peuߠpȑ#y }+))X,ƺuL>k׮hѢs x9$ ֮][S:^ /X^zUMpp0MS"88XK,H$REχ;ڶm XzMoŋAlɓ'3gZQчG!$$qqq3TqѣU!]6v)G\t # | ޾ͨTqnn.hUoС4 .QF( F^tw&q<[c|U8prM6 v駟䄨(k֭[ ĸJ޳gϐOOO4>}h=z ÃD$ ~$1t62Gj'K0"A~/NВ4MhGRq<{,""" ˱vZgayf899LJ҇VZY=86m&W^HNNz}G'b^.3EСCQ~Ꞇ*y _Q*oZ@L>]{UofYmn8rHB*{9svvvx-62!CŅˢ1a,]4MCfҞ8uRL?}WǏ|ׯ0ӧ(--5#0 ggg̙3j*Z|ƍVR̫:66V?˗C.)//ˡT*`T3`G śo߾d4 Z|[.ƍgưzjBhvϗ_~ L-[\1 L4 0.**իVVqF[(O 9{ UR+**¨Q֭[#899[n=ea }_j ,MhٲaC~QPP}A&cǎa0c B0zh={jE"?ʰi&ΰ0--3 ӧ"t 92r.O&AJ$XE0+q:3ӹgv8=z»iӦ-Z.\BaVm>ݻ7MyQZZ \nuƫ%AZT*>>>xuW~}Ʃ[.3gP]g=zB0_ ,~e5s]Ç HdzO.]ЦMӧG PTTgϢs p 0ҥ \\\ǹs4AX"s},0ؽ{7*zbBT*qa^cѢyeC& }6vU}988T+,,4ؗ~}kL>]p!ޛ% ˑo!JѩS'= YYY w1x`;nuжm۸;99 #ѓ0 %A  &",&C|n$[28Gaj*d2ԯ_ϟ7j5VX\PwٲeJMU͍ yܰ&[l1ih=CMw3EDbb"z]Ӱ*^ / `ggW-ٳug=2>sn!־}{4e誶vۅA0 idj5_VVqƁl SL1Ν;L醄`ƍ\{!߂)))w|#DJklekNMXh233_u7\Hg o ;w7푛7/ɌVE@@ى'sf vTݻR:󅅅Сb13yw j;wDLL DF?3`#y2%]!"4ff }H 45ϣ~dXj_FiL:ϟ??cbmaA޽RDz>}1jFꞆU*y 0  (쀗^R7L̝;$$$`x@l۶ ̛7OϠl>iӦYsB*"))ɨ0]o޽;(!YYYu߂b4w߁ǽi&CBB0vX5a0|pPO>{KXׅ͛ f,ɢk׮ѣG}qqqҥ ߌi3f`E|nSaÆ'VT*h}RXXMOTY֬U7^ /_=묌Y3fG1 L&âE8JIIYFBݺuTդc ZOOO̝;Wn H=zZڵ :JKKqudr@@V"ݻwѬY3QOtPq֬$\hAy޽􄻻;v똳g"** b)))P(faD6Mr`ƌ9s d2lr|ѠA9(5 .\!?CuOjxعs'0GR#>z|z3gh?3gi7YTrM^^<==ѤIH`ʕi`pÆ ー<4lJR/J[˗/_~iXn,,,D PNo!WCdd$70ʂD"-;+V@&Y} T|)SK&ېɓ"o跶gaa!&NѰaC>}O<1k֬D"S n֖ ,xM6i$7|D6m !!!f%222Bag/ЩS'D dd(1> Huo5akII f̘HB¬P%vZBcܻwÆ ӛoԨ/1c$ (//,YRS^ /f͚wwwb·1#~=z"fAQp{AAAK]<~4h.] TGd^PXXnݺ~[מm[lz*HoooZJBJB^cBxJ-HMM`cǎM53g!˗/78]**\ V{HD"櫯2i Je꯳3m.6l0x`͛B8˧gǏs/l.XA2&o|!5$ 0Gv6я"r8l'o0Yl'û`k֬\.GzpY^ǝ8q2 4M#''Gp{M {{{,]~JD5w՝V[HB, _$%%[n= U ##mڴAxx-z!:HJJ!^!| |V!6o,ؘPZZyA"ŋ>kJMMTծ`MGeVgqX,Fn̒dfM`nƮ7n`MDh$ `2 MH}8{ gb!>%5rN%"hBs<hEǠib?.\pJX|9͛o T $8PQm'b b^rNVqRa,l)Dɱc,&`ܸq iX AAA8q"ڷooU]r}З djfX]GGG\|III R>...H$iwy&r>X)))hѢu'ev ;;;DEE^ܹ3RRRxCB"999&:1k¬j-[>|Ƚ.GH\\ mB`tDGGDX7>>>y!o> 1KРArHR^Zagggv5.&L`mʕ( {FII vX=z ƏoAA6n܈-[r|d/.).!OMSLKK3_;w=rss)&%%Ge eee ?4ItrFCII RitzT>Sx5 gyf=uԱuWZVa֭6mD"Fll,5j ;o Vɚ;ܹs ;N<FuF T WWW;f9?sJ6,2//NNNZ%}!=BFi5C6o EQ\aTlTbl`]v>JBxx8Ν;h 23r^mC`+ӧO7oUK4qD_~ H={0 777Ed˖-Td{JҤkrD7e+!)zVr۷^^^pss3ȝcBAA BRRRg< (D7թS{f̞=[Px&^$k:^ 5х 8;PA_;Kl|20`#j{JQQD"hFfqYgϞEQ&<}  .Lǽ{ТE H$ذahѣ! X\iX|9!0ĶqUm9p$  dCӨ\eEn]={6T* 4^7DX7ÇG )8}4!p αG竤o:{]v޽Z1 z ԳJKKѯ_?BV| D"ˤg@JJv)SϬVZ={6^~ "ZibgΜ1䋻wSN `z+SN+۷o|}}7ZtX|9rM,焯>B  h9j(-?mWH SVVPT:WT*_RR8ŋpuuEvlYS¸8#** 6m&g{聈QZZe^u-qƍ\'''̙3G0qvOyJ1}􁃃F+Yq-Be%mMfqFPQF v-# ֻ.ʕ+C`m_@~'|WWWb刏T*ڵk5י3g˦k_a0ydbυEAA H/Dӧ#;7K7GGGdggȑ#Z>[NF |,%AB0ͅB@ݺuu<,7k=ƌ ^J(^zlK޾}x3vXĦM1c6?EY]QVVuApp0~*8p bccoA%\)T7oݹO2bǎC&Mkcxxx(?̄ VU)Oš8Kn:N2-߽{&MBf͚%n7vXoo ?~4o\kAŚVa.[lM6X?}4zHgggބu4ڵkN$*VwAAH4-[8FDa8/sZܹ8T*| зo_AHII Əov!Ю];l۶͠0 XEdYGه1 !6mM>'9Q80 ԩPsNxxx,57nb$?>M,>@/Y΍)sٳ) @j86lIŖTSRԙݞ4ij׮ m(;4ib1~ʐ Dt___|VѰaC4nجZii)0a+N8p ;;vvvPT1cHn|!x97n> ZA 52 55UѤ)`_/hTׯ_ƍ&YCS[{ V~2L:* k֬~O:eM %Yj :_\zk׆'N>mұ۶m/0 N H#FhUFϟc„ QYoܸq{.tzaaA={֬ӕ+Wz駟cYYJKCMD @j0t)Bc6\.ǪU^2dp=x{{uꫯ@>QFA$KKڵkdx뭷6o B9bF[XLS{aN XSNi^ZZ4888[oݢL7|X={ s$EFg| jrdK||h"j5?~̵6m/^?l7nԻOyy9 2 ԩS@hh)"7`jܼy-Bݺu :<==1~x&̓a~H!$X\"CMjjrXG"R\ (e bpssX,FVVM=`j7\]]9đ#G@Ta0 lgggxyyTшEVV s7'N2>3׎QsЬY3t]1_4:tHwimx[URf+*ڴi_ݦd:eu֭[SNpuuʼn'쬓̺Q.WiӦA. *..F`` :wl8Ǐ!w#̚5 D@ZZWfA4T~II 6l:uhA>}hŶΙo!JѥKƐL&Ha=22Æ \|=~'qa}GB*b޼y(--/OnO>Deee\ 9JB||49@Q|||L%@&[nسgATZFN@ɎIBT:p@M蘁9n޼Ϟ=CNN<==A4#$5ܻws2d>} ///^\/ڵkBxƍ 5u`Emog>Ly;*ڏu},e|6l$XHRQyZZA; -աC$a̘1صk!~xO>%K@$Y Tdj׮ 31 ̙3NNN?~<ZjAVc O>:ͅڵk!5^>}:h6?\.ȑ#(ȉw!d'رUd@Axx8z)뙙HNN|͚5 4Mu EEE:u*D""##]llE!]tX,6KyfbnVq! 0*ʬjeqIHRQQQZS(߿?pB߿_+̄'W+**իir\0 w} ڵ+&uJ]퐦:%L_v >իW kerrr`gggSr>HD"9cǎD"ŋtYCYRP= L1cp&PׯH$Tn \AƫbՐd!99PRR›fBgΟ?bb4kY?J%s募XWT*aYftaŊvi4l7f&ft?;B*֠pfy\t ӧOG@@EAG5>yΊ [nARi%"JJJa{SNZkŋW_~-[!Ftf~|97bΝ ޽{fqE|^u~ɒT0|ѻwoi _d @j0Zjn@} H%IYTTLWWWlݺhܿjBz*NJJ :v\j񈉉;aٙ-X/_6;n) h"B*bȑZ-I,rf~/'XJR|xzziӦHLL4R ==:S<D,AxϞ=Mjo8vT*ZjeUE#$$DpR.u\Bgvcǎnii):vBBBBp!+R<!jyEEEHKK\.7įV9mS+>|\4nجYYYxu`>KϔWWWImՍe@QN<^YY6me,EII &NByjZ kѣG믿=-]V_c޼y"Ѫ4 @j(AedjSAQ˨Q@HE{#t`-))A󽩰ThJ={ETEbb J|QXXC"`ذazK謤޽{5^Gbb"d2r˖-J… 5nJ1o߾ 777Ï!?૯2舤$)YvBP $;a]+ WB*BIW۶ǒ={-[BT@|9z x/YdggC*xa\ըĬ.̝;E *مzܹs/++CTTbccV˱m6󔜜Z;m۶pqq;xw%$&&iLJCHHN?0ʂT*.^B{OOO z ///˙k֬H$@j EEEX|9<==!mV!t `ʔ)(:-YZUuZسjj͛gwp,Yk`7}`;hu\D"ӧOA~(zU<~$7nΝ 777cك\ۛ}6w} 5k{{{O6vZjܹ qػw={6mpq{!Xyy9-ZTzq߇3'o-[Ng.ϟWb)SWxhzUS G3fd)χڷoB*J.YhKg>byyyP(fKYݻw!MhEqq1VZoooD" 8$ܺu70Xt)hF-vDw:[׵ۗ ;;JR'Qa <ؠGȕ+WtĉZ^pnnnjvELL :udBeU)!+Gڵ/0k,|0+U|MttUv[n HFh6۷ &x7nnn=z4N:b[Fs SyyypssCZZFiiNJFFCvv6y޽{vXܹSϟ3gR0YukҤY\'_|!eUbܹpvvֹPk޼9VAHE;ZF6mK _ 0m4:u>o Tj0hժ֭kTС5j$ϟ?Ǻu˙ߩS'o#֭k fGw᤯Q]6t>JKKѶm[888l`F[RR~<==i686,Zj}wT-,KU A(X#QQ-K{XbIĞ1hlKl1v [P;~3awgvf{|ι(3̝}]t1 ? :)S,ԬUM777lڴjo&1""8sI]PPPP;wm۶J,W -}.#Fi|ᇜX"#((H0i6ܯE Ԇ³g鉌 uhơCЬY3BPV-ܹS`h4eddf͚J~zA&1aaPTT3gr"5χUڊ~zy~ʔ)fϼ5O2339GmE[K.h޼9~e2FO?Œ%K@Q6o̲`>?HMM:խ[$ ۼ<prڹs'(} EQknoܸq<ﯿBժUfyxY ٰboos oA*b̙Dž4BڴicзRd 7nիWyHaÆfƱLUXcC{=c"::ڢ1w-Z0ӧOѫW/R\Em*N:B\]]Ml鉨(QzF4Mرcx[/^9sAXvIF BFo.VsԨQ7oEQ,-!Lނ9 GE֭AAjհejvnѢ~W8wd2-Z{=bwl̇3f 88vj5rJ4=z!24 ///W0̏,7lH%Exx8Ǝy`- 3gB&aܸqS@a=BPP8y| ;k4ԬYM4i2b^^A 7nDHH(B=LYx[T*ŪULggφ7o~`#""DyfϞ D'Np/^VVgΝÝ;wU\^^\\\0mڴ2 +WPիWQfM(J^C"66֢1O>h? (J,_^m?R ٳgFϽufϞ3f8ܹiӦ :t(]SMIׯU4Z-QZ5xN:;0À{QRɲz꼋f͚0ƳbTL0*=W\! /e~ IDATeev[۶mw+;cxxkTB-[pߵk)"BヺubP(ב Ϗ i$$$f͚Ԍt\DxxɋvZ͛7#,, 0\tj:x !Cj5ƍBJ*MϜ9FaJnZF||Gk*nA*mhm͚53)0k,xzzYY՗6Vv5@*!r>W&,D AHILvv6)ab999ٳ'ƻfggCPvP%{܇9`47N<)|Fm۶!22t1`XLOaggDNNNFqX iӦVVV0b -aYH%ի!y?Rg}f0Ǵ4T*dddilڴ fBi&wѯ_?xxx\X`rIB#F<>c:v숳gt\r,Ǐr4N*j,..SWn1J\ضm!:/*#6V"jl޾}M_]UV93f̀L&C5X BDDݻj- 94 4k,/ŋ1qDEq%:ur%4={BRg"ڑ!_FPPڵkgG͛0m۶qN'ӧO祧-@sTF Yff-: 994M#%%sEPbCc]0h {&Y8p ԩcOOO̝;צs(sb vU8BBB0n8#T*=xNN0j(0&L'ΎVŮ]v%&U{p=AT z0'OprRR,,RH4RSSn4X_#+Κ(ĉnZ1`7|WWW 1 :t@,//.\zꁢ(3!**}Ο?B0k׮H$HkNNVX8B#F&+Úk.2dIb$"\,\ 0lذ)ؾ}{Nڑ#G8 ,e;v,<== X7++䈂TTTR'N< TRs~~~&fW0d姟~*YH%DzixLZFBB;ŋ{vG888 $$PbxyyVZ:"&ĕ/˗hڴ) vލ={Cdܹ3kmOr )ɓf?EPP6lh8vaay& T`͚5!;;݃L&Uki[npuuŮ]X .@`żLÒ%KDO͚5XeرcGi,Yw̋-WNN {{{o3EEE7OJT45@*._ BzYk1=zÇR!mÿ~uԁ#D7c x{{[% ق/@ۭ[7"??… !QNBмys^Gd {1118z(<<@Qw׷Lb޽ qF%̖?WVŜ9s@QZliq"qj^:'NC@aUѣG볪))):9Eʕ+x56ouPTE֭AQ^EGGcرl:_d2?ׯbСCP*h޼9^~ _...ذah/BJvȘJ$TZL[c޽F111ׯFˏCh{nԮ][䄩Su;v7I)Eaܹӧ 2w|VU[t_ѳgO>|wԩ  Nǎ-E_xkT2$%% !)IM 2$1: $C9X~} 4HͫWZhÐ!CAYm4C!!!4hMظq#(2[PFcMڵk~6lhfܹmΙ3G%K СC( 6ќ/8pM1:'N@`` <==f@XXL_֭[:Z \n7CPuϟ?Gff&)I2fPCCCD ^f ܽ{$R\xEَtzz:<==mJWjo>ԫWO!T*1n8t$;==]DL8Q~;3f}/hss\>5???_K[ixyyiXxUvѹsg( vİd2 8"##1av7o ƚB" Lh>M^ߗ/_|"UVرcV~~>222~Mjhݺ56Z_ѠYf ċ/VѥK12s"L¼F˖- L+/^*F.uYhA}|7C gϞaը_>)`\F B Vií@4ބBBtutLC8rM+=(gigPb=6}vuNcǎ!<<\p ۷g !ooov4h4hтs|r~ȑu*ϚP՜ÖY7 >!9ccc2nݺ@dd$j׮z F5k0x{{:t(->ll&ѣoڵE,=F۶m{|/^l`2rNN8"2w\\\н{w2 |ع4H7nl}0]vEZ*Di >HMMe1SuÃejXc^la۷o$}ٳ2 ]ta篢"|ܹ3r9d2:t={:)#Fhٲ=vvvMӘ?> & 00LիA'ʤ?&LoAͷ8p av-4jH޽{6yÇ;wIG8T09yX>78[l<޽f_CEqSi砷H% 6q[nhѢN8rPTvmU۷ԱǪUۮ[J^~ ___Zjh޼_'N )) Ԯ]_`LluEZ5jM[AMJ=voe&MټD+ȢT*ƅ'O@. 1(OL<]x~6GRq:u ݻwL-]$j>T*EϞ=Q\\SNaĈ!+V$ d1cY9N ~a}mS4BѠ^zٻ̇Ǐ2=qT޽{hYj̞=[0<44:x(a,?>\\\8pׁf+Wx/[2o J ^(1:u*- D灐j5Zn*N*((![n5v~~>d2֮]k;;;ܹs=L4~w$''5j`Ϟ=EEEpww׉w/O|ᇰI˗;uW^n>mkԩA`3 ++sΙ|btR *5hۚM.EuFśU4,Xeĉ9ϟ?GBBKXp=deeA.#** ǎ=)o04!틍R)>c[ OEQX|y gΜA5D?̳8qFٳ 3:{a1CUV Jep^֭-CE97eH%BZZ xi0zrrrx9R;w!1?!DpcDGߦMY:x"PJ_^9F`P* ҙ+hfwK gtCBwMf5YsKbbbУG\z} *]ڴiÊl_qvv6J }(,,T*G}d}w'!QQQ:&L}W#>>EEE:'O\.y0hS"e˖V5DE JBnQzunkY!w6 vvv6m(#G111,ή]@-67W^.IeAAj ]@@4MXK.m711]vf+K̙3*uݸq4:7-Z`РAPT}6뤧s:Qvɻ/] &S )7|;yL~ٵkUQЬY3k;>o JFϔp%V Bar {{{$^z...HNNf ^ }~BBB0n8f )))R '/!(ue3M6Y=qqƌpqq OC.]x_^^^F?tC gmرc1{lB8EEESsJ(d2*˗/`2W-PPPC 7oѣIaBlIXRX/aoo-Z,ޘ{ŀil޼nnn^iǎL&yٓDhft@ELbK4͛#<<\˗/bw]1&۶mZܹswST™#6X ѣGym֭7nx6o ?Q]bk ߎOYQ[[o JuA&o+W@PL~~avS!vZHr窿y&`oo?soooq`OS\\o$!p%3sE rǶmB\#t .Ut'O(..ƍ9oUzQ!6Dq IDATWB.[TT/Ç1~xի{ɓ3g4p4mT)Ea„ b{UTTjRW4k̪c(`v`KeykT <j2^N,aÆAP Gaa!дiSxzzU޾}0.c _.4x iQըY&^zA" 00}P^=ؼ!1իWY;v45o9SBBBx"22=6ftނ `B駟C{aaaFwU?sAg%Ay9Q4Mٳ3f CTb޼y&-)IXgvF9l޼~~~prrºu*L]i0Bp>>>ap#c 4Mcʔ) Ϟ=nnnڧM;;;efԩSKڵkc˖-۷sfkР`hG^֭m2ݺu7ץVZGV 999U͚8qbyd5@* 4h^z\oܸqu}؏+nݺ1`qn2/áCؿ-[ *h_3f̰ZB"Rz |A+зo_HRc͚5eb r`]r%m6Qk:u2V好eP(8R4da%%:uMx 3:s򲬁bv sx'Od</^4bҥY&)7nΟ?yMOO79a۶m\$~' μ| !%5REi Ht ! MZZѶhƤI@a5nZhQFwi!$$IIIewY"nŋʂg7** #F䄭[RBZfA]*b̙f7 < /i\k{h&R hT*9ckvZd2<U^زe g=Z4ᅬ@%%%YEhӦo߾*U +WK,Ǐb QD6mйsg{͝;"/Tcx<==MPh۶-)6l\ (y!ɰl2Q 7oހ⭭3g΀g=/??}!H`oo4|8`z׃> aoo ,0\pu1'm4͊fgg-\wY4P{b…BJ=K߿b~u^:%665^ly/ ;!- tqvvt;>o J6@%V-Z 2 cǎPBAQQ<ʵlԨQQ\\\LV‡~u޽z=<< J]-599:2Y9hժ ͹sl#׽{wq[l1ZX^P"C hp|_~8ןry8///Rd3LF hƯ}Ǽ;71ዳ4a}ݺuJf_s^^&LDXA˖u\]]ѱcGNONIilBg]y#%%~~~VM &dJ7o ;;~~~]g=88 1-M2htR^A0,^.]bQPP !&T&5_=4`h̡tu1ݻxbPooo… PTܹQY9r[߾}ѨQ#:L4lu} :rXt)޼yI&]TJ+/**:q˖-C6X!:ɸ8k׮]H$ӧҰT*~=B*UPV-!Tl%YYY( Zd0BRR],Kp!BtunܸYf!!!9s&_nbR~~~3foݺLDd2yd2Ө]6R)&O|4CXXAG` 4hffݻwT*1z2a4WBY+ҥKA6 /vAKÆ m6W_}y4_*2 SL_FOcԩSd`?~`RM2`vg~Wo)))FC6o ,w>}:8<ȑ#aggwww,\P<77>>>HOOh ͛7pttN% Y[E}aF?p>>>8q T޽{Cʕ+5j%xwtBxJ}Z˗/-[E\иqc5$Pٳg-9bDGG[yt%@HIŪ#G +(**Bjj*r9KtA0,E*U0n8Aqq1͛;;;DFF8rlAAPkb)D6mذW[j%K@"NVE@@e.\BL#ii_>_\\,μVY8X[踢޽{+thxkTiڵ3~dd$ƫW Bwߡ^,J{Ě5kft9tP40m4?zcƌB+͛ǻ$ZK8 QQQЪU+1r5mϧiV2ꭳØ1c ˑczF>y$XaPqu> ;;;AP߹s{a ;xyyYӸzjr筨jV[Ǝ;(((@v`gg&O0aaau~8;;#..?f1GaN:ؾ}E WC yH%$l+"wZϟѣGRyo(..F͚5ѠA21b6lZƶmL~vJ¦M8?wti޼UX.` reM,_R!ݦR `)D6m8H R DI&i N6#Tzk4nݺb1`xzz(O<̞=[pE"..+-&̙3? >5kִIۉӧ}ɓ'CT 3g- c+!j`#aÆ|~:~\Znm{pR%oL MV#>>-\p{e˗HII!&Mڇ 2Ǐ#;;]<==1j(Vׯ_Yfptt!{/^+W(ddd B{.+أGlMݻO-0f炡Rs 0`h׮LժU ?_2'qo췾q9yHi!J~c{̙3 +gնi#.]jR޽ڵT*t}J \$$$ ǎ38B^ɓAH`[0j( łil۶ k04M#%%..."h"3w@iRU(,777wMAfo;qD4Mct0uT0CcavacLZ w[lڴ&o ŋCRYZFÆ Y?SNAP ==ɁILxRw#;;ۢİ{ORɓb+wBP`ԩc.0kYO"[ճhЧO:K{aY_r%g=ca+jڵJ)INNF||E4McݺuCݺumƨ1x`VAXv-`ժU7i,/^;WWWvϟb6/}<-[4iG֟@g7f2^:muݻwAQb~6̦%={4xm̙3 $bڴiK.q<}#==[\..c5k4aoE@@:rKk*$2l0"j1x`PUn!jG5ʄ=1cY:_S?ΆS'ljj*͊[ LXTd\;SHG'z!(L4 * M6ſXL0ٳ $F-.p2rիW7o\]]YokM\K.Vo[ ͛RC\ InLLE?xw]Po߾1]TTĩlNH 1q]ԡ(J0ɘY&LJeˈ#>3ͅٳgKoOdmGGGy-Ǐf͚Խ{w8::,W>̘16a=77C!5•+WD{!|}}ѬY3<:B {1VŠA@QlbجsA"`e_>}Lfh48{,6l.:We˖nlΘ9K.x9""" ۾; ҧOr6pYbQʩ+5@*0!H''' RRRR& ?!s2x !صk`_… ;;; 6 AAAl^/nP$!0%$T[V-h\///0LTZj~#&MT#kfΜ9niΫj߿?$IPs~ѩS'UPPJsT~f#)) \O>ݻ9uː!C,)iqU609D\R~zH$t|(1k,V>xty?Bdƌ\aٲeL&q]%Innnx֭D"1;,[ J|QQQ&% @DEE]<4ŋW(l)Sx ](,,(ҥ +tq 6l(ʜZ ueE8u=zѼ[n!$$D8J_!O(?~B=>|Ŋ Jqar 0*~ 6 mNJJBtVPP+WR 0XhL8>}*ٳgXbE `mHx޽DDDw}'''iӦ7<.t4; ;OcPekZ`޼yV^ H$dffrr߹s5kU+<ɞ{kSNW^X[)S:Ghhɡ=W\dBر#]///ぁ^r~ҥKaooϟi 2S#1n8Bפ{"4Mǰa\z?ԹZBJJUd|2|}}|B233u?~Ud2]V~ ?V((( ]lHHM&xǏ Q6\`4}|jԨ[m%'' 1~1FJJ IDAT4Uto|!|>5jL˗$޽{X`ɓqeG(gϞBuϟ'j׮-2jY S۶mѾ}{yذa'*a]բQF $_端h4@ ?F޽{C*"-% MJ7:u2I/ɿ/+W4:ueԩSRD?ؽ{AN%MXvQ#J1c yyyF+T)^ZYxb4x+k5@*(_BţXâE:cǎ.ZM*U'(( j={EaݺuV $$1:;; o EVEPP`ӧOQfMJ26ZQF#33P ar???6 ?<=='NXt?klٲ.(RD^pA5k@.[U!55Ux̙3pssCݺu%ٳ'(ի9;v,"##6^xؘ$a[|9ڷoBvj &pGY}61]PJ/,j ""͚5i!6לKժUe4xYYY&"UVŶm hoܸD111&u֮]h{AAAVuĖ':yk֬) RAxIH~ ˑ#GB*LG!yæV駟1iiiV}$%%Zje"7(k $$LKwE׮]f@@gŋSaIR7&_"T4MR%ᜱƏ=BHHjԨach0sLPdN/sjq!ӇMl֬6md"`vؤ'OWxwagggtqGA*Z q T nnnشiEsʎ;8ǰw^6o xMFAzz:R)cq{qْ&˗&iV0$KӦMJ*>}fZݻC&Y5ϟ?7oggg({c߾}&=~~~fb̙32h=B+V:jΜ9#\nZE@zzI$2Պ2o @/\Ǐ+ZnnYΝ;V'avv (3b'OZubРApss+Әɓ'ͪ#Bػw/!V;0`CӦM\&7oХK3f~ ^9! ƨQ}vԮ][Ng}BJh'4ib6rQQ݋TrHRo_|ՙP֭XUB" 99Yv~'ДMpvvj[FF4ib`̈́W^aĈ( 40yaÆ}Ǚ]2[ljt 2vWXl6o@"֡裏8svrIy1114hߴZ- 充 ٳgQV-zM6+**p*6c,+dgg\E(Ȉ!hX? ڵkmi`  Ϟ=!$2zhƙ3g0rHV<66+VVFFռEQ Eaas{(J4ldG@ѯ_?K=z0w~qDEEA.ccCF*((6]^QFI=Dc7jb%(Q{%hT4v,`W0c0];̽J2s,l WN&]~fd/QYy;̅;wHKKm۶V\xQx{{}I_~#:iǍh 4666f͚{&==]jJ8::ꍇILLaaa"00PHRagg'v*ٓX͹qG RTthJ%O.ѷo_uԩfGHHh޼iiiN:Uk}6ȞaҥK?D 6Lʼnz ;;;1sL#m*Smn"=D֣27B&1cƘmak\waվA߾}YI2s @fwwwh"Gcj.̍ѸCv%<<<,"" #G&AvZRĪU1d/^QJ5!Xd%3,B!ϙ۝񞀼OJܶnZ$innnY J%vڥUhԨ8zV"##CH$,O>5 >\899gϞx~۷ ȽxA$4&MPgoo/̙#:u$ صk >}*4h`|!(#8p)D5Ç)Y%$$d!9"飽4h -[V/]$4z-J(ZDk>~ G,'<e.RSSE۶mؽ{U6XYYyz$MUM#Shٲe,*J,X GyC BStd… D;w4خGlNK*%;wİaò\S,_|)5j$FX5ZM]vV+qprrF{yy!2uGxɇ^I&ʞjҦMakkk5RD |` ;wl~:):hO6NC|Y WL&6m8( 1QT/4XgժU][^cNj+cUGf]L85;WT*ZzgϞe„ H$lllaڵH>>>ܾ}h#GШQ#5kRdϞ=8qf͚!HB JU[~j׮.36lԩS)Pѹm3`LӧO|X~}G||<ϟ?U?B޽{өS'+кH$ aÆsAڶmk͍7_FF2*}t////_nZtiϟOTTӦMdJ CIh]!h/W}oX666Ү];oA"dܹÏ?hվK.Mٴiɺ˗{ڵkfS%22J*fTXg2|pڃ<|0ϟ?;Gm?Ndd$ӧOVZz 4WZߩO)+A1) `~{ŋH$7w||MPPE"HJٳg]!=z4*TW^&.`( ƍc9;;Ӷm[|,P>nݺeqBF%KSU斗sR$,H-%RlRL&[c8|T*1rHٳgG# P(D ̒- * 6=z¬?#lllXzj Qk1QDFj2p8WK5A:C]j? >>4K=Ff1k"99YhBI>h7md~+T`oF:˛=֭[`eEJh$ 6L$%% !4hzFK̘1C8 xv%c.$Hr~(!BDIJhq|",fvywB.]D߹s}"HHJJbɄh 3f ׯ_ߟ?:J]bcc83gP\9&NիWsU֬Ycԟ]\.gܹ4oޜ7iBK.[3fE.XӦMcԩ̜9Cj-]'Ν;\"0Aq'a,T/hD"wԉcǎQzuWи\~??,e*!Cxb.\ȠA |r>Co#==w뛫+::;RjUfϞmվk֬Ɇ 1cc$$?Pש<+|t(D}xB j*4ibk{͛i׮[fΝ4mdF۶miժÆ #00,whsСCϟOZZQs,nݺEzz: 8,sb@s~ШQ#uƥK֗Jڵ?>vbѢE|=zT^_|Ŋdɒ=III\z'OT*TR,Y@[OJseP|#>2%':Q)&.SrM}{ˉ'8<ἌC"`gkC\\$$$dya IDATDzz:rN:1~{&NHrr2ODoی  /Ù7o^w pppƍj*W}YdddFjjjyg͛yz?LLL$-%7Aiq}u#1 C6pa,D5Iۇ[ 8.^ٺsd&!-[d4j(O(UǏgb̨T>>>l޼9ԁdr9r%$3lmm={6͛7W^իW=K"w+P J2PT&coXd !!!lڴ L[n5}&Oƍ)S W{7*ז+..N0pJRӧO1ȕZg? dIj^ Ne`OA>ٌaί[llٲfXҽ{w?~L`` F/ZO>@q C&!Hy޿դI?L^KDDBZjetNy)S0c Esh4|>sr9J|jj*4[@iŕ8vUR!C:Ia+l*0$2CP7wR8}(t^fݵa>-U.MIeʰaY3Ē@ߚGbCZZZ'l<ƹshҁ'ϣ"u.g92p<Ϧ߂T\.g׮])b\*ڵkL/hٲe4Wj2`j- gΜN.Д)S|Ǐg͚535k8ݻwgر#DZ?9Af)^cd׏C2sLk, )))]oxVT$''#HxUҐ<}T˗/dBߡT*KHl Op@Rl83OÏD#EM^@P {(]MS0`dt5آ`?x?qы4C|zO@'OY R G5pTnMGCְ=F߼y)Sn:J(/B^tH*{ ֙3gQN܍7_1cnnn] ӦMo߾ 4hޓחڵk/sx[>}Я_?͛w!lDR3֋Hx ~/P f,bt rv8Rrf:wLRޚ cr刉UV?~-[вeKu ӧ_#5UBKNNw_F[!|2\U7m՟+!kn oΫ^6mD-^]JJ !!!;w;wR~}z'|ž={|2& !HMM%99Y{$%%i?;uꄯ/=zRy6 DGG7ͺ>k%+H13B ݝ'r~8эDڤSejGfk-/uI׉4YQT|hPsbQiy$pM&H5T\bx'HYڌxŋYܾ}SFbXx1{6b3/0sK@2ri<Ϝ9CHHNcŐ!Cr5>}h" ɓ',G1/^t-Z۷o-ߴi={W^,^ AĐRQ7L;iDs$M!#Pq]Rի\544HFMɒ%/r՟%$@=~8;vYf2Sjj*.]cǎrppp󤥥qo?"&FܽG!68dGPeNeA:6;4="^kҤ  <888{nZjE6mػw^j0k,v7|òet/_LppH"gٓy$&&6QW45J<#R8.{B`omH4LBlll`ggǃ8t"R!;ePIs ߄<@FF¨>ۑ;򡁃_%nvU>/{vݻ|wYϟnR [[["""r=WWW={ƽ{t,'OdͬY&Uo2Ӹqc֭[G=dΝ;3|ph0$Ż}vtBΝY|QraDBb… !F^ZHi*! $6oիWx#G$""D d3%%EnggG޽ ³~.OUJ㠒EJ!:9 =jbs\]B#вeKطouֵ8L>һwo1R)<0=r2(xf]aՁ!C0d{a޼y|3+b+0,0[#N: ƽ3pB)SƲ !oX@^ &B j Og_#g)p7n&n/W5Ȧw"<>dɒ`tw'CnPM6k] ai ƆǏkKG>z -/<"\ AS(~kd/P- ?oOO """سgiBɯcsL6Г$JQA3 }2[l{be2u (Sn3+{6P# PotX̿%T'wÌ 8P/' ї̙3xxxh4nɓ'9p;!˚W)W?*УGvJDDe˖͓1|||زe]vn:&-- X:v|_.)^8ޔ*U$O&ꊫ+...899H&ˢӧDEE\ܿ|ND]XE1Vs<%>,) bM";666!HOO'==ݨиXcZPf*pIU.B:ڲ(}9w|Ymխ[GZkSԩ'NϟHTTjժ1]viXBƏϴi>|8g6ٰzjzMڢP(+7nooo>SL!֭;w1+gC^"11"<)E"5KOǻ@& 666FJJA"SB`lkcc?C\\[dɒZ<:>dV^͵k״VO3hѢTR_Rɘ1c ^?Ⱦ}vvvvoM/;(IDfq g*`K&$ovG+"{ x&%^[TGFxڛHq3H9P{ ?QFI0nLCWKTMWm - Vƪտ"m$d!8eK'[.$ *̫2VpjT*:ѣGپc-^&L/h֬wޥt,[۷oZ4w**gϞ,[3c ০Ȍ3ؾ}Ѻ]u,AϟK-prrGGGqqqVؐNӦM)[TTW^ӓERX1/I.Э[7UתgǸuN0焋d΄֖ŋdɒ,uN<oQ29BBBB#ɓ' 9BŊւ HKKC*ү_?V\ɏ?ȗ_~ioT*K˒Sqر\aD-bÆ V#[-ǿSUʔxHAŋ)Rգp¹EG#>> :āYU?dĈw` ( :t@Notb$ K޽{Y/[cǎaccc=ݻTV-_6mUbx "xd[L'ZK@dHk*P3;2H@l ) W>+СC9z޶jѓuu`z03AKgaҧxO@!gΞEU//4;Ba?H"!cNj\}||HOO'**RJ&))ŋ܍J[9T1)g`ߡ=-[fs?ٓ*UX4W!yXvk'O'''9|0/^\g.IKK#>>xbccy%111DGG3F/N%(Z(?vWr˖-,Y2G 7n3d4h`v̛)oqϢxe t~Ze/ݩ4iԪsy!}!,,*Ç4mڔ=ߛ,u~~~dddp&L}Xv 7+VI[OBܹs1֭[RXjU\rb!:_[[[ sdZ&gGn~.í31/ @ M@DDJҾ( -O>ͤ)x)ޡkGJ qI{O#G"44`쟹4nhk!CRX~6hN?% X|}}Q*TPA'ƃX?(Y$TXQ'8[ŋ_رcH[+a~bРA|E:wdT9᫯⫯Җ͙3K'+##=zZ$`P~}֮]Khh(Jbڴi9#3߿OӦMQ*;vL'HcߵkW>|Ȯ] 4|W#55+WiU sy\\7o&<<,`jAcv!v)QU ]pwhrXK$GΝ;ѣG1b& 6׳bŊ\)ƙgggKXSk׮\Aq:t`ժU\z tw˗/c툎.\`gϞS*WlѼϜCl#lJɨ&/Ƞr S0[v.Yx+8;n-UId8.`%6N*2`FI42İ19l8W&((Ȣ~rĊs~+n,SRC6 '3i+Wh֬ԩSbwsdxE7F ~}+ Y0I ?H2t9::Fztvoݺŗ_~/b;uĬY5j%Kx2""M"9vXdR)>ȑ#95X@Ҭo.Z<"BOOO˗| h]%666|ܸqݻwSL QZ5lJvm8:a*A=8IF-{8@ҥ)]4AAA 2h<ӧV~-| ٓZHHխҷD"aѢET\3fX?36m2H@ڵkF HrrQRQDTRgz0s?'$#7j`G-쩌-I|O4HJi$ eo߾1'Y_V-6lO?"9|/uFcEsf1BfJ 8݊عs'[ɏـpp:6~&!5H t̊(Qϧrt`5kְvZ*OAjZ2!o~ 9|jw=zܺu ggg|RjxQ]ӂ ꕑMMMwl߾]nܸAFL2ܼyQFƌéR gΜȭm#"">>؉҉)@ylg"#n_$<+$j!׮e׮]5uN2'{۶mK !:t(K,an+Wh޼9YWWWkט1cIII+W6m (`6Qq>B5;;; F׮]رɶ &L/58q"}?ÇԹ#5eP2V* \-';Zj:ny*VȊ+YG`` nСCzl)Ə̙3|IwΕ+WtNى'_~|3{lc̘1$$$SJrVP>Fwi`!Eh&VD1Oջ&ɠxK>GBb罳ث^^^̚5Ν;l !Xz5# %)Ϊ|F05)=YMA$OI˷s]y-ZtR31<Ļe &GF LT˖22-[Xl*H>4HoYv-geZ- xAC,f\ "E 55hDѢEJ*E9JPhIM6B4J(a!`֬Y_*D- ""B+ڧO@mmˉDer1?Eiʌ 2|dv PD*ҶU LӦMH$q%|^;v,{:cg\|OOO *D2eh۶-mڴ춙ɯLv\r+W Y &>p׮]~0DYB?S@ďݻS2uw|4mڔk]glSTg+>$?;kr*s~Gj׮7f,_\\B:u>|8SL3bG`` :tUaU%*;v, bYCBBXno'K1 1k%h{U^O~M>S$Ƒ8nxP(DHzb|0j(Ǝkw.Hݻ7[fʕ,Y5#Ȩ +M e!dH&sYzX29!CX2p55zJf  ~~ Je-7Y}Mo=TJǗ2g+T̴#L-}W? 1rO>4qF;3]!e~B={fz,m<<<ٓ?E >t RRZKtl _Qܹ C6 1bʲeڵ+_|wlPT̙3-[lٲ<#q߿f͚3|p&NH"E8r {{{.\H˖-ҥK ޞM6_g)sssS=d|;!H(\ЍJyd^0B|B JpE|vL 䣿\Gcu̝;_ ⫯/… ?OT*)PmQk6ꊗxO@ 8:u)?UM> k~ >xMR{h8LC$3X*dMoW&ʌ~"+N9[Pcq:mcӦMYԽm$&&%sDvkDʕiժUEűWk+WXFi9w֍1cưeziQl'9" Eu^k"Va*83lllIIIQ|yn 5jٳ:m322ڵvaQg裏HHH`߾}UNΝ;G`` ˗g߾}z-ϟ޽;5j[y.+oC ĉұcGYf0M6iӦژs1"cJS{ jԨA&M,z75mڔ˗/3qD̙c^{Vm֬Yyޣ!!ժU[z1M4ȑ#Zopp0!!!1-ZTZh SbQ֭[_ᇜ?xak1BEEElҼᤢQ#u,Gj4DmVǺVʰePz|}}?>-Z0{!xO@TJ͚59ql;nQ#p+> 69-lٲ$%%-=ξAmx|n3ꛯ8Ȕ=;ڼ~R~|# <~ب"&ͶD"v(Qի(Geg̙t҅`QFYD@?~LӦMQT9r///ͮڤ %xRSST,' !0Ͼ<gΜQFTV-;yfnݺ &X#C#}۷Og)P{YT*ƌ̙3=z4=;& oC ѣG[ -! #F <<'OZZ.ˋݻhޅk]Aaʕ޽Eblٲ,=z%!]ży(_<&L`9nߡCwNdd$Yʗ/ϑ#G2{OII1H@"""HIIy'- ֪ҿT mll p%N'/xL'#"|-HeP$K (`UhwV-%ё3|<YxX{P$p} 1_]Ȉ&L-[EX`XJFC =O<!|H1n81?NY4i l۶MN  T*,s)R1I؋84Ek ؏x22Jxr@IxgH @a?1 NA,3{ xϴwC[iӦ}RCO$$$ ;t^e -$"DdiBX]t6::;g,*L|qLvz%z‹S]V w\v4C1c LNNƓ'ODw.>|joffwwwQz2v\p*UܹsѩS'}N< WWWA\\]#jԨa #G"44Tc#lY yZ:ɓ'g?AAA9r$&Om۶]vosNqqqhժ95kh髫T*"1QF6mjP7odݻ7Ke,ȝ;7֭[g*ʕ+5L}\~~~~hժVZYټ$nݺݻԩSz988`֬YҥI:JXBRRR :uoa&M[;C~:W^-K{ zҼ-YQQQFcq=!,, GwP68$ޣ"QPPGt"7>`ѢE^zƅ`cmmÆѣJ,qpa=,^ɛ9y LSz/]M; _-mHsX-g/^\<|Ϟ=ӚD;99i^<<~?;v04j+V̴—ȴipuٳKÆ O?a|݅S" K[j3gbȐ!x&Z1cth4noQNrŋQ1vS`#2D7Ƴ[ll,Zl OOO,ZH>w7׸͛7֭6l؀KhѢ (O9;vmcol2˨-[DnQXOXqIȧTz-9:({;"XH" ŀYО~wйsg,_\|K$|M[čHx0u^ls7|Sza*t/\p@S -P =]cE_`aa j/*Uggg8::rI~xx8.]d`@d?wܢ璓/v۷^A;vyܹ`*5hРq1сKGyQzޏBCC燦MէSСCyVر#\ݻAa֭XvVZA("" 6D||Xf5UjU ̳җa+W̋.]ɓ'0*~O?ʄhbD,[ +fCMLLDDD֮> ByQS⾓ĨQ0sL*U*@@@Ƀ׋šqǑ#Gj X ڃc'Oܹ~~~ر#m\kkk_UT c͚5nw:$111SF!w8u HBB?~c+ħ.4Cy?֭[3-IBB a߾}X~=/_ KHH!Ci&,_|o޼ɸP:5hG GfىwER#L* k`cPIIH`h{,ܙ44z/k`64OOOlٲE755r3/1 @Û77͕(Dlgeoݭ[78::,|C9Nn Kb믿LADPP֯_'ܗʕ+8x`Fb{,H; ;v,~W@1i$ >\ڵkѸqci֭[3g {.j֬H.]{_gpO H@֬YcnC6l Q<3IA0`^gF¨Q2-~PmvddF1&>3M=zk׮Ye[l~˗HJJdgv@_qqq8zFΝ;YʕùzȓPEG5:6LBܼyXjj*kժ%888ѣGF|rd2v҅7]]]kPϟcO?du iT/_n*x,PkԨdStUvE777Zyԩ,kVTI"E\)JVVLJJ2 >>>433ӼOSNu8{,:.]J\1ZTܹs4ik֬Iss +CRŊ£G211$yI:g~3K; o+g0ro҄+R&=.l~ TVP&qRl˗l߾̵l.1Ṹ11d*͜ {iX.]DMt*Wj(\WNGRnݺ,^Q>J% *=zh%%%I&Ç +!!2L߫W/صk0**ʨPTl׮mllx%2w܂o:uT* …>}PT2..+Vԇ bP޽,ߧO+VÆ 3(w0,,̨r;vKMMe:u/_>Iyzp*ؿ{.mzJ;TH>|Ш:\BB)SyZSTrΝڲeN=Ǐ'޾}[\~XdICͣGrJmۖNNN&8 (Ν;sÆ v#~'pg*J<cLrիRٳg+Щ%~W N OuE:f5 ^Zlĉ jCYjX $t!ttc9O;ga~gϞ}%_+|k & edPcKZ*-XԳ(˸z9WR.s&sԨQ̝;7-Z‚7.^zW) IDATF||<}}}ϟkoܸQt05kVΞ=8h T*gϞp¢u!\n]zxxp550:<{k.sF\.ѣGMKp1+V,lB2]T+_hQw*Lkkkԩ`gΜݻwM0A Y& {||4d*UYٯ.!!!ܺuF%*;ț7/ǏXI/;3M(~w&M0, |فmbŅ֯@i/ի3fիF}h>gzܹs]1͛ٶm[sݙN:lݺLC˗5jСlaa+W֧V y :d2Uԭ[WAכ/_>.]}5Ǐ {:{nԩSMEL{9 "InN2=޾|*T(!$66nnn 0.ǰaØJKKKsڵөgǎ3T*ɓGUT|2Mƺu$BGr8l0޾}:uҒ&L8X[[kT3ӧ`YCTF8}t#iiѢQ;z$IZ+tpp`ӦMυ/)o޼ܴiS~/_|u{yyקܹs| ihѢTT,Sι*Ϊ3Y̗/Iy288k֬hg޼ye˖im۶۷oE˅dɒ۷=H;vH/^}~X*3{0w3mV/߿8x`.](YjU;Ofjjjvޞܹs)%66^^^ *!3f0nݺePyMB?˗/'cǎjT*9uɟ>}3wgԼ\\\x=ѾΟ?_3N+++Λ7Ϙ[cRBBBX`AܿxAi@___zzzY$99SNHԼxʕXbj=BT*~wP5M6]tAAA `JJ}׮]aÆ>c֭iӦҥK^ 3$6wi[WZ?CT>7oNo4KdI7o^cGLƗM/xIӗ4d"C._ZҨkɓ[+25X4YdI֪U+K+/^.ҭ[LէH UZ~=H@2Aiffcǎi1BtP J sѩ,\0xUsӦM;yQ돧Oj21T*e2,YcVZEFٟu9BѣHLn߾o6 =,Yb8&&e˖x߻w9r|]iњ]Zu,Z d{yuΞ=ŋ7J_exp=V\9sAg||<njCKKKqƍҮGD@$4WYʧ$*鄏zA RHII?/ӬpȐ!&nԫMb=3BRqjf˵k3h~ڵN7Y%99uԡ3/^Z*s7>} VX#R”(J.Zrp\fM޹sǨcbbUԩD !Ǐoܸ!ݻwuf:}J}ٴݻw relHrttd6mrJ M +W.zzzܹsWTܲe =<%433ɓ*7~xi;UiӦꓻN] 40ʕ+ ~\vvvvZ N(T( HO\\ k)#+S\99۶m3n-qUiʗ/o=șT*nܸ...N}}}){ƆQ(y{{%KpȐ!әܫwӧc ]j=z4iӦ`xׯwڕ#.]%J֖Vp0` +}eSO%HU*D ?E J w@i6`qϞ=ׯ'4.\ȑmzu[oR3XgդP4"ETvٲeYBDn2eФΎTT4i4j7**JoBBƮl5j&ŋZY'&&N CBB ޡ~xJPQT^ oiy_ ǂjժiy!r͖-[rɒ%:v$J‚˗gxxJ˗/ 8m40I IVXݻw=AQ&Auc}tP }4w ܕDDDg``u ;tkjzܹsyAƎu\pѢEF{:r>؋/\.7M ]&Lcϭ^ƞC !ܺuy_&q횼bϛ`jC3LHkp~)J ǂ  ꌊh]K{c,P-Hr06J ˢE211qF-bd/^``` p?sf788`dI YV ݿ#FsG*@0h ;T"W^x19RG/CBBxOjh"ٳgE,\8rH ;wD'"iffftT*jՊ<{,hff&:1'N 9VVV<}44O.]Dyڵ2tSbŊk׮eS%> nзӧ)SFP9|hw=Ēڍ4IvЁ5kjQFlܸqoG$%% r9k֬UG1#/^  [iNET8q"kԨY,^8Ͻ{f{>ak֬L&c߾}oͯjT?\"Xܹs3,;o}%Rx-Ν; p6m2*KDD-,,8vXn۶ ;v4{Wƍc^ (SG3!fΜI{{{ڒ˗X"ɣvB#F`}ժUc.]4o477do޼2eҒ-(T*OM󐐐kL!!@&3LJJqsssSu+Gceec֬Y~:>|K̓?\\\+Wɓ'FQjU$''GE۶m '00vvv:7n܈TPX1cFRAP܎6Ϟ=CZǏ\rs˖-CXXS֭N=111֭Hf?ؽ{7)bp۷o OJJ}jJ,pc hժˇ+WB&J%HW'>˗#wܢзo_ԬYSpssѣG)X>** 3g"##{jٷoohٲAS}kkk͹k׮TRt'˗bŊHMMŹsЫW/qׯVZlٲq&O,8I<$DB$d [nؼy3bbbp)>D :Dbb= XXX۷E||sn {{{M^Id֭n޼f͚{I+W'O֌رc&Ow!͛7Xf rTz-wC- НpU)SzL۷oѶm[8N޼yA\r޽{{nJ"i7`$rC5HFw?gȋ/Ν;3ҢEp޼y ^>y^}3gԮZe$}С}S۷orǏg nG"H",T䔔I&i˗/ƆmڴR`OּuI2CUѣG W6ԞcrI5ktUL&Re˗/wՕ˗8b>}l2.\X\Jki(T B{?H ?L2 OIӧYpaΝ7o9T*b ͛:uj%r."aT(~ a'SMrTT*\ӧnݺ#… gϞ:u*XvmQ~~~MRSS5χ`=˗/ azˍ;6iܽ{2~=6mJB+WS 'OfLL ]]]3___¦M2nZlN RslْfffFBR%^^| tQ_>_U+T\o޼!I]V0BիW9i$1l0d|4#""W?R)SPPjժ6V}HCƫFF̟?_&y18ٳH1bNڵk Ȑ!Y*;?rGAmHnbXD ˗;cƌL&]~щǨQL3fx{{ 3gmmmM֏̐@+++>;;;3!!A̟I|U6V"';ٳ͚5y///=zTh\ *U͛7TTիW͛7 @4iB̙3I2; ?{գL&cHHhTTtB Ck@@$4ͦ9ܼy...xΝ[^w)C9J%8 rݠ~7:+̈#)Zx"X|y-՞k׮͍ 68]ll,K.M///>yނMB4*Y%55 Lƍ7NHvQ^=׆ $%>/m P@رCS۷o"xɉCͰ]\ 4b d֭YV~/4;|jE1OQ@@$H'NdPP---5ƥAAAz믂6I?8ܹL2Z1Cp شiS.Z,?es1-SRRD 6,C!kٳtpp`ʕl?~sf#""ܹs+:u2uk0p@!!!,R,Xė~<>>>v*UQFرcS%%%qܸqeiv?#"" !iѢE7n*y IDATiLp$ӂr׻wRJ477Yl{dɒݐ'O2W\zgOZZZN:V9|0  ӧN@6mdÇNv>vsʳ!ryV"M´̘1,ĹK.ܸqc׮_N?? N~zAwww׌5?"##YP!+V̠U/8qo޼mسgO1֭3fRJeo޼ab 8K6|-=zU… )ضm,T;Xr%kժ%:HPNvd2nJ۷/˕+gv3Krr̺u$Y'**֭cN4vn8{l޸qdTΝ;666  4x]7|&MsqqɰHs~y?cw~ kvQ *D 9Rb! $>Pl'O"E5ѣTB-={JpΝ;zc}ŋeƋBߟeʔ1ھhmm3>>d5j(iJT*v֍ܾ}QݺuM6g@ VVVׯp`kk˹sj\ABPjԨѣ\r%e2.^ŋ֖Wq֭Klܸ1ܹcv%r>җTktiuFwwwdSO?XdIzxx;ioo/[-_\\BBm&8rppɓ'zy` -ӧO/_ިٳg---ٸqcD'99Ycc3c $&&ߟ\`Dd֬Y&iANz5j̠3gp.fh"*lDfQTq̙/_>vؑk׮5j*ɓ'jժܷo`<'u*R>>,PV#{!o޼ کS>>,YF=((Hoւy:[,$?~?3+W4hlΌezw,42J̛7/ǎk9z( (@ggg޽dZ*UK$HL a [~InD޼yJ*ɉ׮]r}v])ӷ('OjzK.AZ\onkYz5r9;uQs>/μyz\25^zf 4(Km:tȨOͳ-Jbpp0mmmy _~EoBUgϞM;;l鯄0*w  Ķm?,'_0dL|%pєd]6ǨUe#G3CA.Li޼9ÙAdթP(8qD&$$pΜ9Rs H^Ôqypp0ϲefS/?/YfMk=7ׯ_o iٲ+Ba58i$@N>Fmݯ_?=9|0sŊ+P*lҤ E(P@+(1T*A]&M 13˹shaa}SO;uT:::~ԾJ:ucǎej4muݻwСCM*d쿿mAC&+k֭tppOJbK.-T*#ZXXעmMڶm˦MjT!\\\ءCVVժU(םә5kpȐ!PA۷o… J7mժUq۷oQxb$eIC/^`BXJAX> +&M<}$߿ŋElժ.]j/sαlٲY>d ,w z S;z Qn&-WX!x={Yr7|Ë/fݒܐ bn:/\ Z]vtuuPUk#11Q/^r\phY(o޼,HJJc *hWP!!!999իWen޼)X&#רBu{,]4;t@ggg6m(T*njC0aɴ y(IzL&!+WLFǏ/""18ĞYS$0ae2Y^j׮ӗ ;v,LֿT={'Nd5hffF -E^m6~Fnݺ%Pa 7\Hjm\,Y8RdZ[[TR;w. .Lsss1V0="޽{ɓ'iӦ :udcOs.)))lݺ5-,,o߾li399Yt%[oŊ[)VN~1ZbÆ IMϟ?|mW+sϞ=&T*1m4i;B>Cf={F0zW.'ƦM}򵅅Q&eJLY#ԗѣ}-<|+V`PPF.W\EvT*`U#|>` }8h_Aoc ިQ#)D ɂF]v-Ӻuk͛W3IMMehffƝ;wfk o޼)XfΜ9eL"Nǎu[XXJ4oޜy۶mcϞ=YP!M=uyf_RqyLS/iР͹qOCqT*Nf&NhT/_gٚ<΂¬:e)vÇ̆ $.Z"Ed__:qqqܻw/ qr 1c< ~u4 (-,ٸqcSCB"3!!p:#""D0MGYGRG ƍ7hM49T*1x`2m۶\ Xv-H 9Ǐ P(ɝ;77oŋaaacǢlٲpwwG׮]i&zJOJB>}0o<,^??ڵkؿ?ڶm)L&ŋ <ƍùs Xbd:o߾;99͛7GӦMСC(J֭[֭|SdILT*\t ӦMCݺuƍc۶m^:6oތ/^̙30a曯~0ػw/֮] '''*5`t9Ӂ:t&L7 C -&ͯRz$UVa͚5hժ'ǬYc߾}:]]]Q~}7oĕ+W(VqgDR4d(Q }իW CvpYm...Z*ƎӧO#555z?6Jݻwǒ%KrJ^:T`p`w(3g O0¹ T ZM0߿/xk@HbĈ?>/^.]|.a̘1pqq9~,\PxfUo6l "|퀘bnݺa͈S0`<|=z@!CHLLrHNNF۶myf} C͚5QHTeT\+WqXx1cP]B; QQQxFVO]\\o>]UgȐ!?3l֭[޽;ڷo޽{O5 Bpwk!ׯchذ!ѠA_*T  .`ʔ)Up<\áxO~Y5V<|FfF]~3Xtj -`_ رc1c ̟?={f[1i$sǏGLL1;;;4o\'gpQy( =J ,c033Cj0n89s111 E*UqF4hhԨ͛۷o£$j wƶmЪU+[7F͚5qa~r"6CеkW<* OFF*-RGd9A*N;sHH|$Hh! !&w@>fr&M¯3f;Ztʕ9[3F)԰TL'''i+Wē'OpUL0>|8J,"EW^ؾ};bcc3VBB5kbΝhҤ fΜ;cǎرclmmMxuӧFbbbЭ[ :1pA~zA!)) s9ѣ=z[f7s\\>}K@pQbŊț7/:tK.C ëWp <>>>ZUΝ;xNJE,7z) k˱"p A^ c --*8Јy" JU̜9Gĉҋ. ͛'xnٲezֱz!o^])[nu PW>>d2ʔ)CСC@9r-Z[L4 .\hҤ N8{A2d +VbB]v!_|݋%K5-[ĬY˾|:pBbʕ!b $I&prrB:ub x{{cx)\3f~ݖ0`O HSJN< @Jr "##kI :%4ϟaÆaH͚5ѲeK* Z633xQ8ڵK縐wqU_瀠 =̽sf#V3˲Ԓ2V4IT EA9s|߿υ +-2gLo bҤI1n8*W:?͡EnݢiӦvZj֬I.]믙Ξ=;[lٳ9p#퍫kǎckl&S e˖q={8p >>>t!kJJrR>MȵkXx1{`,Y!CG} ϏnݺM{pL'a{P(y$SLM6SV-&OLB6mgϞѣL8f͚=s$|7SNū[aQQQ,Z(޲#Ln! W\?ܽ{ӦMω'ϩT)ٞψիW~zǎˎ;ӧ !Ν}q_ZДz@n޼y)RELJׯ3l0|2 ,gϞ˗/*S|y\;vo_Nx?k`dϞ*@Q2a=З,YBn޽;'O~I;n8>s.]"grss#G {z@&Cn:\]]quu%G7776l 2 IDAT LTݻ}vzB׮]f5)6ErjGxxxo٬2uT/^qZϜ97 ٳgF⥗^ݝvڱqF5kƊ+z*[n?J*OߜEnsimM>u*f Gr+VUVٜ3ҵkWvܙc%ZbFo̙3OtЁ9sRF &L@\ӡK9koWk 7PbN'WߣԩS4ZkO$?I@&K,VBX @7Ҷm[^~eΝLYQ]t'nQ. 36$<<<ސԩSGGGlٳ'zbɒ%6k"d̘FK`` Νcڴiϟo_|:t9e͚+WZv>}ĻL2Q`۝;w˖m۶ټ]fFȣJ*oj 0f̘d-[dԫWKrUvȑ#Q3$O'=zg9ǿfÐCu?JP"VaZȶmhٲ% 4`…u7wł /"Չ'99x:u9sf6mO:u#Zhڵ Ν;G޽ɗ/eʔaРA_>^&hѢ,\0޽{^0k= Ϗ)ScukD?nrfΜIΝrʌ;lٲ1qĸ:5SN]v+)_5'ɫ,Qw!#UUwMs-?HDttteRݻi޼95kdҥ4ț7/Æ nСqnzJTOCH`` uݝk{??VGGGjԨ'|ž={ eTZ иqcxo8vXިQ#Ucmܸo&gk= 7oHv?7ޠw|Wm궱5B~J.GGGvl*weڵ 82eʐ7o^zɓ'y뭷غu+׮]cŊۗbŊ=u .$ɞ=;3z 5^NÎf_pd95] @*k@""" !}}ѴiS*VȊ+yxbϟ?3~xZhaub %'ozߩ[.y_~[nY˗ӻwT?;*3f>|QFСC)Y$ O>,_}1cg8#WaVQFPy"Vݻ >j~!vUV5cѢEih̙ӨRqƍn]X,㥗^ܹai&;uwJ*%X_\7FaKۍ,Y5k4k˗ȟ?qѴnZ 7ZnmakƜ9s4mŋ}:gzwO7qF|`$8ĉ Xtv?~ș36uiƌVo-[(UQ|*T0 flܸѸw#OI 4*V~004a=ax`͛.\Hːgʕ+V߄ o؇˗QKSرcF\ *׮]K dwsN#::ț7ou3f4n޼iat)zbay1FWЦM ^z#GlٲӺi6ݿزe1|p^~زeq}+ pvvN4ɖ#E0 QqmիWY|9gմh7776l9|0afFE͚5PرciڴiC 9a$n -2j`kٸqcaVם:u-[?w}GVpwwnݺ+V3grtXADiDlaΝ;4 .]~Mȓ'OZ7) 0jرcy3 'Y0=?>* |g3I&=YfM6L: ;qrrbȑ/_y2vظ}nܸ%Kx7ST( Pu  S)YeQ/V[K___ڵkguݾ}x 8۷orBBB7oos続 +((g7 bSrA@._L gӦMVka<_Zdɒ`]z5k]ts΄3sLfΜßo&ŋӿ~W]ر#.\ߑ#G0aB\f͚%Z(ɓ|6iBHz̚5ҥK[]N͹z*7oߧbŊ ekb!<<\H*P"6Ydի4lؐׯi& *MJWZnM,`„ ԫW/Cqðbѣ]v%(( 6r$=2fHÆ iݺuYv- Y(G(^^^&2BQضiӦ%mPPSN]v͛#Gؼ֬Y?lPwTDlJN1^ ƍ4nܘ`6nhsd2_[}`[l*Uܹs#'NHj6i$Cٷo76m_{[ңSN)S{~ǸB+VdM" pi=l yN(ljw?a_`U :eޗ[nb KѢE)R#44w^=JΜ9^ǐ!CXhT""d)V$ = nݢiӦ>} 6PTnRUlYcuossεKFHH= ߟ7xٵkժUk;ҳ=z 4heʔIq9x m̲?`(5$f"s :6ԃ{C80AuP{ 8öpvyO3zhjժmڴaݺu4nܘ_~W}v>#VJYjD]veV.\jq} ""@d2>`> Ch޼97ׯI5jٳgO?z9CyN8aѣG3dv/'vK`` ΝctЁ9r$8&G(Wm 6m A\ ^ DU'&Ue1AH B߾}?~<|w8q'N0eZnMlz̛7ꜟZn`׾ ""@$`ݻjՊ?uQbŴnS!gΜ9V;r'N$t01b}:ubѢET\[,ț7/={dѢE{nFI2?gL3d-wkv /0gK/U+Wl2z-sֶm[k_NfeemVdddgvyTe-~:׮]_r=ڶmK@@UVM&=UK%,uա,[nzǏea :?6mڰpBڵkǯJ֬Y蹟UT^O>ݻw… ٳ'sN)Bq"nT…_!"8ܿ%'<[%:q:Կ{=bkܹs'r[߿vH  2a$""۳m6VZE͚5Iɐ!&L."""۷o[ Lb{Lda7`qsscǎq ?~<%Jd1I<)@}', KSDp{G߇4 7Bְ _e弎1###Sv?L5_j0 9u$|ܴi5k$K,+UcrKDywjITRCHtt4ݺuc,[ƍY[&L1Y޺u+IDGGӳgO~'ʕ+G@@~~~ 4(%amcDOϡ(Cb` &xՃggmR0vwkȔ)+W6eZjE^ooƌC]\œ bܡ87S,T"F|D2%Jv2L$o ŝ? Rr͚6cnH$Vkl6[ͳ$ә={6;v9r`яuw +W$K,dʔ]v٬.O +W~G?FBȜ tCvS.ކ#G0a7n͚5oرcV{TZ5͛A QWXgC#o&~~~):d$*-=oݺE}cM}Ta0h &NȴiիbŊhKҮ]jݻwi۶-7o&C *U5kiMZkAX_.ok*&t7TtC)0![)(_ ߩ (@&Mhڴ)׷Z޽{Η+7AA=v= f]>l3L$iҤI09C[.4q~رc~)_8ƭ[hڴ)v"""Ν;j*鈋 >S!zMr\sf̙ <2eʤ8ر#FPJhݺ5_=# &1^ $ ̈dw@@0/IRbcøqO7n 9%o޸~: 6dܿaÆ1{47# Ԭ"=zǽ[!'kG2љ '/B#sL6<H ,hAɓa'Nd| 6쉟O+\0#Fr ? ""q=с$l~8oi?KϞ=Yp!޽O>ի?RIv\y06p79Cf~ěFdf$11K6ǡC.I H<I 'Of >>S?$W\ό35k'QQQInB:u믿0 &*T7c/:;e1,pllyLZYhIAիWgȑ޽P.\HϞ=ɓ'O:5ms,@x{DDo+lܸ1DD7 @$Y òiӦѯ_?OP,Y;v Lgذ7=zPlKFwFIDATYӯ_?9bu/K/qq2dڵkyWȓPlYK1duwXVwX2~psscǎL>s/ 6L0 Obܧ$NSF9b)Ht;獲`IL:52Ʉ3Yf%$4iΜ9tޝ}*H, UVd:{> ˁ݂8L#fS&ٳgU/^$GSB_<(&LO>&2*!+nb["Ƒ⃧g9pyo4`ƴYԭ[7unʺu3g77')J~%嗈$'HXQApŽ`ěHL2ILQ?V{ED5zd)\+вeK̙`fxe TH3\~) *Td]n&% aXXxgq<8R-(^R+"Q"Bܹ,Odʹnݚ&M0|Kg߿O7z`]}z6wpכє4ʕ+Dݦ҈̼M0Ӹf\Ÿ;|'$q‘p*Ujmyh$[ѢEL6Odǎhт:uxUܖ'ϏcAFp8;c >{@Մ3e'ޚ͛7S~Ti-%Jp|pMteC.1WF4%qby!0DcЬYj3AY$zŌ3,/UIٻw/5rʬY˓g*VX\m{ëAvaZKȐ >:_ք'q|iZ,͒ŋS"6?yKL&j MXӣG3ͩc>8}<㋈<HJś4jԈ%Jfe?Jv؁ w;=^H^Io[hyD9s˯|lN;(u«;ҨQ nDDq @$lܻwdѣ4lؐB vZfMz,\L G~X@IonUYyC:uy!D1b%eɓS""MBdLeؐd|ٲTQN:QfMLV?4hoooɞ=/ AXX!!!}7o#TG?!TKFɄ]%fRŚ;sܔ9ǻE4.r)W @$I;|1̘ HiqL$"#9q>ɓ)[C?N.] ׯ6l-n5ݻ̜d9'k]$ThT> ;#^z9^F [5j<<M|g32򃑋FDFY 6rIӵkWϝˏӦMqqqaÆ xxxJ/ǏgT(X?=v^k$Cf} :wy0ZӚ,dH$10M8nQFmϚipP"V]~^& 7d|"hD#\idٰ%K5 lڴ ooo;\+{Gg|o%Wv.Tʔ)Sh߾=#Gx;: ʙJd4N`& "9@8#8u" 2uYS+EDDu4 N$ 5\x9]dي͛7[|6SpĉT>g%WR~Ǩ\y7Q$ԯ_vqaf̘;Y|0#n2(YkT^QF rp!:7СC/_> Ie;w{䄗)%""yȂ ܹ3CR/=E")p;gyvbpdM ?9rwwdΟ?O|l6ix;GL&ܹC.ܩOmwç99L?w/DDD٠D℅Q0_>4XdNMq8aܸq#^`._@c,Y>?:TDD|saʬD8 W,[0}VI㋈HDۜ2 'd/'̘N&F`K;r[D#§xv 8ëqmVkZ]f&/OA2>w7x̉'hʻw&Fxxx}3dȐd/E2WWfИ1cGwAjw?̟?N:qEDD$]R">}B ! DiPǂM,z:pgrӅlqb!8|ͻHm>!!!}v}=!{MP(bM8~~ۓ7!=)gpڀ6͛x"<W8p@Θ)b~<|7{Vv\0S%|QQQ 깸~zcL&<<<B Qzus&8::z ^|6&&٪وìvT~gR!""P"nkžyTT ̢Eطo6qYDFFəERqeU{:(ͮsNPL 2)WocuiYFS0GRcL^tˋu3DDD)@|m -c9ğ|q108`OʕS""""tPPtirfy*_q\)i+{_:Br{|M." v8PX[֭ktF%] ۸DSDPv rtż_ """R"qN:E"Ec3,4)u{T4I۷o_DDDD? n")A5sDCqvNcCN2e|[)71`o@*UJcGY$L2pNf4|[D? F1aD""""9 @ p}eJ+DtQ\aܸq!"""" @ĺ_|]{L10}ba)p Ya 6 XDDDD"/dcN+B%2Qg0a`pHp7,o׎o// I'Hܺu ???# bh2-ˋ]ҧO)VtJ<;wsRJʕ+'""""MBQ""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDDF؍ """""b7 @DDDDDn(Q""""""vDDDDDD8p>IENDB`ipyparallel-7.1.0/docs/source/reference/figs/frontend-kernel.png000066400000000000000000004537511412533144600250000ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxw`U-ҀFDAZyDPA tT H$${uvhy̞=Ν{=\^GBBBBa"fշ[ QhPHDBrT%$$$$$$$$$$$$*!!!!!!!!!!!!ѠU JHHHHHHHHHHHH4($GUBBBBBBBBBBBBA!9 QhP([ pOO"@&oU$$18WTcHQ]\vРVEBjj*flQQ}"!!!!qB%$$$ˋV=zԷ8ڶO}!!!!!qU E>w"Hi%gU%]ŏEEVFLLOGշ0?_ZoZ<~ݺ՗7>|y uFVn2Ujku~Fqr(ulh8$$NڦRYL@PSu:tMu;9Mzt\D&$꟱Q={kq=M[ ǏLz[oW֭%'UCYFPȭ8Vf_=!ȈRט3{61:%GK p΢E<4t(sgY|OQ5j-]`U\/*ʍrm/r5wqߠ k I5'ʙ3t?O=sX6kF~̮ tRlkIIdԤٵB}/kp,-q#G̚MXr%/f) 9l[p!?nΈ>}e߾},;?Q,\}˩*+ pqqa'K݆ikr$$'nu^çqc" {^HK#'#ñaz= Ç#W((8^hJLĻQ#z=?TE- /kMoFW m Xs]FL<;Զm&xr/PQZ$z&MSf۵[2'5#\uٮd޽ ݛQO> @uU LlrSEemUlٵ{taʌt׏ت=f!6l(-(`ҥ֍&Y2;INnxAAN ^ Hz<9tt78ܼy}y&[qdXvoP*ZIhd2JJEYWOOSrr<)yqn!I=(˫˔JS~]Q*u:m>!!~L&C]Y߲MUQQkSm(}}ѪTd2Teex9P1ވ𾙨(sTk)q1=O?MwFƳV~'^'۷硧:{3璓ٵmI))뮻v ȭ,>|Mz%&2)*GgK/Q(Ҿ=qJEmKxxؔ#!P0z5 U gW(O/6'GϞG1VkS*qvq{$'`>=r%T(NJmiƍL:B3. !뤪ҕ+{. QtL5 &ee!็!oXL:OьXɺe˸͇}bj }Yx1/M 23y'mP(vb97Sڵk㪊 2.]"n\~a!^^f}(Q&LP.!PȾt[NlCqq1A$TSEIӧ9o23 c7ibq]]]ۨM۬CWΞe-2H`Wy?p9+͚-1:X*loknZsСDǛa6VεkIMKC Cz۹oGvT*{ Drrrؿs''O$ {vضA:璒d<.Y}No޽̘7]2|߸[ՙ3ͮ5{K]]͢ q_}=2Q+<';u:-\Hի$ZMk`iEK} &5kfs@`}W7u*Wsrx`-U*ti҄Q+WؖD>} `~CЪukâ%Kh(O4òMw͝KX&MHff&_.[e`@hY]3jP kZdC69S 䁗Z_`rJl9`joq̡}˖<3yM2BR#Yv5 g5p ;ubm.^Yp0۵_ 7_~ɢ~eL }yf Q_Rt&eel޵1#G򫯒tKVxqt` ШٷweQZYlQQUZ-ZjJKJLmwƽݺ &:&]۶QXRjqq1f=<<o(( #̙CNNObq^VKv̚Vef$?mۘGk+X}S^yHZFEqa;ƴٳ-)RȮYp!{Ϟ6mj.gtj*oMf-Ɛڨ$/^yjn;r6l RGro$Uor쇬ٶi ~=u놓;֬G^z:\njȪUYbbXq#'ys\CNVz#ΜoϞOI>mE5 eU[5?CvkwB||xu \==`PZ`ފ#77OYt4/^^l/4i6t,2Ǟ{QQVClΑ;9z! yw*rD:uӇ}./.fSO1[7Fbe΢!g ?|&ˊKYp]fט7+-;g1'RRU\ w @CdtNޱop0yuvG(P*mzTӦоwoN9áG"Z-j#z)SuMC|`NcL&cW_9GF6@d J%:vd}qM+BۄxyyQU]mR*42RrT%Dv$W_~IŋZFae y ѣZ8>ѽ{6aٵpM_͡dۋӵWI3+Յc򊋙駄n^cʄ l_Er‚^k;K/C/zًZi޻a{;]{2'q2 t6lTWf:2/+ 9}Ǟ|+-ٸZ,~`Or2%(.뉷2zuT\>}'Ǎ#m["r+9E=xP틇ﴕ3#;zɱ(.bVѭZs۰\r2֮W^aV,WҲICBؿe "@cLjl҄}w*99 7!F #{9GΟOai)3̱EΈx{yq#?bu**kؖKm kߞCIIh,vv \̤Z='˜E8jPu> SOxU Vߝgo6͂`'FL;Q_jIٳߐ U~$uUyً]P֡Cȸqoz@<4lo}rssNDSԴ)5<sMϏVwGTTgϟ4qCxz fs[f羥;#'N4 fF<_XAzz:3O٫sg8('^}ծq{kU*qvu5pwfAfarܴXNVFssu8r#O˶ur/\@֭iӫPӭ[7n؀N1V4߷? y]m*LSw=fvWmv ʘ8jj57JJӱ#o͘AhN␔bBWu~JU*ڷhoa1b6H~?lΔ'yVͶ"gĸq\t'Nu˖OMe: q,LJ+W"S!K/W^NvnB9EEШx`h(~f$$"j-T2={xFg{ur9AA|㏦gs4 %ͷB+LRsi'8kܹ{觟Ν$_QUGRI||K!$?NdX.A>LJ^{{GfajԊș3{Vh /^$F h:>Wٷcaa4*zeuuUϧ=w[MY:h| K c£{39b',U|zzy1H=yWWWw|f!YIq ~?~%|bQK@FF# a/=g+gV0Y@PJ Zm:j))-EڎJ7BB++-Bz5l 53=p9yu yN觑|rsq.-Νͮ-4n駟VyLnVL ^|McZ,Z7}{b(dH:uĎd, Xf f~0L^;wq^S[Xa^%hZ:J*JK'P]=z޼y̟:>p:-$tla3@Feec/JO77,ruaLBVXcfٵ3Ш*/gGν}rp.9Ozvc >_'&.#z IDAT7%i/8GضޢϪ**m{ N}A}#Gy*^MeU:o]{I>Ï>c| xƌZ9,.eg(S{رng`Bf&j~ٺ 3涝pJtauɓ- ݙ`[c|v-?$ѣq,Yb7XB^D,aa7L#:,R)+,dɃyVlЯvZ.0a`ES5f^j|*x9`HB9~|j* &vl \Xŋ&4mޜGKtrY[m'ݻwӳ];6R[N={Ǿ}lܶ \+*X'o͢в%stO_}E Yl.z=n*Xdj(Q(v.o駄X!plͪ2{rt:&s.W~4cP[ٸm3~mUeee5'4rs$ۛ1pr|;Ѥ$z1 ^sBT6TVUܹ]MLb~O{/Vm3bͶ|;KwÃ/رXw5@nOrMVo_~!><}rq*"cbl;"ۼ)DV4ɨ)yDAfgeV^o}eegfN9S}{\E!{/'x'~~۳|:}:xiXoooz%$inOx/8O/fɧfz  ( k/Y7K2B"" } }M#V\xwT_q{/OXbjΘAx֖DY*++oWTеkW4bTX}/?{6We˖z}zYٻ[."X$Fʉ8w ob+wޱxOϲ͜IL_qJP֡_e̛֭;;vd 6mjJ׋oߞ~;vоgOӵƐbD._4)P8z4۶mc]1lܷwsifUK;crJKyQAN+WݬOqHJd5!, yMIAV L&"oߟMJ`ުuk@Zm%ڸo6mFa!CF,v)ϟ翯!-4*{IKO7L,  g)Sy2m۲i $*j5)$?CG~n.Z`KH-3yET݈x"N(g3oK zO:O:>y'6C1cfENtv|ZK\uO5xp;շWX˟>p E۶YC<("!yO{:ٻL1ɵ10ku͛k# XDf `+o[8d횜Wyɼvf}NBt:Zڔ{H<ȞѪT8Cm4foȿy^իiwYUN 9sgDFothPfйsg(ڶm,/zaڹV 7}7PCӖ-xWen>L'C6V5۬}U+'Jνz1,󣼢xYY||mѢA?GvWTkAٳL_u޼M6nziE3B?^ʺP[۳'`;Jd30j49r({?oDϢ+ܽ;Ǐxj*V"!oqqߟO.%7'NǪ`g%$"2 ,SBfzz4&% ugZib C&Ѥ}{~˲.TUTboˡ~"?ZM4*J/77ݕ\kzJJ6 d2bbXVC_k] m׎|Z-$L;l3T`vtTTVk hт53gbp\BYq1z!ڕ9䨪lڴ $$6vGڞf̠3g,"[jSǰ QSa%UBkot>vlȮyhPV>^-}海"V\8c^}W?0Fêtu~Q?jMFsiyNQ7kcHٓ5u V=e[~X|9gޣ_}EVk ؞HD'7ٲ%7g#ňύ?o׮ek1c~ݻ7'Ol2?GN >'۶:aQ(ͥK\/_6N2'a(٪c,3 -Y7# ?(@Zj Ϛmʿ*[̙x=lߵH,X@-,K\ӓ%~˧|qwwgL ڲhYKTmyNϜKn^͛6em( u2-۴;?>mڶYh#Hʷf[g{ݙ<#(eP :gU$$b f:9>Mc'嶑Ɉ9BxwDN@d$tv&ATQafh &O;v$$[9x|-lؽLĄVo` ima33p9d2ӧUG ̜61zx2aa,^Զ+ƨI+dRydzM1[Q=Leu5(,];f͘'ѯ_?h̬ޣ!~3ϐc=^uVVm"'2.o?fΝ2z =cj@}Lk&1 aR A6kA9hZfBXe;wZ=W[!O^C^)/lɄFF2wٲsn#w?ת6ByNfVl$4NyNO@Y- =j͕8V:s EEdl21b'}KOs%#VYڨIN-uLLdYn]F˽Æq1\\]x[msϣ/cLjfڄIJwI;J 3>Æq>%JElNfjtŶhڸ/S^ZJTBX83Č~IͩcH7//bK9JobSV=^o׉1͍"?tiQj8~ĪJNvj4f9\%5jNU*J'#m++,4piZTWT 3|Օ\MgOsru";aJqCwj+d>^_ a:axWrQ4*Uu $İ:~:q!sgݼKy\(J;wP,Ǐr;BHVJt r< ɴpyr/^3]jp%),0W7olb1JJꬓGPa:&?O +r..DvfzHH72={UhLυ81];۵==QOT6la ڳ-2.ȸ8: ǰ6*+ܧ]}d(n#!>AA5h]YB:e68!z[_E;A_[r-ۚ ڒclmR{ѣ6X២.\3sr"QY^N~d4ؑf9 Q6d2["eV9Rg9:'9s[A5W: T]YI3CSvs~rt:n5+..rmV0hΚU\0Okޣ2]esjx7oL&LJ녅lms۲rm_@f$7*)ܞ=Z/ZMʆ e"&yzb }^?0N+͛.ʉrs9mm47̣G߿9դlD iz56z=g W//I۲XlV7np߈0, iْkP|i"vQWUs*–j?WQZ9s- {~z7mʶŋ lڴnr7q  8:|C\bbT -r`Zµsb rW_.9/oHhܦ ?})]9Es&##[T V}hC(} <MpY{QBg& kzsTNvߒL=L&#Fwߡ GW#ȕJb 5.gw;f[;%!(&K>uuŷiSTxx *1'NFuy9pܽ @ѵkw f}} rHNd]Wp0:Zfm-Cu+W1ۼMAZWH^ԼcG ^%NGPpoӆ75Nk]w\85m$J4dɉSW( F0b*KܨQU&mڠr('> CA1c(ϯmJ//bC&?T1jE&vӄ]x**Lu!Kdg@#FP^PPg9NM-k3z4T1jE&MTQUQQg}7 _fx7jDEaaED/7GUBBBBTTJvի=ʅ S'=CZИ,FEY p'NPOtf\s~Za\bӎX7 tkuo` ##͜RqoS]^Çm8"F 7a8m/relI!|VH\9{-68g_Liɾp˗i۫xl[dRS()!FXR@mF\.7zd,>LfV%ed]F(#HHSd2<Cc,qru5m-ww̶@eLXɕJFRd..8)(Ftl/Qթ{!AuZ-SWg9>>Daf!n8åP*OCDEAFQH˖42 j5rh6ϏMa^7ϜVFFgÜ wWǥÇrroLFX PLW` ݺf '|;8գuqv6YǏrn\NhBuABB\c_|Z}} k֌x[}6xWz/\HRFSLѣMe:'_|NB};VV܌^3ˬq-pwgP><+f9; }]ӧ6^Azn6rU CP0wB"7EBou gZn.>2G߽Wfu+~u:ePf빜+bji V]KqE~d7<8b >/?dҥo|Sxxpon<;f!ْ*)s`EUP!v(,t(W߬SWVR%]=s6~5^x_́'[7kfaWl7[ۭF/7p:oV]$gŧ?3n`Epؿ[2Q qB@)11(+Dr26o&8$_zjIH'2 Fß_~I6m,B Ue%W0n>>uu:ne8|}DŋmVW` -QUTp`2B۶mTiX~=AAuid:E pvw9[MPD;v?@:*+C~OӦuοiܸ&MBĹ wR5^Y۶4M~feUI : ` zsTBB<< JXN\KI!$:29P/4ˆ+W'xfU||l{)¯Y3j5}$YYDvP"7^bZwp0n#DtB+xtv11:sk׈n5:[(L&Mpq0$Mo/(!q'46mDfͫռٶ11^bu7b>3Hi)?KsY +*Q_ddвW/:[ BAZ*"eKݻ7YY&'R6l y M+D{CrBCq;SMt4ؑ'Of09hVۛhQ)ONFcc)ˣ[76oQL 7/nwG#G8q822((( 0(=z{Ha@cܬ-=KL*q3gnޜPCxTzL߿Ff&ဠsǎ'0ȶmM _Adg}͍-Zdbmt!np^〠SC++:Խ{!l#jNE99ܰN*淟~Mǎ63Ჳ1vAv6YOӲC))4}ǡe@~v6SMɓ&;6m5kzݼ1kx}L6Yop#,ZKӦ!%G{/f}6n[,8ܝhKomٲB>o9h&gJ_x>.66P+ٹs'GdZoof׆eXr%_}XHl[i~>_#z{ b8'E[ Wn 䌤={hLۘ2,.6?_/YBZf&A䗗Ā^3)s3ʩ,(?B5}p/`JH[o޼ … YZQLgM-lyiH&MH=qiAN(+(Q4"˩j;mEO7>ԦmUL3s |<<;t oYpZA<5a '?5gk'ttYA\04U7e[hOhVccQdɱ}ص3gLL 4< $$?U8sTTT(߽;LfLƪyHMK><3/ӥy/bg§xPOdQ'rޞЧo_ڪu$Sz {OFO=eA{hBڵióӦYX2}:iiL~ ZhD}/dǶm|E6.]o;vɲe4fc;}Su:,_n\.7ìY/s:m1cIm޷WufH^0n WG#;t%V1$1},{GtF} cUɈڕVsQ08}4]H;u Jez3Ο'ÃNwͩ;L+?VP//m20??>+\Ғz_/l 2' +|lo= AyzZ4/RHC0Q&JC#i ZЄ_9U*ÂO=Çev&A[]_i)aFU˽\j]%W8.qeee8;:boRUUe3T*jeFqv1~r(RVXwr^Cb#"9}:[&+#[ͯ{_|a()5EDG;vX]5+Ws NN:}v֌;%s^Rq>RfΆ7o. `斕J vDGEв9 Æ1IٻVso __B }L$1I󥚵j˗s CmQ.䥏l-5?y2Μ7yZv.+jbΜ|C$dj!!Tidefs ptqUvfnr UZMhnqrvnߎaQ.zn K-ЧI}ծ޵>+3ϜaWJ 聇8&܆K/=P768`j5N">:D?]uq:TFdԶ{r;;de7rTjc_xx0v8ӗ_VYWܾ͸_$orN-# CZZ2V-^Do[FmCXf<#MOЖ-F f}{puqH}۸9eeˣE|ˣGhXyee9B޽e?-Zt\N:DPNͦ}\ 7YaŽ{&LqxWpf~>O=8Zm0꥗ TxVW0 cbo҄=e~DR%wvyr^? ]+13k.E5j9T 1rD|cJ:ohpKO]eeܘ}oxط/ -"$"p.䧕+D ZI[oiH/RVUE@`nR … :yî;Q*h9B>}hٹ3>gs!"H;{FHNT3UOGBAEu5k,A))+'] ikZ4 y7o{nn޺E4O.ZZuΨ}XjA,ߴWǎ(}MM1;!L#oB}:4ML=7RW˵z.\`ڵJMeF˽RP::BKCv%$ƹ4TWs,%ReźuܸtO8 ": cc(}Ut+*8|0Z<.߼ɀmIz!#RL?=}s 3.iS{qޘ9vQQ<ꫲD 1ؘ,¡*=MX_85JKpqq1y3W,tcfj^ SrV鐘ȸID~Hv{< RfϙCiD|6XC#Fy:)3gMRϞy?O?-18*=Q*@$}Mz$x7}.S>6h`8^7)j3JBQ nBoɤSӹ3M##)--ɓ4 eKAD0?f MQa yyK}}:ti*go\(!#~+CodUֵ7QՕ,] H8,XKNq-_F˶mq">:)) SIMK#.&;*KJM/Xrz<7&0*) ^}G.&73;I9zE 3`>LjZӇ={B~[DLD`NDt4?^ͪE8z(%%tLN慗^}^wGțPvCg`ɣzX߆~t3.gee, )cmvŅSǎQpѡDiB$>.?RN2ǍP2 U7[[~]ʢ^Jrh< v-X@HP݇ 1;F(ӧZqClЛp\Bj)+.6_UUUlQ+ 6hJ ڪ*lde_DaEᵤ2VT:wPP䄷73 u{C:w+ӧ =xp!ysVmյf&87'!>2fZP( &1*siiIK#d!BeG.t6~LcVk0|d擫'G6:gҌJ.P)Žn̓C𠠢p,MɜGƍ3?_sr9顇"i0(X=:MG4 St/#,b"G͍G_|>=-vߟN or(S'bCBؽg#ǏGThXw58SC5zyCzz:W^%!!jٶ-cO?q=?6Ne\< n%%(2ͩݹsGMzJ:#}NkdlÆ jAXt|":JR6ŋ9};I2CjD "G"c24kZvB+(!<Ν6g67o/n7 ˋ|pZ6y]/*bӢE Q-8~ٴɰ_2acY1%"'/<+ _~99|Ǣ*mu4\q8Ǫ-[X1o%nAu5GmcŪU$XP`Ed1zu[[[0;vEIiG'`Q .kۤ0v7PVX۬n5A%;=ݐY|u#N؁Wh(*;;nddԋNaN]yWMUTFJΝx*jZCa?KLC+5׀]/:ڙ)mlb:*} ۹SVZ:[` {ԋNei)Ņc@/_흜6hi@!%8ӧh]Z\Ső~5kQ(hڰ!9x(oM 'ժQ(NPD͚1F>?в%3'):F )t)kׯ7 -[&KG0zܺ>0#7oիx1}{q<'3<"yAxxq:}13g{{s`'s:pIy7z$ndgl޾FsǏ|@NQ^Kz=!.cW,OVX!G\#"XGaLё /) BCyaHbfb NN0QQ&Ypqw˥KyaRXڷ7t2A&FxaHjMh1Kܔsګ[6m";;p @=&\ xstqi*im _+U?X>DDczDZllTN'k?A E}CtBjkΎF@`t4ח:1[{{ 4LHaB¿B FXqW!ҳj^5.=ğ6pBÙѷ/^^^,'z~ߴ tNׄ|NNЁid8ޜJ(N:%;~o]D?Dq k#uk=-۶b:2cݝf ]^Q7WW:,GQX'Xz5Z߾ADǜ~,D a#)of[~A~^;vPX\LM3jG$0,L7m7:(ȱ5{a H^^XJã]r1ߏHk ϙ&"s /3?|!oC͌ ]ɘII'%iLŜWfȅJ۾]#sŔr̍K1sr~$W+ f7ooZCțVXaVX9R6=zF!DAؽf >B@Q矲 |I>yR^^3_|nsM¸)SDI}d~<=\c0p^P-om|)ڷ5KGݪjp%qZKј=olҬ)hZmڄ._ٙGgr22h ؟Ԩ@eoScan.ػXDGrhu +NѠY3\9s*]tPYC8D-n^B%% wv#K.+-%'=_An]qaluw ap'TQ^uy\;wbC.^]Q]UEʶmDaqmw .qSDkgYVTĹ;,/V@Ӧ䙦)~b:^^DkgҸq'NXLGmkKd8PRPݻ-k5xgss-GxֆP֙3d=k1{{%%CssgUU MLSͩܮ]55 ,g<=Xaſ 77^Q˖u>.)(WYVTT' yhMu5:oDk4oEUEEyu:vޔ :@Ue%oH͛C ZMyIIy+q]ZKJ =r휜'*d o@Z`|B»qzMO/4p6@pan߸a1{77B[B{d:eGTV5垁B2BeG۸15"JA 79r,֖V 8/- VK@TJue%PVeLJӧɻxb:j{{B۴ 7oyż)X<$!Xs7AZ?B:a}΍a֢E(JXALyX ;,Y^ؚfi[֢>64 \?uCPM_իšee:?#6frjbwϏC+V$ՠ.ȿ|+WӇ\:v̐j *JJ8q#njAղoߐ7VWh5RƁK JL4|,s,*ɨȑlIO'$jUx1>.n>QCUEN~~ N$l,++#_?J%Ź߾dNtJJOJkעV*-vvTUVڠ t!WQ@PK(~={ &-Ayi);V5^WaHؿyFVLMZp j4 {WW+*8nv+n?qb//o_=;E5Ʀ,") G =R(T*Bu 0%ͺwtV{tԶD\WFE+YaŽ,>:%sp=(b윝q=Ѿ=))SQR=->aad?@Nz:mn 'fqmڐw2~~TWVR/:MpCA9m륌98 Ÿ+bb( F.R4%ezk*!{W8{{?x0"_ܢh+H 16m:xc:j;;{tqVXaVT_⪊ RvϏPE{zz:ONxtofprq'(E ai={(/-=ϷaCtQ+RL(~SRTTpy0׿}(JUOك-x@vIy@EM=\IKYFiPRTDHӦBjN؁W` H'+ +7pU.)7+W'<0;(G诣;M =뮦pD54sPپ=n2E99iyjrusIݾz:yy49.\?wb:j[[''Wr~^4]oK`@MXZ֣;t@?732,ccoON iVXq?BX_nkޙ50Cͼ@i`_ENeu5j5++iŋFҰbq_}g_G hu ][fO"t$-{6 nHF f }T石v ps#7GG[dbrB@Ot**1{6wgt[YqBSUŭl0NR(WxcPJ_VXHi=R\\DQue%Qtc=negG,W񳤠".!SUQAQ=j%; ]Cka n<*-!~][:zy kܭah w"i8y3aab-Aqn.GWpYף|uEG-? v@y?iNxpb:NaV'6lŀ?Oի⭲Vqj9f - 3Z-'׮=0'\Ixǎ mDe]KVQRUHToYqa={ˎS8e eZHB:-_KӡW/<9k ~/?.;g r2-[FNn.3:V(tOLd?^ނ ߾6 htՋH]˙O8[L)\=6V 㞀vgRH3EЇ[o?3oݮ_{n}ug]Zq_~I`lŹJYCKľŋ\?jZv>dXMN()ae1oUee݋g~rh y[GGRo(/zE޺5%[5JK)+(͈p! Z-/ϣ!e***,. 5! 4BܵC=@xSRzVn^LPtt*vj?vNN#UBB]@Rxsa\!8>~9;*+qW4xJk=\y\!MZ/cV9jŋ>r{R`ǶmmfcShj0`:YYff{T( ]\䮇3a~;}%xSP1!%S6ofŎ4j#^|pLhT㣢Ⱦq=۶jmݘcO>&Pj=75<~`ONf´iF^uojt\ P U+# M҈IG]qm* Ƹ&UۨWSRh̍tA婵kQ(\IIix3Jq={L},!deVѦ !z;UEj]a͋o҄[߿^tRV&86ݐ\U)}8\%=J8!ACq$P[[ 3ʻґv9r._p0}=++9s'7rrptp Q#53jhNŋt`T2y22KN6(R|m={8}8 `U* yJ59.qy\E!jc(-*" vVX!r mףVH֍Z1Pk(짧 nЀ%ìYtۗH]¨ 5aX[h}{_zx1gO% 'ƆÇҙcjNti7= 0ZZk~` ޝ_bJB#:j]Ū 6vv&@uE dE`!dT*cKiQ>!!@[;;<2NXBQkd`%ÛR>⭤k *1̑OGK]2P`ccS2E_ϛZj54UU5X4hEImccxnrGM޵)%iӧsϜM9e sbNvv䗗c^WZ!K5a!I ##7`4T͛_{Z5if& `Ux1WBiy9JJ x`E Þ[OlQ[ҥ,o;]^R)߈_ܳ'}JF54??' c@At4.ZyG^ӍG+dn,gJ}&8RVa$nh(eO?׺u<#54QSdfd~*-[B2k:~j"ù|RKmЕJx鯼""c/,' ̤Qp0>Uy^RB+]xPp2)]J;Iċ9o- ,Tw\w7Vڻv*жK ЬSdUZqT擙3i}f\=o̲%K8IiE~~mْG_|Q'UZMHQ*trp͍6-[ЈFդrHHqHvM~z1d8Y:r\kגFqe%D4lȰQ̥W|⋨xO3}x\\\8sјtjK[5q"(&y~+6,^̯˗3wT9>{ sgɉ~_]DW3iݶ-#'M2;NU܋uTi4$6m-[񊙛з6i¤ jՊӇz*ޛ9ʖ(2M|}ϛoPZRB͛f~%~AADm+KG:&RZwNl >Dܺb)]4IL]7/P9{6S>T7D4`LN]p/DyHBk>kz ywJOIwET2e4#&~m&Vt/1rHT*AtjՊ[/c+%4#\]yOw/{vѣt)/晡Cy7 rMP˩..Vkh1' UUJQT^;;;~)KWs/_|A֭Q.) <=I ɻ}[xouPlJVwɓ!|)_mD_RQ[ZŠ bΝx1;FS Bsh|!x[7\8{4mLQa!/xͶJ6Y,#-**jf&#.]╏>L9; LodӦc9r~k>':%$0a8|||ȹ~[ڔ)|0u* 9Eɤ?A>jEj3hhdzn };o?owwƿ~Pja:#OZb,HtۻP5qCBt4M7foTn.6R'Ck]VݻɅ9!4MJkl,qjչ3_|.dvRaRkߠP04iJԳO<Ud 7ggB6\\h.RJ R$!yrAR >@U\8~y{[[CHEIƪW04k9L)۶lA2﫯WYP֕+# *j)u JEBr2 :%;#|B-IS#ME@ nڔYBN Ϗ[]GN \Q  F?>poPsߵZ٬0}nNJ5kxWFcTQՖG5GT%ns>=0[m&em"; ws>2B6hUUlޱnz[H*1gD3>g<"8\>&mDKr;Ʒ˗p.L6M9l/=$6ݻ-wN]Vd)M(ǥߔ{-f4ǛQ+^O?qǏײ%/?]5]=̅6Ifmmiӣ*nͯ6d ST$ݸ9d&s8u >l1>#FЮsg S.Ink'S a]܄));u*;c0*JX3˓Vp>mNuJ{AW TÖzTt*է 2WBPХW/j5]d[gOS U+_fyky6m)..fM),ERGh ..\u#[f ӧNqWQy7n1I?@}mrѣ:z*iÖ-[bЦM3>W,]yKߟ &IDC>o]Ns% j}5`bN*NsEtZu`k]/O;ף&Re׮]wNjkDEBAaq1F/-Wk9měDjZW5Pj*.dzbl^R ~L,}&_BUr÷\IʥK*,d.IѠ6Rt X%%#<z">,\+sXY IquT  64 gMP`RٵUB /UZ-SSiFx./ghU*1)dxSƎ)q.7̾cnj) CRIuUaWTjX1mˌ/DPpjH>3f Xt XZa Q(/Faa&&b#1T$v1cTVsƪqq|l~KΦE<fc{uJ}#>t_6@d:%%jGU2֭e6,]ʑ#GqcbxOz=0qcvʎKRнxGjߞgH.]ҥx3᥉l҄qÇ۠v]y78slAy={JFJެ~P[6lCs1o ޶6=z'(Q/( /+}۶|R^2 мuk~-T@6m/K[8J3h ]7sr|uN6mg{'ճ @L |=_|2{U**Yգzݽ_߶u+Q<=f踿mc]pAKs _3ya]] :r .TB1jZ>L@jUg*Ja]=rO?BaC@q~>/\(:f£Z[^5wȑ{{lwyE^NN߲ͣeKu+³&{1CǗ7G +/x<|}yw}-UU*JJXUT1ś4?It?d*MgoCt eHI!,(W//1o2Q %3ÆbVϝktMSt D7o +IٶiFcVYZJvz:v( rr(+,c6^c '+5b:h4ܼt ?poڄb:]fO7oRQ\\/ގoڄGppMȗBAnffxJMEmo_[X'loe.(@P`̍˗,-{Xp+;^]h79c~+ٳ{f  oR=֖Wywf|Eyyu֏N)mjn7KJx𡇌nr>}hMx7Qؘmq'H=s~˺Mcg$$G[HsT/.禴,.f} }X+{ uxrM62-Yªi Mu5id0gO .AhAyK>@:9 WT $OǚE Gգe߆ ӱwu%ZW= ҂V͝k1JEջ7PDF⦫CؼfAo!i ʉMecCN* uʺ j4i (h޳'׬\ң.p@[aax~u6hC!xA~Y5˖񬮦P:6hQ^ϒF+£0e49q#'))JMePN4lҤF7IyCn{-)iXDL mXbeee\r F?q+Z-}a͛3xifOY7u"%qwgc~۸=^=tnjȁd>M-D ~nߞgΐ yI1NP.///i$G7fQQ<1x0NNNjsN|74۶q%3-#ZU۶|#If<#8IӦ< j7}{HnEVӲM4WJ3O LfDyY믤K]E02h; NVXq72_Ėݝ^1ZM1fhq&D7K?EXc:n64ӱŠ :ݸ} cP^KNYX9{,۶o'(0A{o3" :$8rN8BQSIF<|"?9nnߧwm{JQaN۴ ϐ d=JPӦɻv VB7<-[ X(-My ]̅jߴ 0=kT.ȾpGG ^O: n1oeoSRXB֖ccooF KNFPP]]͍qi|\.< P'RV\[ Vmdy C?Uj5ڵ3޺ř۩,/x ~o8IZ:iK[FeUQ "*(ąDE@E\(.p# Ҳ7"P@V-BWr?K敗6sܓOjZG[ 7n̙XHAA4͞ov(|ڌFt@uT酎Sڵٺ5bbH9}/On𶮾x(ǶmCUwΛ`nT %;AJoOcQϞ泊PO^6E &%|}Q"5L>}ZB,f3Ym]+T iկ_8e[Oh^iD|PŒ3 Y\R/\B*חk׊]:ߐE˘ʕ+r(Z|3OXTr*w'oJbb "M{ń8@a5Zv2 qq GEnM¶m\MN.d?4-[ڇr?r#G \9"[ڠ1r~B< ի9++Ѣ9sI חm]|}NJBQeJ'hQ"? EQW4sT\ݦ?]^̙X& }m XL ժ٢{׬O"4]X:Ezj*-СO[Vc0OTJHJOg?i⧆ 7,E4DUUDpS;*j,bX ſ*=m6l`E!E[fEΝ)_{Wr糠, 5jPeKNlNʉEͦ 4λ|G7m*R.;;zݺEY,B֥jt4bcI=sHi3x{Ө_?ND\\ҖEþ}VMczT^^;t_o.a!(B|Ȣ$|˗f>eQL&{oݨi*aNǎ:qӄ(EٳTwoj~L&ժy> ?$HqӡMZRWiʸ)CD&xk((DpD=m//V8G͖-IMLkXXA*qTnذi e+TjJ nМ`w3@ 7U4ϿJ ^V PeK{(_o_( \lUX7^zu{pcEd(C{.yuCqq\cDC=콯øz XZM `l,Y4s󧴒Ϝa֭XTS]!Jmj{:CCݬnmFk4?k11d\fߟ,b6sm8Gw"5)ݻ;w/WT"px6vQa`Xhܱ#޾-yO~T^Dw9;]V*5{TիgFݰl*֨AHs߶-WfcPZ5kph}i;{oZaPsg*Xv59}kz~j^&:wg ;vpj߾|Z,Tm>NO˖~ǎ\Y0__Vtju}@PVѣTN[_xwI uO;plau֠]Ә4=z9WbR>,W~ΜW^u˖4n6N~11b0??m˃O?oeĉb],lW ?w/>< 0o ;ٹgX,|'TSqkX-DMԛժ=LM_$bE^_P^ ̛A7x0ϼ׬s-I~ =LU՝߳ukؑ <>{6Fon{߫0rBxXsx.:DX͚ϥ>MիCjt] _p0aa-f3Ɓs.9Cz Z>wrt\ r߮Z,\8~Hi cT%V-wGpcE/4tx^4͜ڳjMa(Kغb﹇%zB~Y70``40/ UP'm5kT͛:˗t=k/s?[( ̶5aiؿv">mۨٺ5W.^}4[AxJpDE/Dq]YI'5ܿw/CVhV:Zh4)L4%3{B&gc…nݚ;|^a4 Gl9q 3f͢RHS~ݻ~1zy̙i֩sg^\i†ի9Ȕ=r(iтnj %9’"tjV7 2m/i٩UaP'-*}6:9 ۺq#&陙={ݻsː!8*WZ=˧o͔WO6Z24 T¢7ްUXy3/ΞmêV70:ۻ4j= r~ɚ5kxw۠۴^,Q̙Y#9|mV;WCgx_vZE;̙Bs'v$H܉/ǒ͚ ҤI8dǏ?g$|??Jիi5x0UΝݱHs/]7 ͯѬYҶ=q"o™G Vq4ڰΚWmp? "IzӦ%%YyNBӥg1 s(C܄S9iKN&S"`( TjذHOKOMſBL>>EN[dVnڴ!yBk*fU6$)BxU硱'իY{~F IDAT!pixl(&?Ͼ*}=W_ۛOwWKⱩSiض-*Wfl$8IKJ"bEg~7uܼe mקu^+yl ( ^>>Dk)9p#G8Wo(4Zkh"fF_d?]mm}[v9 k'0!Ml vW֭ʕu}P!M6 4ɡ:5>TִGԭK=qRICQ:DN@zJ Y׮Gvf&v-R8Ν4׏{l"PsGPM"8o6e2^?YYDq듰};իsj^4ԾDQ.8A-]ZJ ~!!;raʕz>8Ǫٗk/}Ivvvΰ.7c9z͛TRq]ʺv}$;GwkȲyNGwŋ4CO˳z2._b?( Ҹ];[F,\Y*ʛs搞+=GӶQ*<6}wɷߒt q0綾/d;lNvh8Wu*+JΝٲIYYלKH`Ӷm7rJN'Oϙ3tj<*@6ϧͲez.6[%At4/'nZZñӠAo}>Xh9.m{L.]F}l=&7R{umdϝ< Jzt&; &//%;5y&ooSR-,(FT9ٌj6*W~3gb!8"B|jNU5gf=N Fk>k4C;4eeee?9/qf^`Mz=/SUnzkhk2's^G1!D rb0`pJccJ*%ۺa/Y!~.% G?&jaWi۳u+Ͼ۷y;};S_~_|={EAnJH`-0gs|S8eF;wڵv^VSͱb.x.\{8~́sN9kέK޽TYӨrTߧN)'^HBRgZzM& 7Aa9UeK/ΜICFazL>oQtYӖw+x[8'Np%jDFlEuH0[zg?ȟVqӝw_0( Ut6Ow}cɎ[SrN[N@96n۷mcɧҮ{wիs}rjYk+~UФ qqrn޸v-ZطC[AԦm=̚<9{کS'n.{cˋN X<tچ:WTmlQҿ?o[k6 \LL-[ִ)593e {[^^s4*$^2,ooN>=G˺ucԤ$]ΦAd$|37uùzeHbI0Eҥ*Əbh(̝kO^^y!or\2ڵcaksNۂYضgM9q/|xm ̝;s:&IO9; 4*P!s|jXi:8jU ~d&Ey7:`2Ҙ:z4zAL0V߷/7cߏj(xuÌʫP!80I}kS={@8y-5,\z:Mڴo(Eݪu6EXx#--_zrVyM!EXfޘ={p]wѮkW«U#x ,#.[8 ⓯bkL 4e@$8t50˹RdY)_k׸p'N`^bUS jl]( |҅[;?w['@:uxn xI!gedqJf O?h$[L f싅 |jKo[h)m^4 t1Z+ep}q__^Ӝ疶Ջ9&[UUCώ촰!+G*##Bl5ij'dܽyY8Q[yu>n2{,h%'o2KҶ9r"FUE ǖ'SV-Zjʩ6u F#'/]`r2 <γiiX +T`mXs#c0HHIǎD5n!*{<%;{S-;Ʀ8f̚… \ȝWQu*XUJXժ-ټ?'{/u>Ԭɤٲ?oϘ˗3z@z\۩]'z͐c?v9S_gΰyj7йY3;t?gdh WZTTf~;ԞW'J6jĶ lu(6,?-&__|8ŋ{|LAJ-sC6ν5ܴ)K>ʄѣiکnkc/{lڲڵ߈ fSOԩ:iQQtkӆ[:峮^e֭t߀iVuX2hDX+mNLC¸{@>'vgnc3WGpH=zӰQ#z<|XԻwoQfU^J;y_߾V`Ԁ9qbEz]̗umܳs'} =6e3ɶ={X,S/J3U{iU\By??jժ9r#3z4l[,9D-1XG51m<xZ5C&$/Ӫ~}Eڴ xYG5WVU\ł+=t(Q| hgmٴn-[ڷ+hN;Ś/sլ]b3GxhT>?I3g:ԋ}i[C7ڵ#.> rnll,:t7lȕ6kd$hlM޷][W_ѡG4l+5ܠ un>iܶ5KÏ?bT!!n֌J2׭=_K4{6A<,]П/\Δfw߱Wxw];lElhҩrύx!]!Ʊq&zvJj^F |F{4}DŽw1ºa39L1cX8w.+ud-Q-[ұY3bccqml޲zsTm7yy__@vV6'O&5%\xJz3͛G=8{,j~r{诳~Çg6+TnӦo:w&%^^^ӲukZYWE륭mYIHJLĒ""#tD֭dNӦ<6nlÇjQuxL&C-3TgaD5hXP46s&u-AF&ѐ?(dRwE=ݛcǎq(>ޡJ8t_}fu/;+\y^}C l׎%_}EΝi­s>bnԮX3Omι*m&ݺcYYKH`}|<}4wAiS͛T-?ΜY9s&+;ulkDA>c~ܳi-w*:Vt˗/& :Ĵ3ݴpΧS9sr~&NF,xu^{<\@:jpNuxnvÊ|ϴjՊ&G1C-f3sgɓ,zMœ4&՝73r&^x1͗,.N:uxrv_O[nqx+xhp} mڔCdZl;tɏ?Vf΁U;#rh^0Uqc&W_%DQհfah˗uj}E)gv4[7GM׼97缟H{98OëVe#i|a{򺾁T[Zz2.ZDUi%Cw 07T^ dlc3}dœ#-%_|:U2elL&L-:֒%ھ|#D!C+sr1Xrw؟כӧw`?Ӿqc:Hs:F#?).\nJmHI]'2{w(bJ{|3Xt)];t ,""[f{`q9Gֵk9>+Vd}:m33Ck#jчЩS,~5V5!nߦ mƧo|3|7Uݺ[=t@Vزy3[lwMS3L?zPF rmpr+_SxǏa-K.l#fftpQs]-WFz 4|8# ï&-#C!P!WZL&Si}ϴ͜Ip^ .'&[{TB᧟:W}׭ڌ}$ֽL&g# |_om1T:3}}[Mf <4L>qբ1pH7i 6@Zv6 \IMekۖ||Wi pi|bLqϟ=9Nֱz͛ӵU+~޼w-jL&GU'``ɓuB}GVzzRn]"6q>/8U}˗IEJ[[oŐ!l"u/=zp,> :`Z @uOV `4ݿ=m!av.P_f͝wz4!qؚoָ1q[ukGC5?t^NVz:/=^F#ϼ֩hgܖ-״j֭ۖ] Egdyw M6lܼ4f͘r!{;se&M:NYES}i gO:&;ziNJ\[naܰal]Qu@^x{R|}y1: !{T=f˗ϚEܮ]:vLll5BrM5yxx&=̝ <&u_͗LJnsWZաyH8xз룚7gmBڵtsڶUH_>NhK;FFF︃ÆѸ];8uݬ~L?QtϱUiШZWՄ/xpHGFrjiY}7UsvzYccUzM=zPE#S}a߀yN b48 66t(t6Eihm}=q2bL6jՊ̡uι97򺭬0~}gO֮>!7(j$fO@Mw J1dQEF)Vm[pZT(Bk i"IQ٪8 B Qa4AUn]lmݺ/zg\.$3#O?6ex{ӸA{/ 2z4k rm\ Y #///B˜ ~{/6*tʈΜɩ9-P<=5#кg=ܥ 僂a St4|}=SHR22t?wEUɼv 7jDpN‡K:ZPs6+TQ (Qћ+,$<~-ϴ98t_}k5X@YaGSJYtX+֯ [u\壏z|+ n^HH|ʶ= # ]pnHƍ N3X9a5koU Gvf&gN]ą{%~*X[nطn4[ T֕C""ؾ|9U :mvV {P:DZj*/(qgX dNJTQ#ϕ TU%oiR`(I၍(aڵ[,bH;twT(¸pڴ)믦GeOҩSYW`7zyu:[3BދX:W> 4S8]tɾl.r8ϟ=ʑq N_PG=m/aۦ\92ҊT>Dm%?ϳIOXZ9]HpjU*@<'Ŧ+QUP(n^ l{]ڸ쌌B)fM*GEРgON͟EH}O@ # 6Eհa#F;&Hs*TN| @Oڵ( nɾh"{~ҸSlNnsCQBk`=|7o^f3!5j+sZ9sFl/5ݛ~H0sv6Q]( Upj"-("^ ͊9s(JbbEQ^xqҖEΝQJ8sgfXF{ťbqMsv6՚5FnUTFcfP_( 57!q ZUu8޾_( ՋЛ'$"WrDuzbA*9+(;^^En fX!Djڿ 'Q#"oA*o醄sxͷ|y ~CI)A!J<rd4G~B!DA`S~DӉ E( |ŊHExFTc$Did߸J[O++-R՞5khݚ0wGTbBjTTǏo(iyOm`eJ|G*.s"n$C£|(oTTK? !D![Q"B$ARQB!|1HB ARQ-GU! I !Gj$Bx+9wGQAPIq՜bj)X!\ʧP4| B`Q L x:lxg`2H !_!QB 2H* !DbJBdK$U!B3Hj$OzThdBxARQB!_! RQ-ZIB B #\ !`$A ~ң*E#C32H*B!>iB 2H* !D_! RQ-LTvw$DNSWw4RUp(;lxu[^!sRQO񟑨j~E4j^d *$?.m-.;"x%'suT+UdDIQ^U3}EQiP~&d;.hٯ;&!$$Dќ즪twLBS)˔bJ4wܾ 3E'dDqh*i[Q$1D^tw n>0ݱe%smg,J*:B ျ#Qӥ,[ʥqB]|Jdji3Fo BQ"cE)';)" !:,*%D*eTTK;QB"Qy/+U!(!RQ-*Bɯ*#!(RQ-ZIB%RB1B2NzT$ !zkBQ2Z&IEU!(`$%Boq;ɏK%vRQB"B!GLBG !gŔ$vң*E"CCHj$U!B%9Qң*%C*eTTK;QB)U!(!CeZEʴTTBң*DzU˜|>qc!*8XSPc:^B*]7@%1Q4;Üip%<Lrw, r?PUry_h%1&-pw,DIxݱkX`roEogWT; 2!Jz24KTC RQ-+J^$*CB!Bx !B!(RQB!BQ*B!£HEU!B!GB!B"U!B!E*B!B!B"wkn%292\KΊCٻ8㟙RTEib{EuW[u]emV,wł" e)J&? sNIW^07'7'ɹ9%IX9/jRi-p40/=!Q /(8,d^D$Nw V'/؊'`unNzs!~l밺0bwʈ*S`ן? |xlZ!Re1 9sv2:klVDų8a[(5`ÞR =r^ `+.$ЉODJgd4<66|,XCWI|m; ,&Q<l+똷 h \7Y \;k\\wrT<'q{SǑ]%@y X[ IDAT_&Տ}Y܉ʩV3r<*c!_6a6oB.?Pa=zc7oƽXI.@㎚]دMs>]o\߻ Ll+ؼQ}#$l:c+N4':܄X`=,ؗUskwzja3n8JRm+}IsAj-l9n?Ɇw%E)m;q,Q@iqf:Wj祂+ﹸ[$Z޻ Vmz_\/HF(rԵAiL(ݕ$^+Z-G9k\U9*Y2[-x1R[& TI䶫+|gFɦplx-G/w~Xulm ǻgU⟗D$5\k8|N^9~d[1z&y`eb$CDR4ؠw)V}OpϧF>w"v݀tp IM@,"KO}5q/T\b^MImf]Ġ] +s-/\^U^R> HɹO}5/ԏ9vdpS-`-~f؆"l.!_b =c{z=B\/l:} Jl㎴]?<Asyo[,0\OեUU1n[(Y[j߲t?[8Ǒn(C"ʋklCN`We8>r[!m$d^7Z |H=]0۟.VlErf𮲵*=aeX9vs9; }6cV=N.ނOd6F7+VYFSQcF0q'b;{`)|dr^M4RjYx/z,`.\C%Kz+ϡRMf'c{ 5('F< J vu)а<I`u4lFXv U޺9}C%/XϮK_.\I U.ZZ^3HފbבKWV.oÀނ4lmTTZEe\|OyeU6䕿`籠+cekoy;{AsOm͎q~% ;QmVZwaM͐$;ϑeGGc I=GDj2-8HtgFZ."lW5|ijbP95NNp>lt,gt~]Gӱ3da`ݰlœ:8Nb*GaÞʶ(-kͣ[w162 V>=}AnZitFa&`]bp|IUH; 9Q=`ѯX+Vcowo<Wu H+{V QtXu#7_P#?w{qJvݛQh+SSKlwUA@0/ϟ ,Hw'NGȼ,(LNNp-ͅ9;;"Rw8+uZ zG"I2Y_<N^D$اHqHFQ*""""""Ed""""""QHFQ*""""""Ed""""""QHFQ*""""""Ed""""""QHFQ*""""""J1|zP"" %9x6UW(RQ-nHL]eHL:iY%1;"rmWD";%''j:Y:i#t$rr,YsKޣ*J. '@dΦHN_;ΜyD2""ɔn.9; Ff'2{tgS@j9r5kn!rzDDrkc/XS-0G""O]}oqK)eHNs"ɇHESD$TWZ TEDDDDD$(Pvj)""駲R)'*?EDNuJKjӗ[DtT~JKdNP""SD$TWZ T""IP~I>e+-""""""Qf;BOSY\i)PvrN15""RvVZ T""SD$TWZ TEDDDDD$(Pvj)""駲R)""駲R*""""""EjS+HD9*?EDOuJKjӗ[DtT~JKjӗ[DtT~JKdNP""SD$TWZ T""""RQ.[i)P@5۩JDTJKjӗ[DTJKjӗ[DTJKdNP""IP I.e+-N_nS*"Vj0HFQ "Rz*CEDKp@5-"Rz*CEDKp@5-"< XI2eU WNZ TzTEDJOeHzf;}EDJM"겕U( TZDDJOeHzf;}EDJOeHzVvrHQH$*C'"TQf;}EDJOeHzf;}EDJOeHz;\-|r}]ҝE^SvF߮˨HR-nNAld^Ρ$Y.2X=6~;tgADR+8Ow6$ ЧIh>t"] \Θ.E0F<\Tn%ˆT;̀n+mD<@5OAoҝ ԙMjOuYt+A]V)HFQ*""""""Ed""""""QHFQ*""""""Ed""""""QHFQ*""""""Ed""""""QHFQ*""""""Ed""""""QHFQ*""""""Ed""""""Q;R~{hdycҐls؋@4E2F^ H? 8X; (e_o|6\SC;A~|=)@سXWqWcbٲTH.V  j5E$;| H?8wVUAbS-c7\Ӂv=ؿ'IS:`؊SJR6 (HMm\68b$KD[Bl@u9/Q(HMU$.k3=?\ \C$.Bž;ϣ 5eR.{ p30(!}sT' R:CR&oadKpWcRq,LwFD R8?R6obi·쐞@i.X. ,N*S"(㱿a# $Ԡm')ɑIk3$ yҬNze%R~ h}`L*X06vUn:H- |{^{;`na߁W-!amƾ3) V`6sRrVVgZ)ˀXPH@)ο+;HOQyb"j"A[޺cOG %6񼑅aVWcAo"3 վwb ̫T oEӱXe.Vv%b#@SVᮅ]ow> ?)QGD$׷آepy['Z+ZC<&;4xη'V?yS y벽.-b}r[m-uR76u-񸿌=nwpVnMtغ(vچXY6w:زb)ߗ.{8V.x9#q'G$Z0"|Fڎ ¢]A#wݏ$r8yޑǿ ©qQoyOS{KK.<ϙ3u~ 8^K" ;9{#D3a#;ҞK|irQ9qjCY ?ķ'%@HlW-e+-ؿɱD8n y׈P/{]"s3uNv_.Eo/{xJ"#"@5[NlOɾtO;y7! >KY~'vs =vOwYkDe yؿ^D+u ‚G;NƑ1s<)N|tFN!O.."ڀxs@~HёB_֎43q>N!>#kRGorKir^stw⽅vXYXK:= ZuG݃2\RVt  t rM:XJX8$c={`C㵪{my7P|z/L=E`*K~?bW.V:`_72^f)G^'@?ˑNr[)6ڈXp>6Wg>06:,Kd%6'컾67Uh)VQj]]>8!pX- l(^mh; EHvm󗃣+ǶjpULFb@!^?aei+OR|ρXeYܕ<v=IŸ;\>!vM1js0u&^E벇`uY#sL!6"[cC{tu<;PC.<vx]v6s`eX{&{>^yMc]p1p+V%: -ņzߘ i__n=Ϯ9ؐ,ssї4a/jl7]SGT nÆ 8U#vk/DF5tP4hy/F*@_ᷫ=VbC#D8ۑ~o_29^XC 9KGc|ijW&މ7|/=# IDAT w^O=Mjբ0=[xPzz<_9CQ U| x?:Η&j{[bqu H#iYFoȗ_]#_@5j: u}ؗVx躹#\u4:Ҍx i9ȑհ{t}UHW{36{pNv#'Wa kIn%Tl :y"J.'Q[9[ٵ Q Sa_,VL8-!3N○ 7;>;w9ύS9[9u#x`Vs5gJlGğf៹*.!˰ڍ'>H b燃y˰t`C+VC #̜thMI4?3E-8*g!v?p5n<]a1dV#: ?`l(HDalx\pG9a'PT.*eov[86+20s]]V8M{w|6lXuz sP| G(*_x=VMMyFql }~'Bˎkѓx[`+F`-m@uXIĮE$-W#]?Ǖ QY tJ*haDemYv컝@CQ9ۊD׶ۈ:XzL`L9UwHy[|baa(ecyO~󰎴.Owc&*/x [tbwHcC`ߦ&:d Wb-a &/q\|~,[ÚNJÊ>Xk<DZMl #qO{1}ER%tWOloYMle1Ѱ,g!qY*Gzw{M@&Aj m7t+qKr^.@"Uá_$v:o5w`Xc2a|JB?`-u^ǐX*萠\CQ1Ń\{ڀҟRw,O}`#|4`nLݾGlˎ|;քs=>N¶sJ9I^{lӗb zte79SႚLf)/`=^}~"3 luDWauω]BXu$SBR 5zƾ஡a/~佁{ϟ\V`7N&6_2a\ !˶b- Nnhj߿~ IJʓK.VFxWlBSi&Wl+ֳՏ2A>pz{4u5 b{D`oDܭa;֧0xC)~rIXV؟]P@b `=XAfaDz|%r2Q֫鯣eE='z~ކ5l6'v,6aT&&Į2sg/@b_DtD lqjñ!b%&qO\s3Eg:lѝE lA.Kb;cC{ƽuĎ뿁F`JGo)|l@@bCV_mS$rl]fEsVà. 6w(Rn3HtL\*{U_.C9g(zO(B ޟb m\C`=ԕ` "xQ-$Va ܈^b5beߓu=P< X$N[n62":RSr^S|Jbn,:bG܄].>x|iP} 67#xIO =:q>D}Bڊ!*FQ۱EP TFӼ9 Įs,[fz z^9\3ar)?o]>*Ho]CH=LyX˿X%g"N!ޡ{~î{ ~5\Xu$CmLKD{¶ty3bWc痞ch5gQG^- vϱN=[ _MQkkhDl~k3R1k_ jaz8[e2jbK ?!=|٨-v!HoXFl ulCuc=A-Ͱ/."钃}/\‚Ȅ-^~;C/æN].Z|섙?w_α: ^݆5\w߱,Iب,bW`ZQc屫R m o:e6b Uam? j6}ͻz>Vks Vq@FX)8ya8_s<4m]^Z~i Gꏵ^&j$b%N`<\mёE𱖅b}36k{8kRU?"̊r`yyKgdkoܕ[l7Ѿu`":b/<:S4\zZbO"b|h[dawr-Gjxޮi]erj=E">ʒu"? Öݷu/xz`k*>_`r:+7®~[9ꨁ]+"kpxw<*Q[O@Wu-:fq.V޾LQ]vʣw?b0ǩ62߉$܅-t.+V.{&s2Kw]6u(_v\Eנe]]gw|\<;=rrj*R% d.uM= |2D¶+"yYאs5r$0aM3%"3Cw†pw\XskEN+aZH$2=eyJ6`~ka\ITa[x%yv\"^*EĽʛDbX_T`xf23//'s9wJ7gQ5jcѺkXa9SNa5٩I'l{`݂hbt܏}ZKyuګk^tm K?Hkz7M3V/2wܸ}GW|\ѵՇ~-Pݼ}3-j*PU]6#5Pڼ}3-s*ίMju!:rru߫뫻g ϻjӪPmY0Mf]wqZӸ>7/й3+4oke0>|)|1| lR{%ѣaܸįwIvf⏟z*۶o0q"|EޫTjK/e㶍;~Y&uեZ^5GpD#L>/29GD~yŖ[QI!0hSUZ9t)LlGYwmжaC{ͧט3&+ ֮rj|xMX"{M?Z;Dظa׮eʕ~G>=Vxo</'#aW]Ő`5ۣ[*3F+/C'ږ[Xnюu:pMkU=nشmS޼:T0u=#H[ӻNܬVڵ.v]χ?Ä u=<oN8N9<خW.۷âE>+{@2"Pa~,vh]S/z'u:Uϕܩ'uسBiH$Zۼ}3Wg1V? gi@z޾=7`_}a6E_ԯWsx"6PҥD o ^*Ǟ͛3,v,hr(#IF٭n|63Nz$^Zsc6X.kPn4SNa{ѰFCx/M}\^B{ :t}E wjPzl46Tj`$wz)׺uCp5~O}&hظ16f`-|s̟3'0}jӯvݺ r;ݨiSqF֨UkU+Q$L^29^z}iNr:'y2Usry٪'%8 k_:@֫<o׉UeKVo?5+=yLT_=G9oUjp>?f&x,j4`L\4[Ulqƌ>Ujps![E(Z>e]-,UV,X %(8/*4?/|oqu~_»eż;]tpA׳QЍw֬K^қ*W%뷮`iVY_K.Vr=ytssrmXOLfk3_Si2vH(|UvJ^^/ԽKqi$RDG߀#Kh]v=mmXcѼ?'WqLyvب .;f\fksBWpI* 2.[Q[eׇFқrVթ˦rJ5F^N^`ڪU^z{<úOM~j7M_eRXY]ηcҢp{u`Ew}[.7'7T9U%7xF~^>C:?wܱ:*% FgOΔןFv۳$RX1cǖ0{5+ȼye˞אhG \mmeiSZ!> 3G5*ByѠyrhXaOϟ|sֺnp>ӒxҎ㛷o)sW_E*2f̎o"W|3UVġCa„A*Ҥfrsri\1 .t{yċտ>7ܣ#j-Vl.[x縨E߹?maj\[N88fT$n`V[.Pvdqղۦ/5 J1Pn"lpնX^k9~ IDAT؜Y2c!|0v?Y)Gr*[D[l^_/Xtqc𞃹xšb LpE0vYï^=+uVQڿUq%PPP?>{S5?f-ZnW3iٺus>zݬ^ؠE xF+V 84חeճUO:52$:IOrQngO=5}[kᇶ}̙Q r=`x- X |’ƌa1PV-?ᄸ+Vt+1[lcW~~ѺlAg~[h",=əOo$BvZ/h5k!v>ָ9v,Gzym_o{mG"2PmTwb~\#g|<i0~;Ӎ4n wo|1?nYOओSǶnKsyJIjִޕH*Y6\Bۋ~RSC1ZEhĈ#*WF?opFGudW_ϼ^, ݷ{go5-7!*dACu يItxVXAF4tUI5 ׈ɋ'QJ NT>aѓFss9|<7NY ۚOv*zNv |˫KpǒrTiV=cI6`jyzȭtk-r1ޭWothV&V<<zJ{%Ɂf`K,FSS7jݚw{`Gw8:Ya9j4O>thԁ+f$-O^.r ö/s≰>йs񅘒 ʟ5jXivV:H<;4d{G)DN]n;lݶu=Uݛ6ԯ'WJ :6ͫ .O: n-0T VT*b_n]e۴4K/Y6T|՜hVb8UYv Aw;`Ή 4K]RyMd};SYD`b9<y}_x!z}Šnk+A'@*\Pm8|xMR֬W_k׶o m[aq%eU73'vmХ l^e?*f-[6[o)(ZSǽ2g-O< ᆤG*~;j귯6[=is!k6IJ2Gtn -o?zM Z=Uu%Kv];x69KT@ޗ3NkD6ouސҽyÅDRULhnCօ#+JasxӕuK;@sJۦ/d[[,Y*[ lg +;{5݋F51Pɾ`֭Ŗ~œӟbhYSM^)~ڵzjZN͞ n:uM?Kd 6ل}mNA:?j`|4磄Yu=O:cKC$[xW3˵MjMn/ ¡tZrpqV.{ͣwJ214&raD T.- ƻHk@[A g,+6nٗ]nQt)"JIQ"8tU)5sAqQˎ|ksn5E{K6Wu?{4ރi˧{*,o]}?lQ4eLBkĺu6,ճz4d!#sxw\% kӶ^[za۱훹 ysF~=Z xK7k@Zձk|q,u> ٿ~ޗ٥A?wg+-z(m۬sg˺6/H6য়ʞ7Y?[]wf՚ԫV-s.{w<6qD}ظ-wLJz/c"JPϟz_1qΕӛj*O&zW b[^h 3]^Vgv Ҥys=f M {3&|IR^+w*`z7~J~pQk "ORիVYmS [nپ]kO7ڷ御:f_cK;M=[)/`[O20\t;eͩpz,w=)SҷSޣ컯{m)YȺlF{4ރqCMS)`zoT\*{O'9_7=mXFMu(9ܻ;Ӗ~6ؼنBk t|la~`ר~{__ݞGTz|}4- pwӒN 77|bѺEV93&v[*OMy^9-k\Ӧsp=m=˩wޱ={1ۼQN W_0Ɨ~!W"`AƼ '']wߝԳޱUospaIy螨M }yzG|9KШC{c{eYޣŨ> Fېbo]6(݂35^㼎9Pk_{fL0$[>`B`#mg5mU96h`F]w]CY}ݲ/dD~zVoZ͊+x2Mi%\Ԩhı`%)VE=.UV u f/*&M`pWom̙3Ѫ{,}mprkG"4p.y;>켳ryмp;W^C,~pqE?{ݻ_I$mXy k6aΪ9L\8;3 z]ZT(YKS_##?/G8լcJ6_Zw;ѱyG25k4:o*ֲn-{NY3wͽ: oszɓaLͭe"?7_Uw+M[w7ވz&j-U!}{}{_]vqlP;4}{ްZϛr4n2 |+ݍ r)Nys@WjzIEbXk8'+=`Tˆ #1aDj .DᚏᚏյK.[[^dE~dgDOȂp~sGtbI澢1kslj;mmI3bMlzgkVwԆԥ tr\XUEիVɻrǓ۫zE=ݪ{j45҆5Ũkwzl2ڴ˹#گ kT!ޞ_Uc7<9'6FSOU-{rKk٪6FS索"Sf5v(L4)A` L017z!'W'!aaa?Q5 0 0 0 sT 0 0 0U0 0 0 #0G0 0 0 H*Q5 0 0 0 sT 0 0 0U0 0 0 #0G0 0zE2Daa&mVِ%0T'Þ#FP̵k~= yD[aEiKh+B0h[͋7ް[Fp(^|~}o$![&ڈltZ W0}-ЦzþP$3N/Cz{SoFꍛz2V|o *ZL;| YW.JUla.} hz.}_ ,yӊjeo _Sq0eGx0XQM{SY[&FʠS(ӠE?ʚ>\yHN{Ih|n;Jm'52&S'aԚq_ nSw;p]kj|;p ұJ ceac3Ma*^ ;ʞqUgpͭ3y;&ed{"84w :Dw ·vAWlFFYJW^ M=FeVn^;ο W]u2/FBh76ߦYP<1 {$Ξ9I@YS58uk |:|baI˳Buybdȷog‚nN 1Ũ%KFۺ/cSx%xho #H9tx Zo/φ9'š˨9_ۆ k9QM"3NuQr6ݞ˨í80/zn?[GHo_wLF0|x[/cOx -4Fܑ2^ uir^w\o{x1G5: &/_I'&/K@~l3-8K"K@xph'KIzhw1jǰ{˹֤L$ #K|t3} quԋuM"^$D3`)P'nz >E)P寭{0qw/h*0%YpHE޻/?NI 8 shaTF:h:^9%T>7] ôm7sT[3`yȾjr2jp곰$x~Q8E'yȹhv3!$1gQ;8uRQ)xK/H &{60O*dwpNji)e.:̲=81G5)o ]~ ]*dM}Fy >ٟY< SCEq7HwEQf)Yx#yPOH.?Ū >@{3`H-29분9'G݌]T/tF|1G5EX*c,&/+=q&಻ݍ7B˕q7Hqr< _T 70X@ Ƌ߾S-͒ȓ׏L= sTSf0{? =VÖc8Ȱ0Ca`KPr%09paTvd'ezz9HYFD | X@Ȟ@tê\7K5Rh[.'q4tQMA 3p9AZ[?MFɇ =85rLEp׿]HA#iVm+^K9tEŅ+fv> 7FjFL'\3?ƉbǞ9u/^)lgSJQ6lT| 6^]_aelRLxL{=}-g'&0cӋ͝Ңv'pBm@o X%pZ=0S] e{~D4Rrx"oW$&F`~a=rhsX/MyFY s+(nT^$~`74 GLMy$NT ϭˌaj3<EY04[? YFX'qˑ x#i$5Z6;UM}>;o<bO0җgiJ[ ˢtdĆv\n%sTӄ,f}%}2dUfI+bx<(͌)ڟy#م4R MOFos \>@{ `,p;z04wKMF*fusW}Hۆ 8am?6۴+wJMFk'fc4)5^wl][/('3$$ooS$  3; |Z Cvż(txZ܁:'eFY^I] #.OM7M1br`M|:o{/M[k34s/ecQsBk~G&Kn?x߰`0"Euq|MyμΟn—'t0igpl|9j:Jaa-ٳ٬EM,ooeF'A@8/Iw{&ڂw IDATݪ)öwӢWҖz֏7/5`;EmoĞ c%t]3M!r)mVyy&?]YuTq+װ?T6dp0Ք~mJʑx ف۾+KUF,˼@_ɪd?b-C$ x82bǥ~ZhK vU\Yk4?Ͷ^b >: , ~g{Zlc OUYoC@3X̼6<`3h}i>-?я=1?.ڐ̀tD.Eec}m_1ꃗM\kdfp ~D:ÜTt[7I-Y҃90'5+}NJZoJEF4Qm@+3FNd闗øtHazdQWENdGpmVGوR`޷'^wE$4ApnU*7rcѫ:3| XhѰl1.Cdk`C_P{,3bMo`0OnFE,2*6a2)]ّt:3^2;1@ƙa9ZEqgu?leJ4RF ~Ydl sT(eݓY}ͷ{neϗ\i Q܈S ="="V:MѲ,Okc-8YDEETvҐ`o]_%#km1?@^-88ew`"xa4,@QYSeP*12#\sYNe<TrhQ5X)̸gLѷO^4>>B#P]GhI_"&"jQ,+ͺ%٫h Ȩ/>øpn=,a3/ 0tIbAd6_k_`=2ZjƝ\fJHEFUjP+op|&];G#d3Tg:M(`"!M\3}7idQ<ȅ@ގ2H<Ͷ-:l򯆲'tӍ>|S^&)AU73siRN z^dw 7)TTO…e'E<8߶;GV ƨ_^&ޡ8%1B^c48$XȈFE p'vh3sTe+@o?%{wl>2NBH`\`F@1"DjV/ViF{PHmʀ<_j_~l1@>i`6k4dT4,wS 3K"\ȃa'Ɏ9FT4oߣ?d?IQ9}#F T&EȵD ŝfQXN3ja$@+3Y,f+]7! t#SsTMfzf3+~<5YGt'|,3 wp'.i LCDLq"橶v<٭g'#vL> 7`)!XKa#m]M}mZ֘yJUFC~ZYj\jIƈ dD (p3#ML+WAQ[:$ne7,81YɈ dAD5*F=BIs( ?Lf X%ڒqk8G7j]{/ٴd]V0i@T~eFl|LIt]foTϖj:#J`4A%X<]_"rag%#.]8m=UHKTX"+T: W|u@ra$9 xz88_*#!\>r`/T'VrXsU'C" B9,VqEux^ @D~ dcT&߻|{o }84Z\yUud-;"=pU~NNQF2Z8 5Cza,S \Z ,׷cq#yy@.dcQ5Jn)NSoE6.'okܩFxqxFUM`)Up7JfD;*ǝ4݈4-Ij;Wr#n ̸qCvm}#HDvéW%>ϥʹ|`~''j*"/1DFE4SNd60.uOji>sU#)-q;rG`tƕ8@;+q'GE3݁ۀC+q fW!"_fU5(XV-=Me@qCt~çbszzG%V~S^#*|#B^j4e r1N˧@_*@}9o~9MjkЯ8c6Xܦo@WOxTLEF=aFRI 2bsUNUw"4W?+`o IDz@u"N'D'1 ۨ)@8_TpZ1nL IdTWAg2""D$ :pyꡪz~U LM@Q%CF$ z?*F|L AN1rR[tRgZ[i9FPq;B_B {BD$ig 3 Uֆ",mR5UOdQ=ׄ""ŝN F+TSUM9Q*U8b@ۅD3ҀІP`:.bPWeFm`Y&@6>/U 8)F =pyu$$~_#)-qE8uSCY7IHQr^.Q5uՈ#N,k0y-hjH"""p+g",hb4g(qJS=M75_2 "`\4ցM`SDN)&QW6g?!ֈ "11 YSN5غ?@:㈬%3phq?h (Y4NT(O'đ- [(T5.Wq<]gɈ7>OBmh`W?lIDD j1nf@mH/` 8~' xNU8"%"#qg9B0L{. x9m0"܃u _'';><cNȉTHqtmQ'!i<[(oJRQp`o~؎bkj w ./sIQqψDSN+u"K-nWm1Uw:0s3pG(hQLV83xܧ "< FņJuˁ1?0@v9Hq@|]Z4 ܍~ 0LjIOQa[{09PW5FyxXDN§?qZ':ɹx=6'gvNUʼuå Tv{Tmm)>T>$pbq~ DdG@T+SQ7sT(Ozv_ [(PWz7GO>TDup'Ot[D^FdxWD;Q "RY M =訧exOJGUDvOg9TXAH<^ d졪?G{T\h|Ӏ_= 1R U]3Tz)mWr^*ί]Mh#-yV|4sT(Ogjyr,Pn-MͣS_l&NeTgN{z9^SM9zZDk8[WbK\ \I"?T^'U.5 'Eۀ#TuIHaOT2up%L. ~LFf9ln躱8_/+ηF?.C-$!`iyQ{Yʎ{Pյ<͛BTF7TE P TbS*p*f] h;&X0HK9X{݅SU/DH`"=e%0@UQհ``qOo)8q`("6GCq3fT`XqYe9"_|ц9FJS?&ͮB$rU-W+pIP3"R?# QCqB:AY#r n*P:W~jB0F)|>(D\("껎_fp!:X2H0w\-W=co|beKP<x FQ0q0aE2]M9\3 88_GV#+9f=ί@0GHyt}Q^=n {?PD\AU/tK{ AO<_ U/OH0nS|Q ejzADkE7'ܮ@GܩE[zyN6zf"?Ct^9 -jsB9*5Wybt* D_ee8@U}G˾W5@qCu̥gw>^clSwAU} *KST(NPS)cmN='Dj "qeV#q n:H37I88_b ׼8.l4 DՈ+"ҹ2jDQ>;](JϹҧUuNfZO,4\68h5{[9:Jfrz0Ƚ)iV ON2xOUFUN[b養LIWz&I G ߴtRr12"]_d$qucpRGuпImA:8МT#*"@6LQ5⊪._E$_DZRp!I IDAT]r ;Tu6YP1 5ގsJo9]  [׼^\ [cvloDᚢ<բ< <)Н˙.PzF>T`fk[$AuNX'V/'qϏQnR 8pɐ6:Wn w yOTlZ}dmH!"8@W)oU=YU*/ɪZm|Z:\]'` B`8VYgKHx@:-"gQ.չJFTh?qG] 0 RyDD{8`aOhelTud+HtHT:4Cy 4# ]NDZj-N!+.5L#\L/=kZ NH.yPmXnZUJeĕeywzlUKґpa^NąPI@1*J \uſr d+\ՠ=p@qΎ Bգ~-#pMmFU#!l,"2-Tz"fyMEy(O…Έr;s #g03P=MR_y,ιx^FU*B .WUEUSU_o'Ov՜7VtNdȭDR}쮪V1#Qzk6{T&9&܂ s. mVINl o \9qQmE&%S9FPձD.tvEd4}t<4Im}pBKD:lq!FC@u N棉te>h@D9 `Þ˃!5}}Gց1ŞDUS:C)Sq'W1GMնDu%N\iUg)1F0O:US NR t^+7aK.QmgoP;uTI9F`hNDžϿ" |5Ey(Owy[ʘB)|ꋸ0ȿbb~!o-#r+vTo$&*6_kd uDJ9O"qϢ] TlU]_9~҆|"NN"gIũgD$^.|9wj!?q~gqP(# jſb4PQ5v?~69NZ6\S-e\HQO&J@h"RYM#]Qսq"!AZ#r rN&R7ܿ~"ҫd/ D$@U+̋v`p'TB (pv>TD'NFT tVUfH+&_L]9Nlq,pMop*Fzc'FRvy(.4Y9$Z#`7"CWkbnw pgy" {2 >S_!:L`"czMӵ0J-l Ef4QI{Na'5ĝo4OQ᤮7'5*C :FGZW9RWA'5NjЖ7] eNj3 0ԜT#9F2QYkʩxgoH-[i|[(WJQ 5d52Py ,ʨ\폈'˞_ba]~`0wP9I+"{q^Pr~UeۀTlVՉi ՃCY2O(E"Oӽ8ekRwTÎmpwYkrU{6DrSE5!j4`Q5`\h5+l~>\sj.(Oǝy=X?۾p5\XſnF6\DWx /9~r|*:N kk/݀}=.I":LNƽϢBr|-SD ]Ջ+!t~ќˈcr-q,5"@nťx)#U]/"j8tsPWr5n-g1GH*B>j01.d𼪾*Q?1Ey:W(ݽ c6\8վQ ե^;a}^ |Ȼ -Stpi j@7Ki,"q'AفqQ\sLh|C/j>sr$:Y֎k!:fliyWZ|}6 >Ω-F6, ބDc$/I0 U| nA-'"]ciKQ*sq -|'=cy#MPEg1D'K~~Hj^5{b["Q55ՏO$@!OU.9=mqNTaO$PI=ս0@F/ t--wCD1ʁKUPćPmzu~ïa9FT4tN7.E1i~\QWGӭ}+}4COTOÅe"MAXBꀞNEu{SMxXyWSQV=;е 8QUQ>oB3pwpaӠZsB$xjgoS MWp^>&? vLǫղH7.Ey.a1GHf.~P1@.pnWP$"/ԡfdEy(OƅO7GeBiyaxPpD -ȈDnQs p\] h lpoR9"2 Ve*8uT|FJho75 4SC|3"G'$$ri Uw3܆7x>6q:#'#1GHZTuN!r%PCkU&(5Cs3p;ȓDC,"͉Ey:5fbv?ʞ~DEKy0@Mwyk U?S""MEY\xc@[k%k=ܳzฐұQTWQdUu{k~i]ozi A]u6ҒBJ HQ5U?aV|yJD6uVi1л|=7)8m _S%#PꞸߢh42 8 )5(4^xsk":PEnQQk kN埍p7TʍX'/ xSR5iS59o<: wWU{^|S= ΨF:ZoR'M`]0W2nރp<!"wH?iQ~G^%K*fˍS+8A3qR)NE\D59r3̀7BCdUoڅ 4MmED'8ux43:+MO%bʴGC"OVƄWڱRLtTs d<S *VDڈ7=]Y|}V=hkU#U4}TG5nPѸӝp$m\˿Y*"_!ǵl"rn!~L=3gw70)PT3 jO޿Àh -8ѥNMjq+X;o[WY%f{^kcSm پ O~ŅF)1T-ZE+)Ed*5 Q3TJ?pNj@8u߈H0")g]M&JdUP-60ˆE/$I@vᵭqy:SZ8YilJ{"b/n鲺ܸ*ߑ6\ns{LV]&"-$U-S|kQJ(dI\ /g(}ίŭW } tШ " zF5 Dr*U5_N nZ*? \ Rh!FCB3;n/3À #;Q5RU95v>N*.ŕD p-wqF(B9&JaDGu5B h> 2wQ[hi+PiM?-}kjCmotRˀR\k'9FvjȫVBUq|w B` ۍ4@ XzTK9FaH؇ЊNB}wqUǿclz ; ,!@`:z & $$"! wL!,B'tS\5?Fwlْ2srNtݱ7;Ow>]EY[z أ'&zTD~ ؙ1N97{jUU2j%rzx=չzx6LdWrD*WDN ܫq]_ىꞟkᭁzDD.Uݩk.S=*.y {]v؊i؛SU?~kJ"aR^6Msª>_4ƥLdVi:+LwCtV~έfӅgC*.X8]ǎUOF!G/P4\DչdR#_}ޥqn]pMNުKcGL?l}-ώZ5VhWD2H*+ӎV Ol`5a ;v4G~y?`zf~8]UoJ$pWxſQR]x\JE+c^DoO$`nzxEO \ 8MfV'4[i]ɮ3ఞX:a7PY Gt}Laǎ}0:%XUᾍ5{98c?'Υ,[]L_ Rdr Ed0/x`nz8iMnVq\ުGL x@UlZ5ñQ&^IZ 3v|O0f=1خ?[k$"#~]:PRDy\ e nXG 5#?`M[Un/Ώ.:uOEdJi=sS~\^NۃF)j]c/o_  !(*k Js5"[AMzp.pB)_\/ ݤyڣV-m2A{>гdzh1;pеa" X}PD"r s.j)VƊDub-2,kMZx; ovx`xמYaORݏl6ع?wijj,?#_GTOR]lBe p'|Eչ-`tl5|48`NWT:D&s^M6 v3eM|z} rWJ4iELA~I[1*=Φ=_Qur:c ӃUEqQ- v-$.@ `+q.v"r0v&oz&Ky``t>62uSde?LRo~I'y*')^k0=u9J>*tDŎ uXss}WZk>,\36J({$k z4juēNumq÷OR]2Ǝ@d4LA:I h$%Bd*T=IuWTk0٢l ~&RN?O6* Dd֯aaGKQ! IDAT4?nOBut* 2`dt<a[2``r޷\̄[82k\)V`YHh;MDԹ^_v%PBbDO,H\T?B`DQmdV>w*״DfJT${ R5_Qur 1ӫ/e_Qz""{a=azItUn̎uK`+qUF!u_kep-"KO2Yx$`KOR] N2I88P\3UT(a[4Û";J9b挈L\3(~ږ6لm_o>+pdf&g?M纈l}?F;W/_l܊َj~[0eAѳQ=.\UT)wamlnwg2뚜X OR]ک,?~:ՓX@;" TRM]I~ wRyߓT?QR,Iy\+RNwfl+6+Yx+5]USՉ{V Rugm _O#rk禞y߃эܯ2(5d,HdW~F8y_٢ -3]v<Ꚅm.zFU_{.NrȒi\\ =9DcgBW fDLAΥ{8׿;ˎbՍysWTsr:XV0!*<-)٢ zk("2DD.Ƕ~I꿀&I$3[No#po="!2_5Slj噂 $u"'.eRI)|E9W%[aJUnY]E3bil&gFުNEpkgR @18X 2\wX9u}9w97UD^騞RDsJ)+ӬEm⁹~%"aQ$[`;U=nZN_o&?UEuM`s+fFXD4US};5?p+"Cb^1M2 o=Iub*wΥU\RNǖrz *\-?EnDhl[o8 W՛{: wdhvt +Tbv,-DZ8=kb0:kk nv>-zaOwz^OTsU}Xb7FeuQ""39T>I맻ևVW UE:`ixGe_ݱ8,S;7<v95٩x}7ws]sDzE#0ka~lTnZHe0 }DK,U  ؖc>q^G5ws 2 Tdո^)Ȝ=lTh)iԹ#'*dAutJ9sn*rz%Vx$7r(k&")V+LR6W:Ihow ?7V9XK7!4v׹J_[`?]#Te 2?2LRGkyjȊخ=IuUT)@L/<-ʙ٢ EbNrFZτZl:X1W) ܌}އ=Z [YS `I`~`r^8}wQ:.` yꜛf OuE94[0)r _#.QUu'U.X[S=VUTEulMTlB D8\Q=pe01Ymd8w`V`r>1HdN7vmz̋)9D(W L{rAA9Dd;lUq`3`GU}0Zdw*w0/\1+pk, ziwTd c!9د׸U}!W52= C>Z.fkcCQ57("g9`XIjۀhŀSUGu#S1WL`=g]RlMuKHR [`-hŸ }&7чjoՃ Bds Wp/qMnDF?VʜeU'zJB:3vz$0,2,aDR+ι>W؍1ӻdNA5Y[1 uNaZ$_wv1́I| "^ͺ)/]Yyr$U=Iu5j*T=IuU\(},Q:k`(f28䵴ع虾 V9Ud1`7+Dj2Vr^z"Kam=,f۹8R>RD:J)mէc -Jg m~l,I*@{~3nU(wP;|[!p5LA΢:IإI*jS9=IuWTsu)[ TmQife;8KL^Qw+m-mc`V%Od8Vznm;mid=`j,m9%آss'`ȗ3_QuեRN'rz*:ga*%xp= "sb+Q^ьIj1M)dDu``d#ш,1׸)dp5IF}Qb&=;]0:“TWުQKT@u`sh$,a= !W2m [ksyDo[K@X9םKou5lQ6.VBwrIQU]KO&b=-| ުS )y5|7lwb\>r}>T wZy}߃YK 揌F~|טDFgDF:U+@皒:F)b-^BamlL6*#"Dl `> IjlvځXCrDvLA~ 75IRT?iE܆H5&OdQVC*\%?/]sųR[ǢE8-m2vyb{RHMd&p7n; ޛX\=)@Է>|T}Ȋa=LekP$\D't+Ŷ\CU看(t08/exJ^ƶ?o{?[s_N+P= 1W܃#h|d0VY;LR֯$.܂HX,5:;cQ0z'su#[uL>&ToNTگZd"k" 'RjušvLAfĮL}jQ##P7p\ lkض.N:9,_Quιnr0WO/NR̀ι&v TR%Mi3%DA$dx́ cE"K T&RŹsuC)TVe0ݜ\Xy/&RY񷭽UmD6抉9SQ?Cd>,a[6z[I 5فIƨޟNP."Y*w8jXTɹ+9CD: IWA?&`ӨlQ ޮM۪ꦕ(TVbvH9cLAZIH`NRT7 tr $sMU rX?MGͿ3 qB@{(e2$@ӿ#_ lR,Op\1Eu4"CdLA~< CX/jhۅsw 2K:A~%#܏D4q.-:\CyoS8`+`rDVL!"׷. uyR\S4ι%"`7ymU}lQ.~3}3)̹&/E&g)D:00է+Gs"b]+I*1 *[ǹZ+ι#"Ӌȟma$ކgXO[e?AX;{ ThiU5C.ѩ2Y:xޔTuh\ԾxS/Dor8WK]s-CSŹs GD\ԛ*j)n1[ed[aߝ[ƼVd 0)|h"𷴈m?냻p0 /z6EƶʺZ!"ՕB4qVU\>L 쮪_'Քe/fb6RuMfVDho'S udI,"c|hT۲,06aŠ;zc ([~ X%2 ʹ+ι "KP*pe-&^ ,%VU~`ط,S_a SlT~)Pf.G$\vۏ$OR扪s*/UT#~\3٢-ʠăZ*-< LlYɾgm NO#r+":F> 0!2:#p"n=s+sU\s vs`jFB)J9=\ 'OfdվbmM lR,M-SQ3y K9იj+lDh+DDdi>E9E.% s5U\]ٱs'aUUx`}1_x1[&X kooMX w`wQZkG˦LMTw~.Vc/BdiFz9pa0&in"ñmQn8Ws/[-QT٢l\,3}O)Y&;Na(5;"kgRXVD>z#2]E)D|H* \,9|E9WWDdiπ)I(>M\KMQ٢Q뙁Sd r47z_I*cl sŌqۈH<_@|DM!f?U$չU\][88:fy`m-T٢m5frRNI6&SyV2xA =y \=k#p*V|)@:gdCρᨎN'& 27z:GdRD OTs5OD~RY/fƢd(p%V!86G)'Uhi-v]&)԰2Ɂ8`r^ÖAKd7֎jnzst3Ͱsѕm:A58˰.KwVfvMou4IW$XQz`qlQg{> ޺2\Eu qI*DT/VIWTZ`Ese%ՁS=Iug,UYm4SB +S$ăZ bh`"W h%InCY:Wݴ;-7WwMou9zVUK>-N٨9b*dJVK,}Df;W[T@;V(i7|VTY8ظ+&@n"`+Q7m+YiMun*s.U"2DD.Du 'Sbw^eDJXyfWUR Ē0IؠnTP5v=4=p"#2[5O2ZX!75ERXow4D9YW UDjm=:`(vcjT2YKn6+9J3o#,Dh>Db;]\:Չi\ιTY0ސ|T)[e")J9Ak-m2h^n22Y 9;o9q +檏Ӏ5X9#/" `g^)E\U\Dh_T'oyڷJ9}X(`ZFfrt( oB{~  d p7I *K\5?vȮHܨ~lAeAM[]S~ R,5_Qu%FDf.UUJ6-Zb1{rn16[1`{ 2LRG2{0p,0g7W~՟D6Vӣ (`uTM'g;šgSx 蜫y"Ib6$r<3VhiD_wћuSe r(pI7e oTiT~/uY'(Rz'pB0,peh%2Œ^~;zU\Mg.21JUOTr:ӽσـE![VF{ŊDF,,SDdO9U Q 1W<} K4z&;NN>w$U E޹_\{~vs> KRH:.lQǶzػd[-m1gUmoոdd "y`r^oI>:`Oz4Xr3p#;q`Ȩۣzs:Af^)E\CD9/z^|~ީfdrpP@ӰM]hiAXQ#۶6w)@``;`r^v%.$ŶoK8U+:7~ XIUV\ )tb)'ka}m/LS;"JbԢI*ti:%%43vnmD<ZT/Ŏt83hjtOp6O;8ܛv||?kfa~6ˆ$\_n솉}^͘7}vDgomX~;_Eowg|~i}Ď|L쨱`gTNd(c8}ȠuyعC9t3)Ts$f:go;Hŧ@UҸqc&|E2矙//}NJٸq#u֥yܣl[k㏓5Iҵ+>v̟ƞ5-Gwbͬ߶٫f3uT}ښ>ԬZ˻^)¡-JFrwOx7xwXpsiPA^O_+]Yt|yiKtmޕ(6qyG:]v'13Gڹ~/eWf{ on *yy0w.|-'G3y իx  __|'|2<$rssy;z47o.Q'V-N:T^=u ++}^ˇϗZfzڵoS>[tQ/̈Q{}_|饌xٰfV[sxqfƨQ|Ǽ0r$u5< ?~OkCm/~OyyyIi' 6,2xn /ڞZqe?]FrTͨKy+qbVͬJZJQ[tKFkc0~Nz$&>1]tq0c ~)սI[C8Ħ*HRV,_?w*&5j֤W>1G~ţ.sau1ڵ\xӮ5yJjqwsPntܹ@\?zlZ9]5ZӮa;t{Px댷8ccGVf>_~|ceV s~g2PVs#2M&/ k6)(Ko+?2zARt8[vnZe^x,ecώڏyeLrк^kNhw9urըRg:h㢸&WØ1sի^{Anm`օ>pAX7cܴ-Sa@xnHӧ{v5jGE(:]sZhƍ L:?G}4NW[Rvx}H{s]:(=\/Ϝs"^oװ/}St_~H>[YLK}7}v˺_k]5Q6Gxڛzei5<; #Y"@O{c)ny7#^2a7[\Zq\^f8ڸ-Y>^x/ֺ5\qvVٳ]}]ХKRF""ۯzg.ᇄӏ?27<炧Jzԫ_Ⴄ֯~e[JTu.97~m1s|}C& [e'~/)ЬV eo[6n6^O'+3+|%7߄-?'6 IDATovR۶u#"0Rn6m9Zo{YbF,U^ GϞɉPZR!s@5kmb1{_0[>%o`5Ed77Վ׬Z-7 ;vZRҝwIȞ{/" Rn ٛlEzϟ7[̟;D9TmJrޞkײsFawVC,Y{i{ 7ٿx[6~z}:s.P;vBI?YsE)ꍤgd@K&&"@UE*U|sK÷Nn<dzgLJ_fzI9^xqj:"-޸sE*}\;sMyY[vvV혧 K ԩV'5reIꥦ/C:v9))l~i'>MժU~qۚaCi׎3ztR!׺^k|טf 9ҬJՇ<^*Um9ʊ-3ihe%zWݸƍdI"U)Su=|U6n[nU+WF(˗{Điw֤iSi|g>cI &Z3{HqYM;{FvLJf/ T{ٻmJ4٘浼S:}Vر9=+uw4B>HHJ4i҄[B# 5ENoO$ ?/ju$6o\^IiW]v%Q&-rrhݦMVk׮4ӠFW t~Z;K};yɉkOP);uxᆅ_[<(S&+m*x޽⋡vN-wD*R*׏yM72x'ˋZfvx۶m 1\qv}lx)1+((` ۶6(imWD#Ğ[qpʾO&:9ך=6x;]FPߤ̄VaBڰ{,5}KE`CaD@UJ%77oNQM5cV֨YF򟧞M6yڵDYG}@fF4!'' ࢋؿS'oI-y_0زk]cW3yG{7 BII۟O쮂]h{Xylٹ[Pti2N%ǵ*ŰOa}hTz%װ7kwiȻqKJFe?;w؎vVӇ;6سI[*Xn.| 2w̄q#,['ëB^0a `32a nDCΝ.UV :˷9o+:>{wұڭ>}ܦ5o\4M6%#hDu۶md+yޯ+1Glrbo欞9]5 2 s#JVIօLO4bhvm qve{?ݘU)7yy<䓞s;͔)LGLߍEɾ7ud~u΄q?>Ջ=֠Qգ~GqzFHB&u^.*hԤ9ڏE,?weݖJFf0%_5+`.>KH g{;6Uֺ)ewa^$a T\}]Ϩ:} <سUM&M[1.ڷѣ=kE7_ϰ5kkfgsPVLZ'yܗ}Ƣ cwei IV?Yt]5YM6 ?&>ĨmK=q}dA^y)6U-oJQB ٣/8?;^5嗲[jɩJ(0RSOy]YytiCnvgZiðo{`ݱcC bQk.ϹcO?O߾dO3V-><>O0֭=QYުW% >(^|yϨG:$>wM3zUh4bM;6mtۣ[]:o}}3vۣG92F__0i˧Ӓ,= cDob=pg23aXð1cӷH YDb"RƼW DNK7] |LK.:\}ŢUp\4p ˖.eŬ_ Ӳe6m⊋/f1w堃hs-rrxWg%,\,gG6>d/X}KFx&8~&}%&n77l`/s5nP.2g[9ԨRz?&#bۿ>MݞիTg8f1X1#^r;1Ϲ'=>Pc7=tPt_/[[vnz3:--q_/dy(ns##f(ǖ-nfIT .s'}89OAW_|'WZ;; ?ϻEff&۴ȣO߾aԍ6pX*эx9ں#G쮯W|kEoxGyO`Xz}^:"nUOg/ә+grma ܫe/\9"'hY&xs_j*jQz^eG >ۀ݂˻^SC${^N| ~;4 Tff4o p睰re|]?< ?ٺ}WUDUI1GsՠAESr7O:{ϷE ջ7_tF͚o۶'/3yҤR#O+9SS]bo* /I GU~E:Fq%>.ZYJuns#>?;oj}fT>730&>٫f|reVEPz(H}KNy[҄a 0_<u|~ss˺mh߰=wfаykqŇWt6ē5z붕MF;7ٖW{ZJŷnu| dgsU)wkyv[ڮ]uc}j>۷é?_WD(PZ}ZƟ-3T^^p'3ud8{wZаQ#֭͛Yv-+/gw߱=tRXt)twr ݗߟ R^=ٴa'ޓ4*~3')͛N4yfRyyҖ$]$øw&?-#֙j=Gd#gYY|s3cW yy5k݄;7˾ZsB84nB&*ŚkX)Koa!N:9=ɩC Qm`ݶuY=i˧WuoѝU-LY2]nSN4f^&/kl^9K6- 'tZy00{l6nU29.H?͛CϞв%4hචٰMGliW?OMO[^0޲݂FTED*٫ٺ5[D/c<]el2^u^ropr(rVdenIh"Gr.)RYo5#")""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZ1T2S4NHjhDUDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+UR)k#\;""#\T}I/ TE$~~7wk_5.PgQ.Gkʳ#g5>׳p kyD,ώ"B+|ȁ(uc NB{t3{qT,sۂǀ!>׳(HLTrpѮR׈׫˧;"""eDɔDD7B{C$R#+HOH<,TwD$Ŀ|-1Mԁi QQDupr@UDvSmÜG*YK$7;y[>)' TE9 ̍PかǿS"H}C},p(`WL\Fv8:>998OfX6p_Kk\ߏ"/@_~}྾޸=e;q߲6 xzm`ޯ/K`ֵ){8hG? +}^M_q,xPO9.WpS'2mG65{=!J{h{7s#""b+E/zUy_[OQQ.rho*Bk-2B,F>z3u`Z Di7>ϰpz~,7N0%i@uΏ®8x^}"(}caj}Xmz b~dE1O[,p`2GwO{e&Y'qW^% ->W/vכ}]ԽLID*\_b7NFYz#JblF ԏ[ɯ@g( gJyҺb'x}{/6X'x{\ 5 8zq#E)QN}"3'pfIH"H%%A>e~o\4een8xEsp.IML"ow'Xwnb}}ea%nD7 3ڧL.1>[" ""qI"BɔL4†@{ZƯnu [sFt5럠O>G{pucc:"oO ##c!˧e%S*| R]8X_~ɔ"[S{iu˷pO/"KTcU [Bds~ɔg:;- S-P&,wO"Q2%R2J҈즺㶦 %aW.Pg.ᷰ)V( IDATs$o#Lg <7IUѪS "\;$d vn+o-k* F州6`2pkiO3ymWBUōGXvGtw"<ͼX)Ph s*"oAnMDNTtI*K~|s-\؟_:8J_o8J۔ _ Ե >>;[6:Gl᾿|zznzy8pH"%"R 5%Sn$.ܨhFy˛u} ~)on4;RP%jik[OY(}P+nRi1nv{pY)*Pm"geM~A?2ksB TED S@"| Eq|"O=78r=>֔X@ᴊr=7z8؃hPxNU~R"+"RhꯈTb{ ̵ 6m}&Gnpҟ60U[\ Nש""KkTEDJn^H|D v,Y[Fp};{VM#f .[re8nQ2GN7RgÏ=qyDbwGCp!˷#S5XH /~ .qM<'ҩ{(b R#Mq()ҍ>uW^]m73`&=DhꯈH{n\PR)V_vN%w?CQK uܔh{;7}WDDRAHnU'ѽS `TM|l E2Qp#pQ][z= vt.Ymo9Rp">>s|uRAB=Jܕ09;p,lECKpCgKQٸQ )q6>fqɣz㦮EߧF{p)&$\pνuF_g[0IJentMC?=n8ւmp{'RC[$`*gp#S׫mysY^a""XLtb }Tœ7%%mj#I|\+}ObeRTwDdwNHjhDUD$)>&| .cG&CHYURۆێ%}FTED0nmN`5>,B[˫S"""" ""qC#ʸ/""""M)mq۲H$][\rƩH@UD$)N|OLEDDD$Z*"_=UI""qk4WDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+ TEDDDDD$(P@UDDDDDDҊUI+UR݁Jn,8՝IC RIcMuDDDDDDDhꯈ""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""VHZQ*""""""iE""""""V""{0tskTGsg/k۩쏈TZ>H%gi T:Z+6BN/n/]$VƘONfHrcN6OeDDrЈ1@㽀EV6rH0dN?""DkTEB3<@ ^k5VSFD$XhHFTEB2dFM4ZP $""""I@UD*cLܠUW"KeGDDP*"1:6pbAgXk?LMDDDD$4Hy=՝Hcwwtz p۬t%0ih[kX pn/˺Z`N(54:̀Z`vn2//N@ s vOleY3x_֕w$ޣ%m]{HS*"i ?gch/[ m2ƌC{ :uUcL&n~!՞އ? 3 -dcL@.& 1f0xZ9{$1^ {p&..L'?DƘ߁g}Ƙ{Ntwg*aK'19kmwoZkη=< Rm1M1TౠSZkc~T3| j=:Nd}=Z@+N *py駬݁UIk_&{^ m9ζoT?u1W[k_RnPmcL Z^1tNH/F"|`Z{[  xhh.3Z;5{x'cH_#8.Ʋ{WcN)}HߎFZD T1mpAv>'S 3Ts6.NpsvR{d}kcz~^ZDS}?s6tKp}LG)nHߵ\n8 "RiUI[Ƙ}xE@v Rq\ x4:038ήgcXrpF0o5ƜKnj1_P2H]|EZr)0sD,(G=qԿ_ Zy$29Ƙ:b{R(}w=:E)rƘkyuƘ&qަ0H-~13{$S>3 >kв""5"1]pC<\ΠqSsG1GXkkYkXkONCƘC0tZpߡ@km[kmKܨ+>u0hhͱ6' h`ZkD`m܈TO} mƸ P ucLS{pS;@mkiǭnmmk=9YݜT'638p<<8pAi(:B޳ ZӇkko o.pW˅PZ{v@x/ZZ[/9'rka^eEDvk TE$ktn&pX n1FXk-hn8` T+ٹ8Z;58Yvmi-@wkc!}{gH c .h~9ZZKh@^ Z^ڇXk /Zk֮ sZkkmmk8`\ _@cQ> TkS#0-L vT5 "kMSuq?o; /Zk󭵣q3"Ū VLiA3qED$M)Pt2_F?ې19piЩU]~t18n?kZG``~Ssg}"~e4gx3Ƅ­O Z&rwx4j!>roqGDDʑUIwqmCA߉RklXשΰ}$z_ ~4MpY,V[ų^,(/;F@4 S&4PKfn[p,7];Mfqc6 ȯS,""!H:.qun{>~g7sf#c{["EEڢ+0EZ uPxꗡRƘƘƘ'1c&c7~**V8jpX=Q l;0%!Q!u}y,__@ϖǺ@~n $[4}TE.j4Q7^e I4O1GF:c.Ƙq%x=frO<:gƘ'Uɶ8JƘpko!u} G];<2:%g?e* >e@`vK@u, rYt=wQmHS*"Mf;.g־b jW\z6R^q6ޭYe-n҈)OBG .#PqƘ3(&Q֮387m)&sI-NHP*"iZZ`e$#_1[\إV enyBVI1k9}67 `-n)Ƙq31kI*7/_1pӂNN Z[!hB 9XDDRCwl$I@Z8Z |V7T_r2f }[kIIOR>Aɍ#Obp ?}ֺp${#nkz~y Sϐ22Scn%"TD<1־Y VQ<)X l{pu1U!+x Tqnnpz@5xd@`SՒBhBp3Dz^uGDDQ_ⲯr 76Ch`_N7*Nx,W@q;t0@T'RRpS/Nhv`qb~~&ԢKBm#lRDD*"RaXkGQ2XL0ԉP-x+ ଲ]֮Y1sQ1eV6t:$ps/cL{hXkfֳKouz)@àiʭ 9nKƘ1%}ٖGDd@UD*@z1`nd5\bCqR; ~21)!DZz=Lb\-eZ;?zgֵFjSj1g Yks~f1ԈH M8l)JHT8ڑVKZkNFF>bf\UA]{ V1$Wg>c|cś7+:7~g?@5cML0Χʔ7.( Vԟ/S*"R Xoz0>Lz9ބBcŇ11"ç,fMV.$z`1_]cLkcc2f:/cJ'kiyx2pj[ۅwl$u^_c+`x>uZ-^9~sucnFwsc[CZc cLcL< DDv;+"@6)'ndvKc/́׌1p#nˀ@S \恞v1*i9bYBVnO\Ä́fHfPEXco@6n Mݍgwav$@H! Q>7PDVY}}",{d?Sԭ۷{;fn-VWԩOEU1w]??Ow<xsH-P+U!d NocK֤fn?,#/"?~N pjqgAUq4AY jEw FEu퓨Ã>/41diT?"}jם-7I``.v>&5*[00qpPɶd[=G˛]}Jq v_? <ܡ]v]%VƖmste;4WTq34RTq&H888`888V888:888moO88}XMR ؞8TUqOp8R\Quq/q;[tppqzWTqos,0lb[MGUS[-8ӳZqqq/qqqi+\Quqqq WTqqqUqqqpEqqqi+\Quqqq WTqqqUqqqjqYpA>QVTGD 3TV8N@T28qfꜞq "ӣUVTGDv Tul+qUqځKK+"wVջ{N,/!"Qֻ~*"$ϳTV88ר:  SDn[,V.s'o-qqZ+uUZ-88qvd`J06|"{qq>[TiG^W՗ެ'g&uv}Qqqf㊪8}U |x=KN3ns""K H>+"?QB/pߪ'S}T%e:.0OUW.eG#mEs/"Gc| XT&&U0*`m:@DsUlS \'tJ7oe6 [sn}s%"WbsjR,"a,`hȚOz#9`:eNNWy%d K *!<}`l|Z{܎|WU?7~Hy\HU_l$c+b|gR<'3Uj8+eL$"Y1)Ϊom+d"]Oz2fzWx',՝_ ΦߡCn"#UY )!X`?`_ UTvL2Fy,! `c RwAwR'?c` > \~G_cU%dqDd|75zv*JtjeuG{ظfl8N[ኪ8}J}vAA+n|Z^U_T՝؅uWϻMUxWUcnai/>LPTƊsZN jx~ǫ;iоUu_5ͣۨ [zn5l Z⃢gUiU}4~/"G@""󩽾&M6Us5_՜`eaGu ا0+3UDmkc3gݪ:';[UO֭|HpΏk/S+in^@K5qm\QuO!"+U&-'f8KU)1jʵ |lV*R7^ \3֩6$X<U1\j% ч?eDUUqኪ8~"BFa-R CMTuzyJy9e:PA`Uu6)#omse-u6Z&IلhTxp憞/ ܍0XțqSU)e<%"GjjGEd^ RV /Ly8siv{2!k>p, h\N_+Yيļ. qhr\u=U8c4 "xUŠq :QQMDdR4S#>j-3U2RQ#D]&#"Wl&} wͧc/"@T\`bSPտȵ'C`{ {텮n+akK%UU-}sg0+eHýj\lB%7n]vIC8k8N!*eI 89HUK3C=hYpmKDdKLԮʢ E t`JdwB7kN",?4`8=_9 l+M-IuU* |ហ J*tѠ˸9qOSCӯUqVኪ8}EDdVuS5o*ֵxNEa9F:Vsnwܵ78S0DUmޥ98Sugl?ݔŰ(ҟSzBcWu{jˤ=MVTU- );.񶫔K%&8tvp⊪8)!07.3"7GN7T&v׈e%=?=! ~X_|w|R|:YSU_A\&"[f Ķ:@maZrTNy,ƶ UգKmTf ؇a0:ޛq/㊪85[-CTVL |-x^t#يȈ`+C)%vJ lysPD7,IaًCʶ.9S3f = =-O!, cmb_s{[Ty2"-7[#YDU,Fۆ`SqOnf8]Ԯ:@DTذdI#VJԵoRp:[6{U?65, 䙫 M*i,z٪-.ׄރX,#5H!8gqEq>K6(kÜrQs/0Pn+o e'DdE:-ǭ^%0褩ޖ 0fn*nj/zcDd3̵:IUM-M'X6Hׁ?H3?]Sc/Vת8N3qEqϩ~ADV)wfV*\Mm/H"2,Oq]K֫ܭe}/waEdV 8L\QuOӀ?EY= x': ^eUm1szڡŁEvٹJZ2}Lƶ -","2QDZw-㑂}sEo*SC,%wWۈ;,+"UO*Yi64>e\QQ+"g~EdlYN;&Nj:y!"#EdI8}\QugA -"51C`uplPԸO=ZWbiAYےeupßDd>I@Ge޶:8x::$"_/RDdQ\L֝4c/`MD>QLlc#"q*y96Zaθ;n'"K7RUr(kil2-^_ݓQ롪/`Qn")h>"2EDa o_ ~3S؆a[T98STKsDsz1GY OiT`mH#1K%鴖4Q!5r=| رU}^DC'KvI`[4GO~x )VU""bh+T;i/|hesbk?HD賗=(׫xw9֨0f"nz%8΂:@S1/c抛WjL)F6^%Ps =|IU(flVFU_Qm5wEy>-`eU&w=4˸#?훺6<_{sŖp,'gc-U=Dʄ[&`˰mH^͏C Δǃ }T-k[j]?""U=s?΁x 8DϣsT0`]̫cfjc/u6s@-q" `%I}ffo""07ȥ0 ,MT9[(l{5,p2ؼ S+">֠sUKX-dU ,Y_cg˶`=s]s[ǻwY X;Ȳ4&V$ϓnֈȺtZꗀCw=̚$st*07T+1W XŀWs8΂+8m@wUqq j9888m+888N[ኪ888V888:888m+888N[188|?lNqq~888888:888m+888N[ኪ888V888:888m+888N[ኪ888V888Z-8` "GYV8)"+02WR leݬS[%8UqZDYϨ꜊m NWՙݓΩYWTv<pEX{΀+OqEqvR`x%๲Eϒv_4i."8H8RV!"%wUV8}Y%}3UV8ר:ӧ_YI ^ JVc^k8ӧyk8M-IDdf5jё884 WTsszZkrqq+)Ddp1d#c.EU5R988Uq "(p%U=o3888}\leOvQۻ":Q4AU-;* 0 WU)YwmڣTD^Sǚ~O#"+`yFLRg[*X 8` 6WTէZ*TADVâ?l"2X;6pޠZ=d4vyO3Fr!-[_t:>6'} Em5RuthL׀Tuzqt_ U>O_x8)q  i3Koc{Pu磴OEcL8-oR͐p,b9v,| W)ؾK]R3 < )nyذ<=ǣgD2KLӎI;ÒϏ o`Qa` QgWr"_>g(4ok(W:c78:B`-X%~ NU_5(8<~NDg6(06^ n./Kfd;?nEe߬8%2C1Խ ' ߄o]+)fΗ:}ƮoGlG;bC, 8|9z8XD~Ts=-Z_D U ub7 8.޺OFǗ%5fRP٫u_HQ)4sԺ.HLUJMBA=-X`>ȨE-Ivњ<~%EYo`ޝ#7DDʇ:w4%*(k(`/,2SB^U=PT2UuqVyzgY!TEjcF`N{JQVmՂ\>nӿpEqv$0KUjcdf\bW]zarRi\2E;~ \==ŧ]_vu"O`2{5YVF/^'R;Sſ?rۺ2Y^Wߗ,KޯyX ,nvĂ_L]-^~^~p=OjHn)qWTi`mGvvR[6#%ާ.O7)5BYH+IYMˎC3I*3H-'h)}8 WTI9rffR!DUT : 1euے0:Z"PUX̣|]$}hEFtg J|.*gYCKǾ@wVthV vuzǴ봻/!'"'`[_uuPͧ/K9NU_3 6~;Sy'uY pEqEU+ϥ?SV+ýpr< S%V2溕 jTez"M'~0YPb4TuBwǮRZK۫*_Vf̕ODN:[p)՛+oőEjl_"T;n\S?i=;*s۱y7OuQEdcl+ݰui%0#}EAgUqR.] Tfd#Ơ{39XUӈtT?l_\#+si&=]qͶ犪c769nإUoYjn;8ʚ 쨪hswCk; ꄈ,[WջJT/ 7"VxlK//"/߫8}8N/Ў,1zjvA SaޠCQ]\DSRdUo}kjuD٭JMwzcQeVl\m&"T5}StD.C*G2iy-n+Bmzw(;~V *ht|x%UDЍ`An>c("J7v>Evqbn^caIQj!*e~j]FJZ^te*jH#Ve9)*eC_qUC$p-_ܦ7n?jT."]\P 0j/|PsCDۄnAr}eAڂǸ8NCU%_YdYU{ ~#3BЌ҈:}\rt!A%eUDoT%QiQ)"Rwd4jX^](kF dSJ;kEDdM:+TGr? \T&EUc Uvݴ.w!"RWA Fv k-W("IOR/==^\ "iD }<9>FD,[9J:>CS}TT~QN)+ i\QuOUV'(RAɨ}E&,qWՙ{ZlA.pk89BDA1mjp{ `帍Z@B)1r_LDknԞ_^"\Njy7Z+Yz_Z6 j]s x`JԺDZ;(=M6v{ydȊAؚ:c6`>8>ZDSDd=_QS uxw杈Aky=K^PH_Dv\yҪz1(kpu$" Dk9Eȡy/DdZ-d GENR S\偂 r6Zqx0eeba9X{XP˪zVb? s'G_0+OW5"r&.lx -Fq:N;=<ߏX4?>LlVD¬E1ocؼX}lQU5ihݠLCD:0{p<USp]ǮBz9ȷ<8 lO톶aR@sKjV;NQUOZ!T7sK D]bb JƝbIs*cyoZEK_QWJ s8|PvI{hYJˌ)|eSʴ}-Ԝ[e1 HL-|;A> ,[rL` o%4wڼ7[+|Ǐ޺](:׊ҋKFj,hUQ} .. `wLw*ϰ'Kiop}v=~[D?9E sof\Ock lQt\7fO^_Q=TuvIqo>]k˄TUV9>8s߿?hL-q_uo#oa[ M$; 4af𤪖^;֛5^ÂΌk{:/l ܢ=AIzX撘 pV]?;s^*0Eu ;ny^,eNnP"7dy> Stչ1UKGIa疘;̂6ru%ʮY%bl[؋WU%!-c&X{7pEi6Uqjh:8΂BGP]"888~UqqqpEqqqi+\Quqqq WTqqqUqqq𨿎8w-:n8.>888N[ᮿ888N[ኪ888V888:888m+888N[ኪ888V888:888m+888N[188UӁzFAS!.UcOQ!Um+q3q~,٠l] qY/ʺBU_>XA3sêj#jvUݪ"2X8}S/|  רNqθEqv(`*D1FH)DdQ ~|PUlB[g'|J"lh4Um !"h^Jyi>Fqq"KqڂFi&k: mxLDmB{8k8NUqڑg1Wɘ0EDDd7U7s""Q%M>HY"2@UaxwZ)kt$ӆ[DdaUj9\ULmg YIUgN`cLPuRջLYROWI"rMyU]2,qbϬ$"{Z<:[UL>^ طrpbTŮ6sUu~TPl88o&Fĩ\d8 4ZzЬq㮿NEUțu"N݄gڇu6 "#LK׫ PD> l, ;ݓv z*E}Xd,ٮ`6h7Kb۹_~&3Iެ_)Gj=|d_DAoJu[bԲ2uK}Y6JQo\^ό>;AEe\PnP5{LyJ康0ehNN˜O5z?}`%2% aOM~,^ͣB5\)y䩯%wu;W.i NS(eac9 k6"rqnìX8QD. օ$X hP|i4*֠,UqbIqVĔޢG~py`ps2"rp#Ĭ00[YUۋ ٟk#"n& ND.BCK&"Wزu"RڋED‚lP`L+SEoEE1%)(o%&ņb//98:p#/nY$cTIT$}xQ9f,`7 se] {8γU8!~ {p`Fr܀ə10Wu0vfplu\E@:ĺN߱_c֑a{\# wADdIqyQ.fr=1ED2ɞ ܋ױb:\8NDDU 6)`4cWl+QS0B` 1e)}od]5f߀"ݻb/5YBW~Kp H5܃Y':`2KU"D3>w*> x`TKt4ڈwgC=׈0O>]`K&b㔽$ !" ]{bc4'b/2vÂBڤSo%Z0v+ӛ%\^РIwo6=Z/ڌrl =:-wŬdS~a<.L[{ب<ðηD7侉|u]raY?%nkP~l`Vep:\`ӂSuz%sWO7 *9;&yt/&@pq:A?]u݋ ˩Dr礍'm70)@ѹ֞OO:e7^Nm0FG'?)?8;0YsݓvH-NM[fMey?zTVTU$4nQxNRT@ nJ/K(fFK7i5SQ,je++s<`lJtsATQÛJ>ԙLbPQ HTswETgu.΋T;5~kθՠ*YJɤ({{-){ %nK/) t- ^{?:\.͚U2("TZNDFbA62Zۄ(_]'{r1rN {ؓȢ_S֧\||/ş,'mTgEYT\3w>ݠs1nj0PH];"e]/7ADdƛ1rwQ}# 墈@84lxV\4ԮVU/.&ٹcT9K'ǛUqWT~T ؃`x_"HׯFDJ3C$̂#QU׬ <-rD횪?ۍV7}0~DU/v]9u+Vٖ 43; s{[Kջ/h3U']ӚwNYʐlt=6Dmۍez.6HQT~[" (BUwj[em""sլJ;ڵHQPUn\GOTFls!)!j-2 ֓f)IU?XeV{IDATQ-}jy͋R"9BU[FH#dYb=Ifm(tUrbloQB휻h4溘wN򸬂2̵jC$&?j},erB%m>ZJ7VS;UC7=ޒ}̣60ܨ;N:NDx ܂= e[l|PD>~@^~lF־6lsڮR7ǞFȈzsx)qY$VT9;֜"r ~FU}we4)oH.RCE#JU/"ƋȰ.(it/"Ǫܜ;`2NoZq;^W(Pq^ }eUM;kR\/x=R( SyVm鳸89"/,a%NVqSo rCbdg cٽs"Oה[]!un)ݐ` O#1ט`A-g2q ˘%JLkF$&1_WuUbYVOfDMA!uRNn]RTٚe1{aCu%G&,UDz<_主Ky5%B wz{{koB=% WT|8dڳ.ղVpV=,=fWUeH]8>*/bJQ;tu j9ɛyTi]h# 58DQ箈^JZlF:o[%Mk최7\QuTup܊~OTu| "GJj]؞s]᜼EE?f0Bz x?#S:?"YS): h  bEn|[V)VKa/_v[X"M7E/"5"K(5J*t~˖U{;{PzM$eq:8%PKE~ 0smsޱ<ɡ7<ӂCUg>XP:e>S\Umf4P5cED*[qy]Uuǿ$> $B@? HQT"CU)`WDF:$i-3jѫɨv-/"7 9PU!O3g Z0RL͂`TA=doѨ ",/i4s@?_+3amF;euۨ[5Ѹg,8l!j7 _f] 03iZWj圌)*tqP>IdޡPLUQ3>[ޑ}}W{UI5+?{ƳxSIyVl}o4-PTMM3;83 NO叔lK-$uzET46g]#=ybz]!]bD$W,)SJ29.fZvLYn+욜L߻d]1Μ.3k|l gafN@v3 [ n5T5*mUnɾm. WI;Jn(*SMsG{(^MJI\r).w4?d/K;,<]%m\>()?2$4:@^ be۬M\}ʔfE79v$WӸ1v)'/4N+ϝ&EY/&OL(uѸgEE~?`H4l!pMai <`}oyo|Ni tu>BQ !`f3p%)PJlVU\~f+!ݒh"GpaxjmܬL/ u>VOeܔO4Q)S~`IV5Kf-Jrr9EJDBliiXnkHڠj_&KfGzIZWJ,3M}|AW Bg*IMSuIpAdg~SڹG$m+騲cI1$}*fvξTA0FM:G$O'`cf6"7xtܢ ǷYهpE~o~5ՙDc> L)>`yV`:{ܗn[{"mʟ? $:ϒB+Zr cKxʨ5< ܗ_٢gc`|V~UY*;IkZo+;]RB8Gwh ʭ]xOo]`b6[ }iypSAޭ@7~JݐIYhRnn6Z6]dXRњ{OG-\\VRQMl?<+3ըX 1+7C7}d geuE5st>^\Rf %~OZm]ؿ6!)6~G6Ohr} WOd.ɂ|Iq󱨤>J~VkZzGeoJ;3އ[޼Zw}LQM휒,_/~CNmk۹xwgJ61m()-M}Aod".km\مz60 F,ffxC%%3)<='WKmZZRT5f6_ (˳\!]#;VBoAc١?kfˀ,8R4x _e,3nN*9zj21?Gq+b7qce竭jּW /{7|efvEŶ}3z!anּ3ʟa#3~  ->/-IZtj㚒+V*o+KZ |ܼ8< >A&G5јً>~$m|z7IWXVfH~7ҍhҘmE̖J:F?Am^I` Ǩ y_1 `I>S,Mh6jI$# \|3 @C&TTV}(!R܄v\, & l݀+y:'4oO<>=(/񉊝TQ)x;"k1j`FBUde$(|'F\?_3O C;rݛ,J̰_Dᑎ[7_+9klxw B܄+В& (Y4:ݐ3>Hќ7ps私g;1A"1]\,4AcXQ Ff0,K5$H_ 8fϗYWy'pZ9f*le% \V:p0AtPTQ]GBQ$՛/IIZfٝx.'AA$}ZbI//WИ+fHAtPTʗDt $]$O;,_y@-"D<}AМxUK] 6}<=M?MN4 e;]G9` Na% 2&HO ䷺8f P+Յ< ْAAPT18}Z(wMчu<p %}TҐI-=  `i1G5.1U*ԩ)k4) 90Ve#+ۛYnAA=cISyҹ=SDt`B⋁S`26*2AAF(ENfv O>AN2s;^AA)BQ 4 image/svg+xml PUBsfasdfasdf IPython Kernel ROUTER ROUTER PUB - Kernel raw_input - Requests to kernel - Kernel output broadcast - Request/Reply direction Front-end Kernel Proxy DEAL SUB DEAL Front-end Kernel Proxy SUB DEAL Front-end Kernel Proxy SUB DEAL Q W E R T Y U I O P A S D F G H J K L Z X C V B N M 1 2 3 4 5 6 7 8 9 0 ENTER Q W E R T Y U I O P A S D F G H J K L Z X C V B N M 1 2 3 4 5 6 7 8 9 0 ENTER Q W E R T Y U I O P A S D F G H J K L Z X C V B N M 1 2 3 4 5 6 7 8 9 0 ENTER ipyparallel-7.1.0/docs/source/reference/figs/hbfade.png000066400000000000000000001132321412533144600230770ustar00rootroot00000000000000PNG  IHDRN0*tEXtSoftwareAdobe ImageReadyqe<vd24`0LN> `c0  `0 &0`01 `0 `0 `c0  `0 &0`01 `0 `0 `c0  `0L` `01 `0 `0 `c0  `0L` HӶp8-\F싞 SX,3 F8?? |u++V"p9?մ->[`c0 ^ G  mom H^qOж=@f 3 Fcnδ!SY0Ν??ggG.XuO> x\^Z#u|L` aY I<>{vv̙ oیfCm_}F?}wm٩37`^}}R}ⳋmq; ohxxqiqq*04Ja<%T/1$qa&.b^ҰucO۫\&0Ca@-=/ F% ze%i%ϗkDC{G/:&mO~O&@ W|sPOGzCg 12:zC`0zȽa}!~R#_;߲b$bo;NDӯ~)yaݛn7/_y7^il>?7'_}! .l'ރ̕/yKxO-ߓ Pn;xr0xB.=wgںMqqaY(9ޞT_ۿݸߟ+c 0䋪}?z|{>O $?PT շ޲ȳ< _" ? ӓxp6D(~_;25)|;G`( fl* W\J-g^_]Yu 'A^.!+2?n0 ߃GOы}ӳq"_ys{>!'&KKՕe'j.Aj/ZcG&yjy::Աg2YKeO cM/sw_׆mG4㽟,Y1&CGCUIe L5&|@TY#  W-/G!2 Ôh+/G`ovK |֖ߘ`0 %S#Ngk2m>g9@M;V7w%`lo{<0Rx @ͷ/ exSخhZWSuvjxRDZOO\.:Ӯzݚ)&N=x/:`ܠ(OR0&8r^Uh2Ipm &|%Tj#/cB1@wb(݃ׯu.ԡCݝfd[[CccO}ƨ#=IF 2"Su=x~I<{]mmuOj]b|X  L09W ,>F!ƅfZ>{L`'"-@Q0=1999jKBe=Yu).%B~Rx0U'Q @^[xDU }Pa~o٫Qϑ fV`($y>P| ":kG[嵀\v{?qE$ӳLJܠΠܚARn 2$,R?7Bpu* 3tz㽯wvs!e&G 3i? j3WK"\;]yդpإ9U[WWVi HXG Xgx Bi-/-MLxފcB`q{{{Czj "c. T*x U:v(ί&̀מyzA ހ"f}ބY aV$YnyyYw—=442199 + LJMI&*`{|7հ-c_zF:f} p!&p!.e܈aD"a8>11_}2?32*1/[=hb 4%5LЀu\1?<|̓OFؠ0i;TW(,"63aP1bL`&%bvghWHRBg\P}2'al|PH 0'[SKhBU!P[ja!? &G8轭 jpH^'1xc (5F0 Kf^=TQ'U$&CyHGXJ2_~!(a=EUh Վ \NQ[rHTx=,""Sq@ݝ=`^zC?F%`x0/, RqaOz|\$?@::`-SsI&>fk1wCۋ*T0ȉYuR՚A^#^+#c0) KܚSKhޯ.u7";Z2/;mOK7z_b@ UCql\ި>cm'y㱂 j4!/\`}Z m,kd\^p#bF\\| Q,cC`ux}9^oU!U3ɩJPEt] ls;nAyac_tNv6=8=rX,`E^ۯPLm5$l!?걁0,/Oa:Q'NvAʿq`4rkAp+?ȏQ_t E A C5.IZdB=#AghAjUD*LnO`Ls]++P)18LhbB6Z =Z_lX`Vw$ r7L^{3E4%(67Qg-6;$*@U\D4]TA ֡W|c LIIB۱.{1Z岯()a?X?\3)9Vaٓs&C&isdF/$lᘅLn3y^xW?X Kbhӧz1G| `x|c+hi|Q| 4YJD }Ƙ%-(2;@r0&Hh% Aqbx7aEKxTAG#TچƊg/))Bif-?>/X/ʹ_(2jW9c -+PD/,1X[G֖.HPRsKGVn@빋E 537fj$y' P ! $s/,&H&T(.Rk!qww}$E<#هAieD5c|Yza=PU+G('/$&\.\TDJ1ѥ?\6 9#̶ōg|_8w1 " k}}}t``ؖ.wuaT: &DRBB aC1}hA)$yB ʝ'LZ"L[?vy[层 LqB:Φܛ3ͨ-ՠ^Z]xq~IH ;"H6#~qaaj߉H/#Ȣ8b ]NuJ?xW e}xo `jϱVXQlקXP~ R-& _kDa- ǽՠﹴ8EJD8b[ @ٯJl+EfBh:xH^đ,^sD~fˈ8&ryT =MaSՐgyZÌXAZB틩UqIjB0!*˂2.-,F5ifE WV 000T0ҬADPVDz_ȰP*Oɀ (0Zh p666lijn\__ )eA# , E!eޑ`ם{PVgnP^ e~sUt0y@`)0 LMQm~Wl,* KP@`Bq He%s AL0 b" zk6{<]nQ',f`R4WVM|{\ KPUHq "bYʜ 2v& .q-Ffb28\Z\nmmNfOE7+ܱ ɋQ1HM{qDhF5aDfR[[f8f>0&/*,~%*c6 ɋ`4H Ixuee,nj43#L`L^ FbdtT3%jԩVaL`L^ Fo ]Y^2! T`) A.VWMQTE!_0nɨW)m@=:QΪPakk ]0WaVP@^7*hPtFCcEU"qәa~@X=KK`J"N&/*YՕ̤xD ᑑ%f:lXK#B}81^$`#U؉䅪YG3 2b;T1x0РwU771QPBmll@17!՛QnvQ F1MfJ V+L`GD"!烃VW q),Qj yL9V'/0FN p}5FLxjUXQڡe^H:sf/]#kJՕDj"'&ipaH;|(sfh)`4 cccؙ844$-'eьvP" l9hȇLPa|KD]6==ku%^\Xs\ g0ћV"tQox*lAQv0#ܚ8fHab_rj3PhJ]c`nbq B>d9rڡ:*༺2q[`_΂l7=urXav{65}%`b0c1ѫX-TyPaWƒx!DADjwM|/*[jBTFMx^smWsG4Tc9[_[s}O><%Y$[6?׾6lrMpֺ?̌0`>,j Bzö΃yab|Gl0 g*LM4&BS) !ဨ@ T͆ $];H?;u!D8}lEXw$I(; mAT0{ ev\acK|`emG_d:DzS $be 8DN@8?DCiof1`*on𗟓 weO~pXaW[[J"anà7G0s12 H/:/{BAz#̎TŅi+25#WhU2Uwh --.Jiz3@n{GGQ F-;yb}Oh=;++{J?cT^񧷷WF#"brl|.|EPT=O u_1>-דoqJ̆;_oWែ:#I#U ?(^ Xdoa1r Rz7PK7eFHZ4==.1-kvX%;~sUR'_qJ:j̝K^e}0#xlwwWI1 eS1O>Ȳt~Gb /<*JFާ~CυH!ϧFEH+zv͵ƈ|N>4(φ` o=>ø#|_PU?|nAio1?.wjR~]T.F6灵WW5$r$&FeInXjxw̉X1x=Yׄy`y c``FH$+%0 QU|AvOa{7*6<*b=BS8J>Sz̘smRA-N#FwwJBQ!YڇbEt*%K[`U^'}f:q0#ޞOՆ*F> tKm/aw7s%H1cA^ non]Xi`irmՕբj{OdΟ+fWѣAxT%Y nͥ4"ͷΘm~-I $,]]]ZJוThAlB@%ui}]s8@Z}>_N5@}JXO >@x݀w~:r.Y1ee}^}Y? 7Tg?k AJ3_{=̐T'o)[o70%jQIW6nqڥĞ[n/1S>سplFlamޑ| RVY~WwT[0xtU jюx(dTND[c0#/v 1!\&f3{BIߪ%a@\0p_ |` .>C $D"w1:ŹVD}!,7i=gUR,Q"|iBD3vvУ[#:.BHQ8{B(~jb'&'\>dٿ'gttN`&cqSKRh $5R~j&*]chDD(X t)ؠHV.U uDh8oRem,@.ACm02JV>2Y ~` PK?0nmn"2tQNZ‘9g0bmۥ$F$"=AfYKG!X5pM+qӴLV3&=Rbzba. ڠ԰L(p1 mrL8:ܻ*U=z5*i96>~'f {+3)0Ps0` /" j/DR4@>}Dg@|40WUԌkkk+@?_Rv|[t-g Ԕ0n?s+V8PguGSq@~ o 3)F(iZi?0g4"fԺZ$?O ?(ϱϱqԾ kv2XydCh\ױl츟9WOF=ay!l`cVa‰$aRpA\ܷ pKhmY}~`x\&֫HhqmP5Nj̰|4>Sqt@Ir iZ&d\}NftFvw#ٷPn=S?~` Tt& 0ߋtObtM1L<14҉]:U.@c##ë\z)(#@:۫ϦJ5w~%Hq Azr2 hmF1"j/T$2Csab_{^nu=DRmBHzvAPND6'UE'avP8s]}\9&(T' kfSBΖ,èCC@3dR&,РRU&:vK?/+;|<:8?0'vLWsj%" i(qVe'A4epp0HcH:sffK͝??gz{ 8.O/䎙p>AUq*'V淕 䄾ZPdX:::d=r $y4VE, $ p‘jfh鞾 \Oe՘ݡ<M9jAMx^ "cBXr3+4CLr#N&0|jTuQI}!?KieܡP$V.)i>s~٥ &!S "k%̢fXΥ,6Nh2ı(%?8yX Lhl遣S;p0dv}1X:my&@[4kJGlFdlu RO#}+՗ަFb1٧"w BQrQU nTcsx/׀4 '0W ⹗ȇQ #icȫeT̆xNQAYIϱ vXk8},jV#l*k=ntt ”j+V'?Xb`k՘K Re K(}K'kH*)^}l;ZݾcҌ/XAXXADW:}eN4,lF0[ 䳼SP.i### D% 7ȋHf7k'×\/W٥%}ù\/(1KU# ° jiĸb|:iτ15滑.csVI|`|bSiC; Ōcϑ˖Sg BG *g" 2uqC^P2UYHN;"!kU*7r tdΞgi6#y00,?HbuuQ *S!´! "ɒH fD}}h"^ݡ,^PyfBmo_8< 3֊BU#Ɔ< ܀̖HeI ?~Q ' !ŕq}#?3E ^*M5E;tQyʂ2a ap(1@y(M%' M\:@I&}dd$&7ODs~,r2#11:.0%(}$ePsrqQ-d@= XS`5Bs3њh `3)UDQWH[=3s1h/\pF_C* ˜4ߣiJo.F3a &rȌԛVP<0B;M62q1I( j t4~i-¶ 5rWm L5 o>I_UH&zYĀ$#[++DvJ[+ k@bVIar AdZ}w^H.DZUҚsyh.eKo%<1JLZQuPjat4?0g\zh7X)_VR W7\qf0JSB2 g:b> {{{7AX$̃ R-`5#3ȋ\dc"!q  2#geReW(דL7Nfv2- Ĩh?wL6X~3ePi1D.fы/Né?<?%̓{ْvH ewA!B")Mf'ouwGxF"#3L 8i ~#aëX)́yAqJ^*aO؋W7B-R_Yqt2QefTa|j2vD%2U0/א_,% ]9QbT* E֏Bnr »crVa*( G!VQu)2 ͘?Au@e[[!徜!pe@X%]yS#BAx~ ?Iu;G"΄.l❂:Rs# e<7KVXU2_6$kZ$;άFdpNf`@kR_[Htv鈁#s?-C vD4e=l⍮:)r0tfi7 2Fӕ*۔ Y"pLFbࣂȬAIt.HڨRtA$TI9V }T 3rdM4 '43УMPF<;GoJIw4zkeZt+ud>Neu9)d>}͇Aaws X Daa,KuADHj31U@LHf*ņ$[ zhZ5fFjAy(:2#°C(-BHlRfKա 8=OoG"Bx9X+͈~7;Re*EdYvtbεA DAGjUtdH]o|S^uO̫{yezڥ#s1֮0]Gq;6WW!k_qQަ<072A$ Zj$T&HFAƶ0B Dr#n{L{ _+ G\-_80Q;٠fSB&nwdN:V9aCŝH$W`] 8t(Uoe)&"R&C"-v2n)` ~kAӧW6qEa_ u Ui ͇yȷGi:y `,UoHl hJq0J4LPVNn9D$q h^Om𯻸DV=B2HFAś, d11He6Ȝ7F\Q1Eobzz3Va ?K enZYm{{[cQj~U&ws5LRGvٰUNX+;2K ̒<0Eri6{ ?Jqo#s2/!`/s76sK#c.m QY:2!&FIvLfV޶6dnmFlumxl\U<<齃B$I sZQhT;23N0!J R~%+pb vE1fKܐԌ@ l狄5R}^E? З Hq@rTaL) 3㗛VvL^U%!գ;2C3t]qOq!<3Nz/U\˧Ͽ:zm9GFG̩ }BN(Ti WM8TJw1UY$6Љj*/ps7s1b\𸴸8AٳOYF<ŘPIU]^\]Yjxď"zFtv<D^-g:Q郀d*ͮUs&Hv;Μ oivh0y {ZƥKmOZiw_|2q=le TG8ps=)0r\PbmLA`P_F6 aTThwUl`rY4pÌ0K%aԓ[jSiPotKM)?b`hhmFܺ UMm7<7~{gN^xUDO ݠȐӆߑ:4Ԕ h玍-.L۩T]"[Z\XVE9zC8n f7" ^0!XV5H tޢc@pcccWgE."G W d8441@^Cg]UfjdtO%1 $P?0_fO>$+BQ[fY:ѶmaLfdwJ B(2n`DsBJKBA$ I=س ^u=XܵqI*JJ1U ʭXu?kU%S~b+TH^0Z! d-G{TBJA|V& 0zubKG PL@Ug2-+cvig6m?]`Te 82^&XH_I0:;#,L$ -MJ(Ґf /ΰx:.qXPK\'%ۼSP~`桶ZzGt6· Xh<*À.=B ]:8hwFSO,atVʌTe\t:IHZ^Z:-*l, _ `{# |.':i>hBab'0[$CbIkGvx fG:EQ:~ڀ`C ǭTQnoU.YJQD3#B2SIsE+09`ydt !AeFb/x @O---z{{===!3G9c 0)F`*#JHQIb2i-WBXkd5ь_n!t)iąV}䐸lN-{*!2/Ԏf^/MN W}c-,$ $TE0ypAk5,$U ėMb\f$4 YQ@tOř_6FAx@/.)Aw(|XGu{e!fE$Pb!±+}L;?߿;^m-E<0NPwvvPcb,Cfyc`"*y9ƗWD:ν}ƌѰ(}bL i4ef,=}r=/ SX`~#+50V GZ| f1@]6Qi!{qg2P @ 0d̪dqb&r*Ɓ'3e7PL|_?2֠LZX΄ȩDH?8.K/֪ϢV\̈bgck?r/--]AeKv  0]ǥȬ5o ƗXX- # or<hZQh$"0rʑE#_X wPtPEE(-"/Á;Ei!3:{t+Qm?Rd"0LZqBx{k ,"o)+kO-?HC{(%e};)ayO{XIdC#;pXBӔDfؽ*lH~BξJL(NZMzRL0ɭ@6G?0# "Kь]WWHMcRU$ԾV$͢)IDD"ZPTDQIZveT}T1)sݫizH ̓ DZY}>,cNސV r? Z}T}5!DN2Dd&[2Bu2V[+l߹Cn~/D&k]6HeiAɪ2 2"/בmȾn۱(/}Z%u0a>JN6|&> '4!ɨζVZZT&0u,FMXsȯҙB<.p* &~Pq5FI֗ yR ݴehj$u2:է`rR5UG A`&8La㱘> M ' ƪQ.*8Y} W*%)Q>Z̐k,EhxNIK &.QDL#)ShŬ4QI?0Fi* [M,d-HnvG"5a(x"!4Q JH%e5D=Hq-PE˧ٯR&ĺ3~$Z4)BڀͿ"Y 'XAj԰}?E@H9xIArEJLp.: Ez[u h8 LƁf@28Z #hA!~9b6ɨ60o'Pk K&;.p}Uq] r:Z2)*3.lAfUWd V-۔9.W>44 +w3zs-\352iIXvMDaa)V80a0EL ,Ⱥh2nl}a!2]sW1b/#zj_ M<0ule~&cgf"`/ߋB+$c Nq}-1U3faU*;PSI"ZJS"X<̆zpedr+_9I_[z E> 2b,XbZȩ((cWhm'^'OzmUwj[:Y2Я" 7VEsZ$1ҭEi[/?)>/9`rq!QdaT ֮VӊR.X+-!>B!U4qZ (7UH^koQB?rPD-"v^IBة/]:Y}dKuu{_jߕ5k{{{z[~ h&l5X B˙CWa^^^7l<0S_xv}BzWс>FGz`FS!|IUv(ԨSsrZ#XAvamϞYr;uo"-ЉFBMg >B~jK&.|vRCrꋰĶ5,y`/UOљ;>/Bv_|֗[0}$v^y}GF2G(y$BR`&r֣@.+ 9Q3Mҕ 0cpN~%C"ӕ婳_:TH$̂LWUe$#K;qgḥyPb(4񆲊F"&CF`_|W`X\|,/-Mxl.xpaoc2 w%c/}Š՘jR߫pvI*}t@"R:sfR08)2fPUT8& !ptl >7:.7~ !#xpDJ*0lա"dfqYW B䪠OYքAp}}ضPK@6a@L2DvWx b56>.+Ae;f'O>=g$IxB(I``PXẎh(=nYaccc$Q bFc>qA- Ye0)/R?әa:6ҷKa^` Z:&E#\ur16AvL%PJG E"F'NBl)ğ Fuh~`ľ>h|-HS$nB`UXF /fƓIUpL)Xp0?dᷛ;~${S f7K#J*TFUo50ڣ0FΨIT,9KBƆjz2>z}z-=`̨o,^i_}QH5*}ʰViCc,adfc]B86Fu cH:O:߸޸Ok*m\WtCF8 N#2Ljqg"#=ܱ\oUڽܣHzr z8EV; x,|w\ƶJIc%'@IoQa3k"zΔyj&0U#(LTHsS7Rwpz9H'p05 F9̐LJLs >Х8񉎯@r J0 =hΠAڽ^kg+>tFbbI}U p2XA㓫_-#0j]Mu#Ad3cgM{~,ka@y`KKKQW2B]&VG)yI0`ӊ/a[\P8ZF`TQHDؓA32:ʯ.ϱ̬,<$aՎ.8fwLV- ߭Px34q5rj]O⦜ {'΃5,2X!pC%4VSJ3%k,Nu+JL`j=*,nrD`z(!Z¸>`W>:[TE@|>IZ0#%`T&K%S$>Ÿ|0 y`ɤL(jB|, *r`vCш.h<0uy`F%Z(CfĝPHi3{8^i=*L1`>/&h*`0M"09px^ik` 䁽η$/5J WKao d{[@`pp0 R?0a<0wAx ʁt`@ T96@\-5!j 0K˅>*ȅ. "PIUHUg0Z +䁩$0U[䩰BݔP V_gD1HH (+E '0cIUO.޾>\_ˆ&TS=Vȇr@qJnT2ZAH=0Άg0zD*jDQ>& L^,nwx_J  S89X!""1y 5 ʀW*0c0 KSf˪T/TNNgms ԙ+'g_q͹6?qݟg~dd$®;#3*pÍ7jw ]m[xKK?s紏0~;jI?/\s:j~ݎ xͭz*ە0!L$1Rva|a*4ʶn"n5E"T}q<:2C*H0 Bw̙9OBmΟSS w׺/mβ @]wU<+&##+:fgBj</SF/s,/O3Q=ȋPHJ&\}<(cB0Q}$T>qαV%+oaoueٹ뮿>og##կ̾}kC=m?ݿD# l3W\CoAgyoGo_obpg.,)ԅzʪKg^2uxB\}$ AU  k)ibX_[Pk=ܜ{||,>9b{;8{=2moi ? G?@9#7^8vn> uUWE=}x T+"0*KGU!Kۭ,(@knzMclwĆ^ײY[6q>71/ν: Ņ=~G(2F|A;әhc" ,qJnw)f<766FTij,uFk XWu7 XEB8f2}.-.NYb 寯h_;M()>+B8 ~Ǔ~e!2A߅ϰ:⽕A<0F:0љ$OL?7;{3(F}aE0GŖg98֫dI_?MAz &O|io/k|x̎|I3&ѭ|˃2y`3: 3"|bg/)E!7^yI3?{?Qx<@Qzh{am~aXTϞmz,WWV`>|fi'*0 S.b!V/TO"_^J(դYz84_};;䗾EL^(E:Ltv@3r"(f:r7i[BK>d7L]RV"k?yMr_Kwd2{OܙU$&`bLN0}ؗN[ P+`OیQc}eWWW}" LAoo(̈́y(Hȗ8 Xǽ,@` AV3%2VEre;{$06ZPbS"_f }`.CQJGLPƦDq|hAY% PTM HR-)yC U0SYB s`_53͇%01q{mTaS"(6ZN$U S"e`VAd ZF9O^+SVo~` I=ul>8=ވE|c0l>8]V0ĉ9RϗbX7NgS  s6Zzu3bdfAYwҥQg0A:-:uOA%6Z]0 oֳ?h?:/qE xY30///Oe`HăǮ!c >n ^`~^aO˱r@Cb2Pⷩif(&(A+\f#!"g r`2kUB4J0gXuX\X7y0s 1W>+g>x "j(dy<@|˟jU7XlZ֡q9")>Ns&A.#e4R+ BUb@Lpa/:06%0cU+j x./-qPDe67e'ی0[2/fqD]Q⼽`44!nt)TA[mF`YKU9tMh.5U|u#YԌH_$ f8h<ⱘ,vuuJ`x{{9xN`Ɍ3e8weӳ<[}++d}-Lh-}=CZ3 oќv Mj{;r_ 렑,*d> F)0,lFH(\vql!ovntZs;v&Ӓ:mOs˭1i[[R}$Pp}}̈VЩyggO9MFRǓO<ђ]\ے@lst&-J@,:~2))*6#V  Va:!rJK(0o[.@jDT.0#1FW=V6** 3`@2ՑkК"Jby"ANPd@,@r9uq` |S4 v'0 LХN|E/n̑7-h~O4%Œ oaa4j |㔜5[88vbǗ=wZ0;.iJdX ohP#KO0Qb L*BfDF0QmP*/W( e8 #J"aBY/ד v7'Ȅo-C>L`fQ3"~ݑHB(+;m+n:ϖ,۲e˱#k8haBᔥJ˒B)@@ٚ 4@t3tHH ,IJRB $'»Q,[eK]WY%ٖ;=?{$բ9W go8#E"v΄ ,d3:eFJ+|` U7u:HhfCQbW?!7Whg$ _V4#:ѡ&y5]MWKqh)-5rkܫYae0Ft<ye@RiiiiqX=2VȜ SX$ြp ۴(V bBµxH 0aU F1f'd?ƺNa1`TL<5H 4C*B3n&9c]s P=] ƆB58i34g\:"UWW&1;㛛-NDӜή9[Āv9W- B梐dHcxvx&:4HEN`"s 7_M a 1]o~Tyy%WU{΂*F1#^"q1kwEZAoRe$ -ZL1PǃB)0"gfڠ)a!xDd!@=&i+G#; ~_JavEY S0:d+rD tB 7!A[ #ɀ+@tľKfϯ9LR7ɟ `+7[E1]F+± f9=D<~1(h6T) 9&xI,G D"د>9+!(LqI"$ƆC|dZ[-'52 !?rǎ㎞3gv} /9aDf $pʄZik9:$ef r_L`#fEDatlઆnP@ ivVw"JA=Ͽ|BQ&$GtKI}#(>1^5H"yب dFXBA<*&AD]hMJ.bG&֎^Q7wCm}f2kkR+zH~/fPPXFٳ~=''gd 6* PH=:X 0dTWc;%t}nkD!}C"E7 `0DиuO^͹MMMN矛i{N-Z?娣! vدVl-eee=7l0O<9c۶Λ$@u)Lhl&Gk+4&H4W5mx;OX@4xN%bl&ipSHC)l$ůY&4FWu͝7>}ƌ.h Y5J~*/v~e:4>4s_U^rɥ6;Y# 2LJ;XzSk~zmò?vv@P}|݁οfX^^ٴqDtBԃFP8A"4A`@Qp SY ҥA|țGa8@ AjrVˇJn^<\2K^e|~^…g5F* @j/b%ؑp5Wa h;he9\w%~xY낋03M/ :pX{H}X}sEԌ! *կ_`_r^51>E c}iU?Z!ܐvgcC;UiӺ>xofhf+L$¢[o`ai9yzֽwS 7׿"j )]&u1Yl+ ̼@mKKKHa&ދd,~GϽw)@F4\hMo\ Wk}vڠ{qШy= DjB~74߳SN=վ/Ix"-g; )!.4mmmz/LO9\'繂dژJb"UIc ^I&WRZ֦m"  /u\rko`,vKefs{{C}[Ӗ7ܠX1M ƪq%%U2[0Z4:x'1Xt MɔT޼y^?4v1`,X:TE-fe"ݡaɿ>$:Lԁ2AL#R0JWrNlYI}n-C-bIFl%Ksn].N60Wo^Ltj޸iG+r5b &!v4ˌl˸%0a@P|8\jUed7B_87srlm˖Iz|S dΈ dmRmR}-(O[vڪ{vﮖ:nwh\s?j[F]~zˮ]Ý,@is{Ų[|׼{^|ķUe98s{xJR%MДN5+6?:GKrjf3rl咤>f2vf2j9~\c 8o}&;yWWhYY;6|_f>sgaγRA`>_{t g=efc5kkvV!7\d9W$:K S}gE֔ lCy Ygյh+ww$yZhW^)#Un.G J3@K(H E]jP BQ{3ՅsOl]ڏ]x̩\|~T_VL0CT8U2#0W B ؇6. }`4O,5DCPM .yC_ʣHwmPxips jư9D޷J-ġS2asDǑJ=}:Mf䕣}yqpcDR(4y$QaE5Dl8NH > KD~6 )eP26.ĥ$YWDN7?y8 5*^Hw 7 $vԦnCfij$P z ZͦSa14P(\OٺcxRm ZO]) KD D"hڱ0Q6)2!:V!&}I i8 &DE.+ %He ˕NL6rS>B0R]Xt q Cӊ)(cGf٧U!ŎzNMs~Ԃ*!)#fη[R1 b\C&C\})~GD&s`7֯Q " XLT|b⫕i_|g0kc 0hd&tY~4`A?}`t]8ع?HW8?8`}&F abJhN$D< Dc*_*۞p҄8O.thS[o '=4(@Bc,6m!Z]2A3#3" `c0DΜY"s9ܦ͘U=.yp/4.==h1%W+Rq[́U3#icXS=`0@HçEU1iMÚ Dg v\-kD9b0 k` N;/Yy #F#99vujfXލ%&dn9"?VV Fht:WaQWPXX˿؈ #3R?d J<;fن#d2٧UT8֖otS }`I5%P\\<*6d rF\) _H0n7a=ϯڻ"^m~~3 yɚCV3xX|h];wmmml` ,p85B#suvw[ k["{_WWW<H-.0`3:d4lvsnnn` ,$4WIiiUٔ)U0`.yfra0blܼf!T\.oeXlfVkzبA@*d]]]I)M%4SӞʙ➎tXAzdڜj0B u,h4 V&@c ?7n`58I#7nؘk2e^sMsLtz‹.Z} {B7ַձ. X{=t}Aa[o~_#>p~x /vWzs˖/ĜC,n=60iah&U^^I!~6,~7AmnnbZEn*XSyX˱h &QCggg%?!9MZZ 2ۻgϬt*hFAF E5p655S+ܱ8Q|j `erU:t",t<3ז͛7.Y 2'DoZLMh:Xb]L@ ܿUVt ˖~I+M`Iҳ@**m߶hrH% ;X4 KH*)-uϹaѢ20{+'p\h_0 Bx~A:u&")tX,fhq CB8zBnn3Dq wfT`[(|ث9!4@o4Pe(xݭ[@ Yq=51a٫֯[oYz ВY_57?gBS:m4Lٜ̦TҪ@;2}̙ưu5Oρ<_۲%7; V55Q͇д` BM (U ۢ :N圴YĆ6V0A 8B6iR:!GvcY8 w[*_UOf}t OI檕+ }I'AZv4N1KDJ !:"^|1K/mk '`K9ߕMgu 祥Wcmyus.@CP.u(&D (++뉇zGCjFt*B',1j Ҹ@x}}}eB)y͇ r&Lh6pvC"Ha p S?޻e"}?@XywvtjYo'W]@d AfN9yu?+ hoCz-8܁+ O.+TT1kk`PaϘXPQq)0jfM]dۻgLq b#hIF>yhY׭X٧fEy K/. N8Zs |;|Ca]̇~Xm#l ʠE9a/#(;ѹB]kni~CH8{ˎ+QH1ouS(xBi{H!5X)ߢV?x~CAЈ~ qN9T?hQo ΂"'7EJ3 BU>z' qAx͉CjTb(k_$ F$@dK)֬{2v q@"x*̓#6ڮjw #@@b 8i1_'ͅ1i,~Vi`cčr!OaW"/<' 3y1aAO)_v D"r/a#1x A\8_JvYٶpk3YPmf2?#5k=udziSZ U:y/=RRx&26D w"ƆŻc9F"6B5Fcp}*#P= lT43&DEڻw֬ٳӄxt>>$=8QO ue#[48dp\#:Wgg_R#ٶ:A~Mі!"##AV!=fۙHLib 1 >'rt@BO5٧ ƁВ]$USJѾLG LY_WW6V@3IXM@ZhHjRzl>+  !yACf_AǴ R+CtюyT`D(mt}~)J@p!׀ľt3@YDMM:$Pq? L` HVBƼ_0uvt :%gE.`µ(ʄ`D@4:y.皘s 0Xۇ}ŰP$"IRM OSh&foABli>^>0#r@E,`fD.!{a?\@fCZmÜh mmmR.r+ [b^rٙP_?W$$#{|rr'--!l 8p!5G+UC댌N8mܢ1tڞh,==mB:xWIjL u,@G~dYg_~\nwZ$ܼFq*)%%y1\8jZ}4Fcww¤H3 Bm+n 3|T]0+Ƅa;2-R,QqPZ I cp3Q]dns~}L-r^OC($D,9AgN<$\biYs>ԏtWp!`f_K&22 &\Hgy0 Uc*C3ZL2Tx 3x`LeHN%N̂IN5Peݿ?3)c"Lhs!2`.EEhM;띷6] n?YNД"A0$l%j[]smŋqOY11'tݟFbL͈m߶͈9@88rOzffD\>u#6DYb353p`0 v3&S{y rV5P0b0L`8ǙgeYz|;[W-+$ULdڻH b Y: ?#DϘх@ ,wv̺KW_qEy{{{P .YPqI>w^ŋ̳frժpD\@A ` `0 &0`0 `0 `0L` `c0  `0 &0`0 `0 `0L` `c0  `0 &0`01 `0 `0L` `c0 c 6`dIENDB`ipyparallel-7.1.0/docs/source/reference/figs/iopubfade.png000066400000000000000000001143401412533144600236250ustar00rootroot00000000000000PNG  IHDRN0*tEXtSoftwareAdobe ImageReadyqe<IDATx} |[Օn˖-[qĬ!$@ -ӲL7h3@)>-ÿ-ô@ )(mi @NxWȖ,[tgY>|߳w99GDS@t @F @ D`@ #@ "0@ @F @ D`@ #@ "0@ @F @ @ #@ "0@ @F @ B1 ǖY9xc/ئ~O[{;wb=[2<[n 'x¶kV>v|{>1ɶ{F 9W_];;;> ,fuٝNAM/{a]wfffo~[<^xӬ'6 Gn?M]?O_M-[|noߞ֒{j1l2@x'~{u zv}Q\`F]o_k ׾9mojnn Rãxߩo{{}ϙ @-k׵6r9<&gFX[l_Ij#aya5y%Î˯MoDgQXǣ.yaMmgnvcx>?j߲٣>&ރE+O:$ޓIF HꫭdV2^t@qm >S9fP8ҙ-_ Zx̱eclt]/яlGsRmzc~[۴ɳxήM+N5s>w< ?ɅH H^'D^"نx؍O|)=:勺 4gnzܒW o::#@`̒OjO9Tλ!&׿֏cA@ T 9\>AoS:`~ 3NW$>_9`O=8/dsP' @PUU12MR.4`A%J^z{\?я;ERZ9܉|_{VbYP& ?QSSx߼z6(ZZ.EV##$B=p wcj;c^_I◿4w12z< .E#KglE͡-@Rki> E v\bV̪D[\bT[` i!,<*S@ IF,7Xg܊FRV602\+2CGWV*L6=EbZX*mmMJ휈21v)GթLFN,V܇#b HXG Xx"HkhpeR_Nf >t:u@؏s5}*R !C+ a&ꘅ% c#ƃc,7bcDQ4JbΘɉ^"I%X q[$VB[{'p'a^VNfUAQ͈ uba\ 68{K$V4"@^1YXT}1&xm#D`B`~.{(܊^XU{{91A2 nFAPxRzZpظ!X^aPUAke^P4R9BzLm Tz,#RZXY•3948ffz 1bǠhW\vEYC]~?vC? tӹ$>kzsqwɎ#*T~%Q$DP:Fvl] Y~<+C`?uJ(R +`$rJ'JMdt!,/ } eSBpA-zNvDG,(XSj e,,Xa h#k@!(wPm"0@XĞy& t"di@(NVyZgA4K@ _ED`jRj-VŨ+pX[WkaFǓs\Z.ʩ)C[^ܵ_nݢ]/`u݆7o5448a/~Kte$+A*7{uD rf5U)ՌX-XZPEwhP=-ԶhlQ6~ŻZ81 7}][}YG}c .aۓO=a30bj:^goag?Իmnاyj?q--G~X=Paa޽Gp$klJ( [ bX=>1KE -An"#çckEicq~}snE&,1>̿;^}`~),~E#+3G- VXW{w{EpܦM!\/zQ C܉VUUCKxTAG#T oذeмpzzYAhhAo`Ž/gY- 1t  +楏kfE}MႴ0Ko|p$> U 1S/E 51{ǍH9S܈xUk@Z`uCl{F2YJs>l2`qתI1 vӎ;:p<@v jc˄w[d" e}#ޯqjn=.@og'T9⦃J 0,@ q0B+ދ߷lKAr"Nc6훷Q FNf3ozXT31W PkUNƱᡡzVbŰ&[ &Z?ĠXWlNJdUb2ZCY6MBUکÊa>īPϞ61>Y_VSgggaSd XkjK(Bںz(BFg"AV>Z:J(ƠZrzK.D` 헽gvvVܣ'~F$- w\!ۇVyt2aA%$b|訴5{uV@FXzfiK$fˮ}^8$2uL/ЅI e(B2Z?8y7 ^~tGSSKK\4VTY3*6rLO;bǃz0gyEԮ@>1V 3h`6xe /mmkkBB K!8,tl0^A vJ puJԪUuT2)C\z݉'E{ G `gJKT iݷ`&)Xɀ>Pj˫\.V>oWʑGu(喛P]w_{u?zf|5"“=͝⪨V8(Q$ eфQ!B]"YJĺ^x+¡PǑivp3g \"ԔD"0"+(ڛ8y]ە:*oK;, d<ȍʵr?&FNhP0PHMH(s K]ppS80sƼǚņ hJ!Z1ywo'cÞmƕauX:+P5",>T8p#¥V>"@/gB꛷=\(ݗ0 *uW"/G-aQ\b0X֗-hn>H(լĨ?BcccG*#tH9"/' iчQ{V<&FbEHfQ=?ߓ@`X55]] B } E@7Yӂ<]&xg7u& ȋ1 ǒ ~rDhBa(sh ÄEXSMZZEmNĕDQ_|gB,$ wh;# #D`D^B3zvX5Z5LVPCC]Z  + EW$#"*CJ##($Ir#eB?p+zRTSSSTq>DttC3 zT~G7.rCX@a^HW+߼Ys)ʍ* BeYŮCa&^CkJlll"dL^ ]tAAV؊䅪򘥕Ok>+t_o,dD*bihhκjW"& 2V[`w!\.g7Tɏ~īst+ +Q DXajl`:*v~ /M0RWVTjl y w9N^SSSԧ۴s 7P}5BECcSvFj &0th4UW%,G>҅Wsss{.]!JY6E>HN"W=nG}J ov󙧟ww_CCC.]!TЂ+M@%efWyٌkڲD׾έ͏~c$ CKDXaB|UKDBs\׵WO9]K@X R+Ŀ'dU!K]"ɋ[\HJ Vj%0|:9D\.FW )E dDW"~W{CsP2'0mG;#׫#P܋@ȒT #~Ouq˕!a6WBWl^B`o%s.j!8x _sHq/!s@'*#_TVE#+ }̮<Ѓ1>cQ܋@t!KfR\FцF%(:_=qmUB'r@ռ bHR9tH'x¶+zx^Cݟ4~e]~`"^ߪ}/ze]UB<.[k8G1l-0Y fK0Q&*mo=2|ž_z;-'l+:GL:~v" :ξ3gΥλRv~E,̅a1FlZ&{Q [d@(&u&!,9Ls8+w`~3C ^"YJrͭB2կ_!4[OP>xՕ7-҈ԥ*"Q-϶1ۯ\zi7,?J_9%{&]yRsdWJ 3ħ>9vݺ@6ǹ 'N<$KKk )%zIV1VifAb/ߧfsJdDȦrH3^rJN`p| IV6޿}{֤x<`0HF((~Ganei- B@l4,D&,k]w_@p:V`2OמsnDZWQprdǿKqXTP]}k60ׇ[o}H;︣mv~ޟ=Pw47^?FOReG;zK'?DxԁSOa7އVOٌ߼ yGFG-_`!v9;sE 0:tja⏑ 03€4zr&Xc؄X\o'Z.]Ͽ`kHq&nIZ b<ՙ)b2 Q@ XQ؞Omm-@wtvr"1zinya }b |W$ LJՈEl16Ux5׌1?ԷnLT<$)PV :1@Rf\K#0aEr<\?|(*ƀ.í'{y~zlM7iq&#e0Bq1#E6mAT?WBG*N7oI/w׃vrxXK8x!^oRÏ\tԪ eQE?i iq;pqo;r8[! >cGhimUy^~]R $,y{GG ǻ(a+B⛝UpXWl[J";>tU9Wg?ӡV3Xm{)N`P(FN:$icHׁxlffFJwbM${}˼,㎛p1YF̊F=b=ϻjJ8;Oc=vNϚ5k^j*D [ݍ>CMGľ`U#owpXl1{#o ltd=fʏoO% :PN) HkldDaDب W5'qa;1K[m44!k" q6I[#Q,H0 䣎 D{%W[Nha%[w(P'!XtU€nE])$|>Q>SEX ɢ>d3te}AN^R:.ʼn H% p3cf1 F]ţڐSDVpKV!7iV+6@kpZdǰ݅ٸMZLw>)!?ldx$~|ixIENLVV9aԭ95B%db}A*$7-Z_0ۯZ9aDJ B!nE+CcclDҪV0QIe}q Xy KpH 8|Y2J8q'ĭh<FJ=/yDiii]?ܐ*i?l=T)4jjj<J0nD)c^pY=`{|f}B2Y?Ot b54p97;IW-[/,+\Bb7(zh }#WVq2XWRIp #'{/by`7o V8V__f!܈2nfx-b}Ja(F+僠G (,)a-GRļOHUU0`3k\KHpC qQ[K%2XtLE VpIx.]Nq%4UU=H;S"7E"hcdx+g]wwo9jxd} U9<2z-9E$>+u#BQBE|3sk4=u6[*&Ӄݘdw_/{v=6:sxDōBP~`a F'%MqCiohl6s>wUli쬍H5$k+y4J*n2Zj}cd%1pѨ}X'(*_? 7A& tX&'giu RX<<48F A!wFP"bݞW^9"3ټDGfV,ErnZ <9d\<0ֆWXbޚt`N `8[b,1eΣD~*gX1KƱ&x 5"]%E"0qAQ'Q.' x1l GS ǭ,aqAө^ݨHF\jϬfNJiJ2:6W I%\-EM ]A,'(GGFZ6^02z ۻ簺0Q~-Fq9fr▐E_Evu#@3@',`Lb5JMMt y%.w.DS+y`H@ZI|\tdP\5=֮ }z ^~C<,އ0rsX];k ڈS: UQn?0jC%DװȰt:^/]\nN^p "5:2Q< 8 +q)`@1N+ 7XD1I 6f քڢhko_ԂH]_br2փ`%! "&s#N"0{-AdK/B1Q4gP$.A>YqދpEXpjVb" Ӈdo^$XsR,8p1i|-5!9yj"8X&} 8 Jj Gv,͆E.@/ YKօ-s5"]A!' k]wWM >]9_x..X܆]yf|\`Uffc)b A&N`͉uْܡueq&#>.H56N [eH`Tl"Yʼnfsbt fNrz_h u8ff⟱4zץ]Z4>hA= xQ # c+K X^6sAJx p)9aE|>N`V_@`aYlPᰓ.\*RR;x`j)PV'8Xml`1*9E?bdQk^1/wLFbX3Ŧ1#?l/FȊ- ,,y1 }97tu]:3J6b smX#[P*-̲AtsFy 7[I x+bR\/Xbsْ1#,@aXj,`l\*t6NC}[#g?Ƭri O6eT812z/R/K0в`U FCTK-,,AX +5:)t64K+E7I,A k,ΈLsЃPY9mC:P b!6E +"& Ku,zGcb 3kh4@ЃqW*g~(ӿ]|TfX^YhYU斉$w#WfSu{F*#@Q2B|*TkRl xkq⯫ qL_q ߇Okqoi+"ŴX[z3'qćn&0.oPx 3  \~E0H!1v^"XJ1ވ* E;?>c+B, L|+ USSC HK cqBc"XbZc$2+ Gg}. F$OmLCN -IDƹ0?F|܃jBv!- kZ y>cY4}F:v]ﻏ7oIXmbYr۲y*@ hjytA'c֙`̲͟+W",xs6P#/`i̊Is Ay;q3X+w UTݱLf/p4*sTU)!f$KfF) ޏ[c1):df瘓kozD<B pu](q)FK!Ȭ-u B1-hMit&R#j_l"^JDud+'Cj g6ρ-@T`3u!ސ!lbbR)W]oEK, >^>8!Ff 1!J2+dL ĻEܮۅ7ȉ M<[aq #2Xm΋ܚ3ɩ:eAR_rx .Tᆐ#Y ,7o~#wJ6.w߽?ݻOٰaCQ?đHbƘT!Du*2@H͸xjjnl}H[Sx2k3pe@XƃMEW#$ <]ۗWg}%+5BDP>q=#0]pp~=3nKihIX^1K4<C+*:-C^WGɌKbde.?-R_慩[puK:b`urcD9K>@<VJDM/tyM#r֮[G ) /]Ȍ]MlbTc 2+4qi}嘉q_KkO^Jf wVJ;F?Y`+@.gympR$-TX:??h,1KS=B&3XBj2C2hZ  Eg&JZbKɊB%"*UjMD;2'F:X &3L9\?`Ru];{&O*RcT 3,uDmJ4Mhc" P{` a>hWV,ePYY +xk덼|APp1z{*==38]Zo|(q0˥%8Dh8tNA,IBRbR1Vd1WH.ņ$[ /=?^'d}ʥwqr'Rs$XQBɈ}-PI$Nn1cuQ[=H.W */MxDΥ:_R\ Uľ8U)=`D`<%[&9ac<}`~^xE[`%DjYdR(DuRbDe(H VV vSJdad͚5yo=ҝJґV0ؚd۴CGjBb*%ԥ%et@.\ |9!9 VXdD`)gB\>,'tnx'#Zm5yWgd[BB+L(E j2!P:*`PM=/ޝ\A.JT"j#sO[ 8-oфV<0HQq/ !?r)3nwԲlXx]p{,/`B/(Կ1JX]\mg [:2&FĤ+1YzF1w4Q4DgQ&0AHvFn@$D' 0ـ#J}dLQ)2*1=2,j?}eVa}!\PW_IEC+.E.Ƙ[Q] &TZJk?W+"5J*#3A _{ZS[_g}vyQ-gf&jAz3PP1be(B<#;- ?rjHjex$Bґ9~] ܀ T`Q_23҉iGgŽFp̱i,f*msvYY_Dao߀Uz.fX!\+a uRCX /)< ;hU* p} 7kl H@k@,Ta]x8BfhB3#%ʽD%,U?;fDϲd ai9UrWH^4*  @^do;oVlyj鼁{gUW6̪D DK,'t"$Fq?|w?h p'?rGY 6@XW!&Q/͋- 6( U0H W*]U<|atdu!5 0/d, gM|-4582JC,rCxa,B-' .(\nLA\Jmjj8QY=B _*`lbi D`#<0K\(ug10Qt+!<'z_0ڂE9N ka&Vt((̭yUI@FPu\ĄuwJY8 XF4XO'0_QpcG~%y_Q_[]caltr7TjFĸ~ӟƭrv$!57 WeZ:MܭCVޜ/UAD9 j0 BBj`* qZ4,>,(_{ὂލB!y}.X?˿{ý8U=z7%'1w}On|ai=S񂼕bu$1] 쒬WH86FHQY( ԝ$]Ho-f<{>AlW[\,N#ј/aԮblW) -܍"YUm8+H ϛK3-js<,!(($ݻēO9嘟'4]dqJU۷v;jZ=CdDf&1`ݪC#8%qBu=A^ +!(u*㊵8Njg# DqI6<1D{:'$ *:x*yw"ȇdy` A ̦@f؜X Cِ٢S& CVVj[)zjبvB*%j%,)VW/jC<^ma~#+%:&#[YǷ,<3W_nҪ wFmlzN "Hs`+G3i3{7LR-V$1~^o#.ľ _bLZ㔽 q)9IbehQmkO֗x?>BKFFF䧏 :o=?ÓO Hɝ1w;PtJRUP}Eb^Ľ 3@4\@H|n]*6y?~'ްaO!ϥщ(!PP adHcZA^ZUB5bus;OAihtj`o xP"ס<Y0 w"~o3Tf|W8f[6,PS[˻ l^ϋV Uaף.Jj2cDf P3b#0#骪*| 4an2N(-]DPD Th#&Z S%Wu3JDK̢*2f7d^CC_WBv;/c ސ 8 dj3%/_8 SW-Ȍ=GdB dƬ3ocgˡEտ#0,­.A 4Yx\W /f6qGհgIt\lT)uJ\2aLtb%P3BaNmjljjJ)5AE]\m k|_/ q2Wg Pd>~qB.[_.F  \;",P~`KԮFa`Hhp92B@h,oK"zF L`U;:]E"ғKppȴaeOADB(U?0U+ \1 f[@j@e|ɼ+ ^Kd"~`Ԙ[_PbXC2BcKu 2RE,vGAjz`A ׈/Cs&d尥f (F1Uq5b"P4^B B\4 K*bdJ٤V{jJit8$ݚP8@jl K cpp_O1H.x II-QgOpN:hĊZPpCK權y kcG$($T,FlCkM ή[`aedV 65!Y>^o ׇ ,.t7F4ghd`ʉ'ɖ#W_=(y5(߼az8zCQ9Nw-7 i~179OE 74WD`#y`+!ݟhj{{ 9mg+q,]u!Y{R|Db)Q*foVKI D644{A^>oWbepp\yϤHU冕D&/BzXHMn^g -'@lLT HN`l[*ҷ7GL4 $ @Z ռp !4r@ R8N%z3f*ؾOJ(oo_-gt|[wxz,sB;og`?,,<+o:C Z*\XF\PT7@ ep-@NzFf`$j/B:OZ`KY$vx \33/nDE5=U籠RX_ɔT2A rK|+ǪUBq B ,F6-"#Z$/{u-'T2|%>-ʠ{LeZb< 8T*#Kz]j,d{rbp8V>YXX0q䑻r9Z;;;<D28ץ7oͩԸwGhM!3x^zCR`ZR֒ˠ[Qر\\*0'[2a.u^-,1V曝:\ <̌"q>;jjk=P.r:6X3=a6NnWinnw󪪪ӫz/ҥ[۴XoXb?wmL d}+o~[4-_7@*{%ZJ H)u]wj$ׯe$QYNt6Hwg9Ḉյ0`a0":|ARZ`L"  :s` j&E ^__?KW20Zs@jK%0q☵ ĶFFFɊ!r@+%b>@kZjq-+p2R  u + nƕGbFww!\CE^,L~EE8&PU]lB"^n޲ŧy`fn wcGڼY^lV6ARw#d2.6Ri9z I\'^׫'q@P1~IE`( S| Z"RVW~& Wv3V)%"Y{#/PN`*U>>M'xA`{|YXfb̀FG~i<P?`63%cZY) ,@w}O ?_fg5;pyl""Aerh!Y\ܐxnTf-X:DHO0L)P(@uwwO=e[@)s:R@ /Yk5-Mw[Ė[3XFJQA tlWL&8'sPKqr6vQbP`\(N;t~A ZC~`&]밭ftiA$0Vd&j"ʦDDLVOn3/%EnD :Z_2-(܈t@+fixQ7P#~!P>,=-B!ۉ8?RP?@¤r]pM.Ԍ&tC}'݇TP"Yo{+5{N']*(<0Q`AeŬiXԔB;v1ӿ9a}SLX]}= 8ddrl!'AV1 V@ׇ*T?B.uL PdA\>o%Jr9VKF b138"G9ɦx$MIy`@y`Z>oG$Ɉf /'R<0|Ay L 9q;ybu[ ]6ByBXgfX>AYh'UʈO~SòZa,zX VQY(5|>>O. _,l 9G CV2 qƶm$8pB >%&0d#bBOeb \&+@ sss|m2KoUO [+v~^_V_qn~V؎˯[O8As(=nܨ|ğ-WSScħ>w@w~ =s,.x^:`;/% 3 ig j +ڞ SȖZ<(Fy`AT ,]$yN/r{֮ؿ3q&* TX۽{kyTO߾}=sss5]] yT vLMM9FV {= E-$)`7|Jps僼 d;!oi\B& /~@ġ:SH/o fcsK##\ 8"trO4a{\|7`~ "<"#{X"-:˯<{kkT}C#  :ǎ. |`Fh2pI uw:** <؍0n4Gr7 =տ3?55^6Dbַt储a"0QWt^w9X؝wQV5Y>jHGtHh4u@nP__?!f⎦&8sGV&<`qg#,2 phfR|>,d1#yN|05n[oq]RK!JXptRC+UC]W0 ٻ .EL ' 6m}]ZAKmbf+k^q΍s@SѵwqT HrQތ ]=@G S:dWo;s#zA]T?rS0bٙQCڵ#xzCȪEMt,۷q G!&͘D;>*pE` 0/#p#/uv窱ԔP(D|_ *j ?zLA^3%*^Ets&#C`j>an*Ȝ|u%7kD«j%]W͇#0[ʃ@n zP L|"7L)Q}Pd'C"ͧRɠدԉL5zlz0IA|Gqo-/|NAT ,mv=R+#P=C}s'* L<%Sٟ?zu)jt^oKac:2LכA͇%q&Ϫӧ/x+|ݢ En`13qO!%5z.T~5|-OMMWBQxHt^ֶB&0P .8ކZ70T(v@i:_WTd] :J$>8x{￯kxO?z`:7sssOtuu%,ri`RI*_r$a̧omSݞijٻڮc%̇ !MU"z;KD%~?8BYyN-wEzr"em>~u(1nGa#0)U9A~tqc5>z`Mb{ qHSe}}X!-D*G*x{c|{_V P( 骁?;r}+@KMѴϼꛝ`+>}9[kemXXβC6"lb> E,J1#[Ō@'D=qvہ @L 2%y FF AlzR̬KFʉ-.:SzFu(QUTTd̈́(_Z (d kfD5Ԝvh$X{ec j`y٦P Dj[fDw*I'L_͇-,vf|GuI89s^Nw;H %'420s kx Uނdhh~IQrE͈fmDGdQjR.Q%Y`߄*Q_UcLÑFHd3|!-1ιd5E z~?H>iII\6?&PiQ9ÒpqIf%`Yr8|IϐԐEHl3Nlk+xbȑ>|S![ 4Čvxx+-VzZjSk~%0`>-㽲r2+g+@D ,QW_oD dߩ{adtYmvL]g ͫߜ xvY,rf~y8A},AWf|^ԼJ]ₐ j>ҟ.sɭ/VZ{̶~TG155e+*2!1iJ%5!<:2P7,׼/nx[Ѽ Wܜ't]D!R'Sjm\`6BQYwa?xغo_gOww|S.?9M`3~iy%Mnc$f{*HU kHyM& 859bTtTMMo(mRZjff&mkF\[ SUȶ EA frZ:j{7Uh9aH$kNCV *#UtXZ(Ԝ0/c. mK/]ת"56KX>;"wN̸/xQJ`[/8QOw޽= vǻn2-yfy*n5)>cZp_Qۆ!HC-!h>}}}s---LD|>{S fǏw465 𹯷i;puw'(ZO<‘{IW~c D ᖺ۳gOCth{쯬|a\m"PF5-Õy'CM92H|Y6߿^֓ʜ}Xgy(V!$~t &^-S!mX9qnE%[wz{q/4Q瑊| 7Q$8ygfz:w&+[7,mFzƥ#]www[SSSjs3>7NOM(Fg֙B5V%9|"{r\4< Vl*~iwx% ( , %˹s'7=zv41LØ 1fHc:?zfVNBfs⋧k\)cBsEKKKl">ёס?\;>gO6Ns;!SF /̘3ۿ36qvh[ ( v+s :>W_yjU<߱G+n\ LD+X'HS5+RiiXV!Pm_DvC ??.I/s|FxBa4kQ?kɨsJ?MxO0p>ӓ ׿Ѫ`pCvm_alox&vvہKw0;O|~n_KH{zS7y`s=`W_Ԕpx7"NNNV~Sc@d#D10;3c4tiضZB)E Cu5}Cn:Izabb"?B {,_sKslkkKG%V:%cбupׯ^*(M{N~a%ZQg V`QrQ r^T`Xr=ς&03v>N?t4#G^ + ߘ6.^?Aڐl;ch\ wMc Ǖ:aɀ{;1o|oI7"_c__"T<@SXKK8e4B yu ͖DL{$iFԞ4DVG++ڱS!OmnO\?ӡl $lߐC>U> 3{ Dq} 7M -#q\w3ƍ@ Ԣ-{gOru m6KfTxGP0-V%ֻzIfs 5e) 6c\ۉ/?c[#4o{ߟJWUW!4&2'H `?p}cm\%کF6MM:0{S $lU42M^$ y5tiTΎujhْNCCC&d',,}ٟ?OTF \ӭ}уf3Xw E/}]1=|ӷ9_,9%Z+D%D@c,!)7.MHNT3M^ZJ^;ƗMbA llWة@#4$I B M jݷ/ f cpp{PSSk$&HJ`%^ y9]Ŗ@`P:%+ru& fr3!jY>᳂Pk3!|j;o{'8;?wGooDa_'OȊe>0k!wݖDļg/|&NZL%2`(g&ZzӞ|H=/HVIz-7|W?F45_Bvprß3f\)b8|9I`ۼPd^Wm9nWPɑÇ/Zd+ȧ[;jxnO gΫxn)5!Zt!L&#jdg9"ʆ)*ʝᴴ Ɋ't+VhcWXp5{>(䆊 P $k(>w{W֔ŊBB*o˟Cg#GlX(<@0Jw4/̘NQS(y LEs˚viX^"3_S4,*KZD.ԜdY,P~>G1 ԖBD2 ]7g_\ގTyp^+~P(*'%_ :E K'%ϫoraGV)9e6yxdbTYa$7?&} IpJFOag|ߕ|NBNbٲ4g/OB9/OD\dŲ2oژJcs6.u\Q!0u(nʊ8B5΢;%)3:^1JZ~+KT@|7;e@Cm<%+H)/P50a%⨘DTjRήH*o^SSZZnsN8˾˲2l7:6G/Hs*qmJ%NSΩ/CDߧhڷb~3zy[}Ohl rgome%yO-8L3B4h,AZɚUΟvoz˒r{+y$G}}{w!)Wj6}p++i8W(TD&v޲ہP4E=jەuƦ'ĨYZ䜷+^63Nta2/M}eS+5輪x/D`)?d=Ě$e.m 9U5ƔQB'Ȧj4Q-.F|1J`91?h_TP& E⑇mYh[|Jj6/-&[ d:W yĵOOlM3PeLc B# D45B L۷܊)DOlHa__6jklWmm0>ݻ{R 3aKBpfAPUpZw1 L~o c9A!~N`S()c#,^́K`NyŴI!z6.G>RVVv&+5F)OLϘ ,3#}.vP()Y D䱸lVATW!T}+) ZHP() E'|̂VQS(5@ $ە5r%%Eth.g·V+ b}wlٳkxh1Ϗ*-zʪPr*++'u)k# +((#C## H[Me,--:EE$,쌯yEPKлIova%UbCM6N[h9#P0ܜ ^Ciii4ϋ:[P#Wx>|QeB5\-2z]ló--=`.QV 859TD]dɋo||o߾.&/|~EE4}86$dh$.>5:1$CmL.r\ƒ f.Ba&qz11,,,\\V; (mb1C2g6r\9cz8N"0_ fd9} x[뼹>\xsJaOh_]4.D͈<M{={qAG> qOF"ff"1aQ5ۧ VG i/[t{62<pMhjNj1g@ܷT n%2CP O͘sh%֘l_w{.0%/ƆZQT "A))cAMz,Y{pSpS .UE+g\vF֭A iIBOHyԝkkk_}\)Rpi05UŵH>T-scFLߦ2ڦ?|Z\Orh\T᳷ U $*;1cfCD>LfR$O@l#j1u(k7#6LSG9 "a\ė&sR?vt#澵Λssm/*>h-{hhC72\!î]+htj`;XSYOl)@>1|#xCC 붺|d^V7~d+1edh'2 wߝ 8cmv"!jB!ivjZ\aFse3cwl R7A:o懦[7Vj &O L?KE|ėʽY-ڦf؎I !rq f:K)ETjW+&zg\:@_ BH{m p mf+D0wh*3!ѨFY31NOMiMpžfzR]#80YmTp SiQ̇1g:yl$#QMo" Zz b~CŗhY ї^Нx"L,i{k,JT}LQ{B)&7ΏljE#e_; _5Eӳׇߣ)">ckBT{2k}2T}---B/"k .I)q8ЄdbIΧ`9$d$9"(U 1f3fVW~%Oh-P(J` H\Hbן􃑑F땼J` &ʮM${W 򝦦,+瑒\^2 '@lt$(yD?#}pkM0IL>"˦UB'~=)ZB 3oD(e_R&w~:Q`&%=vP i sKpGϾ= \k0'T<~|L;];eҰ>p\1VBPU$U6ZrH}:V;dULL.-DGfr*Lg屫.cTrox'.)%ʹT` \Px <ݿqRZSuPHKJPrKϹoH P: 5 S Kc`ehl`ag6-O8v|] I,znG-Jd3$˙8h!D%46C$WO,Ҳڻ4NjV?s8= Wr(}-jy^6d V__Q>K =ğ!yHeb9ژ|%JĘIjvǍ]VF.|. I*r|9/B GƼ+|6$,-[=BPS8V/}Fd~A>hBd i[vKP()Nlc=';YF]J|WDLC2|$p+ǒ' B LXw(7D!=(AW:;;ʀn#a ָlBktr.b - B(KP( %0BP( BPS( BP(J` BP() B LP( %0BP( BPS( BP(J` BP() B LP( %0BP( BP( BP(J` BP() "o@{IENDB`ipyparallel-7.1.0/docs/source/reference/figs/ipy_kernel_and_terminal.png000066400000000000000000000671511412533144600265540ustar00rootroot00000000000000PNG  IHDRcYsBIT|d pHYs : :dJtEXtSoftwarewww.inkscape.org< IDATxwxTEgK {. v^bϮ_{ްTl )"{%@ʖ1wfk`>0h\-"?Q.i]z`9^y#PB+/w9h uwIs)@#l:= \Uc s}fu bV'`nR xF/U5ob ObӪzrD̂6̍6XGj<SUF:ǻDvRLWWFPCU7H[G=QI+$∈/av#17zҢ/ ExxND^WՕb) ""亖gYw.kT(owU5y+y7_M" [D$WHOU8 fCJf?&X!DHu|+b|U݋q̧0 0_F[Z*!z%ʹߋ1Ұ"{<칻FFUӟ%I)Ə 6NDd0`DUVR;A ,!b)ojF.;1NxEЪBΕȹDODzbl'bF{$ @Dal=p2AU_R]].j:j:Y׎\US xl,8*bMqǹ\_@kU=ðB"W=a<. a2n{Q90,1DD2W Ui UݭaR%lƋqe?BUIk"N?0 :x6CqTD po^Dܬ9U6 *gRlDFg w8EU{>!(#" yJ|t=WUyJ%XuSc0KP1Y49?SX,1 R""7a+PJ ?8%'$cRD'"er)0zOU5=S;cҍ cR%DDވ| U."k0v\xX,J5\yZU}2N0HKSXT(&"&!ժNSQ8j=j<S"<bR cy x?&#GlL ΰǎ(3=ŒX!Q $WqNGV#Lzhkcnt#x7ouc09y~#o3\? :V) V`&bVEf`DsWxϧ( &@q.jG\Ǥ'YS|S8s:z`K G0B]O}r?Ƙ:Mk1uKUuGy%ѰBD$QU+"];KB׈H`l'->D KUW: Ú?.vJW p pEwZ8ua݊TusfPjS98aOaVL٘I pP59u+[1_ Ĭt؆18H\K;%qsŽ7 ] {HkA17i®:MamGyonL9npVg $SQJq:z }IƨzxDzbT {.|&8 uZ{|/5A:wF v씰-"9QX]@%D($qUD'έ*@xTժDF11aVcW0b8dg4̏$-u ]M|:DAUlj-"2UU Uq!Wݞ#"yLեu^Edfp*gD^#F`GG1ނ5F`Xm0I"rpYub w=)"_(43Ԑ ZW*xKx?0Ѽb B E^`2f }8paq˛2ٝTe˛Y\mήְuVvg|LvgnNlHt'r'{ګz_axkPhs0*zfE<h9͵$;{Zq_v<\uBN}y1|'8d. 浰D95aw{ S;cfBM$j ZYE ډI/&0ZK'"5^.o1.WKFΞuHJM#FHn&Uj 9N'g_W9e7ܶNܳay2z.dM1*/R[[ _"R)~|Wavo`Ÿh$kQ6ϘB#9>ͧe↑fl. ,DF1S;]85 ,7-$D'&띪0!p1f۹LRpq&ZHm>nc"ToD;*.O)RaS}{vx/~INŸx3係QTu my?f:GS1.wTsU/4DC1jf67Zm k|aĹy VD'rVBMqIn i+)CnIstJۓj'%5~oZ48$zyd[L u޻.Lqk?ZrTu|."U5c)j6>>Tw Ǝѝ<8Mv\!q&p"ƋFr@ӈsMžo)8q;B+VOkX-{IU{Zt%^z(HmҎ&h~R|{wRg u'_䷉eLp{@㨥>>v.`Dc"Uq"8+U] "0Àa#DuB̽2Kb4^s@K0V|+f@U%"LDuyVө=ot'[݆"^t' j Zr ǿ43Wf:zxV\. 9)Jõ"23DTuU-BDtIZ>& `΋h1u?g, cÞKD$YDn|`e'n0BBD"r#0~gꧪ}Ddۓ+"kտmzK9 fNJ)JD]z rǛ [>g#2IZ'"9ѮFUèIH U]ϨjwL?1;"^fD%`b"X y$%Eդt}ء" TjTs#Տ0vݭ|32N<;0.m#1=袪j)Q.OLq?kx}{?pCRKZ1|˛L>߽B_pWÔڍg((-7b=Z@U@L0SCLCD8)kJ @c5+CCl/  E"DoWZz!n!:S)U}6 .j|YWڠe]`?w9YT%"aT U5ҵR8Y`nG`b^^fnR'6}1+ Ebޅ틱4Ĥ":.db9>>5P#uc>0j\FG H9:2NQձW4ƘUQDƸ-=Uܞ5l6txRC/ۘ71o=9EG\(t`P>3UFn 87":17+~#$;1[LŐ(9 % u3]]ƝqȎac>ԗ`[ޯSDEDNW#8>a<`&n(sqc2PLDʠP酄p& L;"ڍ`Ŋy.O+︖'^_+fXN`/99;f7j)"ct]=GD`,d5#-8{TÁBN[0S0j' ȶ`f"k9g'`ND:sUr؈dys;i9&j弡%0RHQ[aoں19U֨[y'ݹÈE !cR&wrNֵ)R"p.&xD79pSis`{1⍘z.Q LB}"r fMU6EܞE\g6wj0|ۙEejzQ?p'^b)Ƴ֐]Uߍ|, 9S! }(T߫E$II=䨇~[0+k'|AqaELm?"`<&EvMIat"HDFbR"/=]L}5r 3oqȱ/Npy8.ߍIBw"RcI]q~y*,'awN19;D@8 -\ YՎ~+ Dc/cU/ry8y7 YJV0mb}Z* *Zal?jU=}y<_Dl .bG|G0\-U5bgh)-"2\yJbP; iIu!R 1ܟ)|[Cc.rUvw~VTT E9^ .C4{\ZS)"2IɝTALE2& T O>~N-CwÀg $RU{n̄d͌y8.k7j4q*mμwrRA_6BZ^v>Y#ϫ*MDޭS}1+tؗ7Lr3;knZEğ){Ĥ*"*MB$DYh:v.OGU6i|}<.O, )A_6.=N~HՀK3eH'rv۴.d/b8ĥ8?\Hd/0ێ=qéQrV!6es ar. I T!sD݀+rlKɉpn1'9]ab#+5?]_yNJ&yyw{v*aU}wXܻyk'~Aή|G[z˓e:_Ǔ0be6Zhgό8Cvqadn-<,f1ۗM}#;"XQ^w=.OA<3@'P Vj@UPձ3 Jsϫj0MXr7*: #{_X$n6 @f ;0ɾ':z;\xO )՛u2UV ?׼@3K2g<}/^ͫX,ڏzGq XfG ,nfK NP"ӅѾha>ƣDuJMA' `O޹&ڸ-u:-+hsyb}GZ#\tԋ\>}H~`yn>ɏ'=ny~; 8ͳ~>&?|&9{vp8 lf JVה.oKkRbp}y0ys(s. 1&B> y@jg{ $"cTC]T+.!֣m7 wKī@cpU]>OyE$=˨ U2zP%o1 IDATFmh KImf_ SE7z]mڸ-:F {}_Gr;8{whn A_SAԛ:k1[9Y9 xRL Dl76͏-.W>CzH3KMJG¾DM#N,Ρy)%/N^ Y@U'F]-"jq~`*6+"3jx{uR!TNIwp.aח3ɼD5ز*33X?[L#f°,IѢĺImc.*Ѹ zO@8nZUصj.u' BTkؚOb9Z9'MO@ƈ55Zv1˛@UӁ1XQ8ܽITh"I#^yN_Ӂxb\h P70EvQ-C g0&isȿmJfP2cI_8&GW\vM\;*k' ߞl;5[w3vP~|R4g߱F\IJ\1j7(+:]KNs8GH(YZ"yA@oU?K12!ʱ\06YD7 ,]w'ЯO) T{1N["KO`TgF?C&.ªUӄ#ĉ/o2u{TIؽv;`_hַvXOjx'ը7ܚ>?wmiwɑn1٘&DqɾX%"o9n%$Mp4.aтlB6|_xTKr !al/&厣 t8„1NZ.~,]*nϠOFaOTq6 zJ(,`/d^;sODh+ŬV^M2Lr!b+8 {Ⱦ-  LK15sEd@T;ӊ88^VWbRRlG-{ XDNrkz\P1j7[HE$źZ&)`%LHuG-Y?[9l3u:U+BvuA!uT"۵)ɟoRUS+؛W3FAj1bIh\ _]tL*17df'"\!8!VQ7Ś0#_NzYCZ9뗰rjheFNToڞ-DZwS7T߂ZE hq ˛28f[vou6_cb9}dRUPȔbJ* :v>䔎nWܢ⨝R:*j'1C,;u<>qqMU&SC^cipIdlXwk۳ߣV<~L}\~ڙ)-kq4wIYRRE<):/`LD3L.l\D 9< zDDfW|l`LX"W+06'"2^D1Ͱ:0VDUdq^YdZDEh)?䄪֮xS8>0#\`bPNSN_G{0~;1nq>sɍdsMR:FDq<6Ý"ruvzxLD;@+Ӊȏ@ UߵEq7Ffx1!ػi?܈Z2L^|<"ii\nwuJڜs+uÓRm{Kg)}h~\Rq8$X@y VNz,ݱ:ä́#Q<-%ÛZ DhB7GidJTWE5ĖV0 bmA> A[jEq7{Jbapl,ŠJo{pvʏ/c;f53*ƱN ⤥(!Q4˅""cr#oeJ} !&X iIRpjWg۝D_L 7bSvQWz11 k+]RL*MtGӊ9c2,,`̕-}Jdn]\> 1ߕCD'"cDiRIhIč(`JmZ5hvsſZ&ԙػyV΍ì%$-b0%*;88zRN&%T$;+",P0U>׮:~gê_waq}68̪xl?ɚXD@ I "Eb"JDXy+0DT*BHee  I7yKi1rcڅ,@C^gָgU<4`Dz\_E4ɬZ*3\;{Is9y S_D>[U4}C,.N` &m3Zn=k}Ď峨ZDsn#8зw3_ 87޹\˓DƓ[W~󿿱wرl&)R뱴?~Y{Xl?=TiyèZ9{7߷n&}/ț"iOJ&tbvTeŘ74c,Vϣjܗvޞo^΢/`x$t}J% dSՠEZfFK]"RHQ-㚪@Ⱥe)tF`?guf+Ip$OboPa=}ݲ9oFjv6cjT9|+FOػy>@Nj>{BmV~eKiB\L[:E6CSM:3_œwqnz>t)\/f{zG@NVq L881׷L袪b5ԍ~ ,") :sSS0 ʱ(q{yfr,f ˓D['Œ!ϡIW BK`Ř7ݾxfLVqּ(~zwrU\>զk&|5 ][YL@ժz>gaʇK+"-1Uz? "#+.N|TJgyr„N|I-eܣ0bi08b =Kv BؽvᾇCh07VfCh\9wct~j4ߐ F.yٽ ojmoķרw]@^O~w |{vRÑxTwaSA/Jv>&߱PYHUQfI]U7;o8Y]f%AΝʝ+CV]A5ha5k'~AÞq{4ٟ>b㴼jk YE/C3.]rzIUI_0)5.Y ֠%TN Ѣs+ Af}߱EFΔگTz.v+p. !Otr6-s6HkiX_vf-1z}W~!kF}FwlSAK5hE !+}=suHImԆ.W=C ~s~;o7g7r'!BI^amtz}WFj-kkwLzSehg7@rz_V]ROz0څۨX} _Oϸ[{1TלCy^y`nF]ZP̝&";б ϧpݕ߳@U}/&/턳 )ȥs ,Y%"Gs~8⚐;"ixQk1"yJ?ss9Ktq\Oh4,,X[~UKG$[/}嘧g۳P']E˓k|Ihsv^v ggr2/w3n߳SirٙϟD k]B3oGski*I_<@^ZxUVj]tjM+ٻimθf}־W~IU8oXlZֹSA%֦nTo15G?+]?GJZ#T98ڧiL}Ze6zu;%x @098Dc!It وq ?Y_ySz[*u/ h0&SpaUƖV`lˊĒ@$N"Uc [,[f<^9[Ʋ{K1Y<< /pXU7U("HD¤( iD#aT [Q?^_U193/M}2Z ū>ˏ '5vy (]KpBFèbQ-n]KbB¡#*E%1%!cn szdiJ=;}~WUhS R8׀X K٩ B!QUșDU35;G9/Lyl]:Xum`秊尜m &oԀ ճT8IvҪ¤9EU/enK|Hh!HI>U}%"?HB)6`vT~C5c=byWp\+P n!"rr/묪c;3KHh!ID&"Ǿ#0]DNU}V}+O:dog. qG_|{w}Q2/PB[Y)4tb|_$bQ0xBVeBUh:kS8_'ubWN89,)>ȿ/W}!$bv`?K+lTTZ!BUsTu8By̲E[`T{nV IDAT[0yow ~f Om)~k}xof '?\T5 R""GLtpVqq Bl+f32bN%~P^ Zk\ʷw봘Zד?%034s5oO'@7TBDNZ\tW;;KEPĦVݪFX<QCByK0i۳\BA:=;0~)eθ9Q2U-p'XX!QLD܍5| $`_ڴ @Dj2T:y-&[ syk:k[fil7?k<wہFDFfDr瘔ubBcc~c~_`~ >q DĕzDw}#ٳiE˛<=~ '" p\ g!58Su,,.$~V[7S~6&̧k *0M.9j9N6SNƕ 8'VyӿqO.qyv9T`C{+To' QxHŒBb:0^Uo/=%ɘ'w*]$ րN'Z|~T+Dc+ƾ{ ~L7xPU{.I=Nj|.h$R{+`UUSKLDdFV"  @0pB:XDGb\ʲԈHc?p" lD-UDj#Vs=: |5P$j\dgexPͣ'S\"t֠?gf74KXPw ADxϧ8Ӝ1XU.`~ާ%$ܦo{.HЇܕ[{ YDNe#q'ہtLzz  Q;NM(!B U @BD%HGQA@)D)*H &A@ [% |lvonv>c;ݝg^.:?]%ie`mH0rQw`AK l=,1BIV0Og :x@Dz\9B2 =`K#s&B2I =Rt~Rt:ǙFcJz [lҭIoҌ,|5;6;4DNS2I66Lkm ol<(LB(R+$Է3OIZ[ܜ F64]^wŢg$ g0ι"UiaK;OnJZ$en3>% #`l,xuC3ӧ?@9eߥ$RՁ5r0|lt,r<( SK!tI+V7u\6ÁmtHzG=ncz .-i*\ {\.tk`p=\PV ~}3u_'ѻ(\v@>g*IE2xd9xx wsg]\oj˯7U {%V Qhwvefoჲf6>dcsԾgR~`^M[V?]% ifwtBA"4$ʾK3D=5_$_kn! iDDh~׭:j M$BDuS]A"HՃD$B3EI". ,7BU  סY"J!tIh4hKZ=HB3,B j ,0L1NA"'$BVA"4P;BhPgz!T$BV>O#t maY"H%eUN$ W BDE Bzx_,D; !tAKD=70"H%-$+a(.:#!IYIJEg"^l $V+:VÿK!.(CQt&BK!.(C \ FǿK!.(C*4VגD]T $Beߝ(IE-$XDDxHeA"N$BE"B薲 o-="n)K,/iP 4(I-e ]`K. $Q[/: zx6t#*G]5&י{U/| L3jaY`fRtۧkftlUѳ)(E0w%.:/ݴ p >A" Z}GwHZ3g*MIc+< q]$-8!RYn6+:Mm<@ > O,iİ>p ^K;0p,:= }Vp{Bh@T`Tu.vL?0e3{&AfV9kOE=`$p>/i%I3N$z5KY-oPҦi\E<_㣦W-IZ X!t[]xȢ3D3 *=^$ݥxW9p.SI7Xz B7&HgE祉LJ:B]9=vڕ3M/UI=, xvKgs*v!&H$Si afNtbZ) O5Yj5-ONUxyB1H|XEgY cG$}"ivqqXyn nFaS"H#e 7F{K`fxit`!ҖG$-"i?IKT_azye۲rc%}CH|q 2Y "o[XtFBh,IH 1&.H{ΥzL \kfSO=3۴輄ʼ69٫EgxWgԳͬ)5ZJZ焢ш4M!&(sIb]Rt~Bj{65[!W6 yвw-CIP \n3n Xn!Bك?e)sGYt^Bh'fv>g%oo|B:H$c]%-ZtFB1Rufn C_Eg$/E磄>ώ!Ɣ lf6ԓfk=0m ǃݏ={>5| ['(:iNIkiW?p3TW T\輄n%H\ff$m|˰>/.l^D`nq#sfI;>":2s@N4SOբ3R̀S`0 Ӿ)`|VjI{vrQuTw#l)3KW]v~IGاPr|ޖt1S|WqZ+xpLϸN|7IXsO"/|J;N|0%x+$m 'iLO'/o3{*S*2/4_bq{^o 10X@I[Dt~Yql p&^ 8BҩU00 _O"YA2WMgfwg)$rx;ޫq=q o.E(vn̦wǭXdhfT;Xo^, d ժv_RRz:U!*vg;TY5>ד + /ݔ0 `L+Y9=>)i {r4Ղf6-%5R7p_,PfIKcVZ׺3Oau"Ma0ުy6=@"|`g4CM(3zmj/I:̗Hǯq7% b>IL(i>Io n.۬K><IK+޻n^mp~M.i;e }'I͹ U9K'$M&+@"|W7۫ܿ"N.x q.0kV.HmWy{U-*Ns +,o `应*H$+ubfz\>XWxD^OpKf;h&u'#Gm{-~K2ޭ6@˷}{o~3 >Xt\ G-sF.Hj /dN4yVƻAoJGi135 f>zE%' UA"oJrUx@I,IWYjXUҹg1>eLn>Hl{>3*1&MT8wz5|rq}.1tT.7W]ifGKV;O$Iٽ݅Wl%?jU䫜vǻ{^@^n83v]X=Mr  ֹtVWϵYO]eS =wj3n.tP=#-DSB&i|FONI/Oן/;+6j9)tCҾxW'R;xno6 \=jǀi=8Kca.ׯWMm$NPgAiAV3p~Gz+sWkt"#4V=I-iII\iq۩Zm/W5F!f4ٛUҍ=6mz*^#۟=_؊/x50vq]2~4\G}:ꑇ+8 r/p54o6q|tpO{a|[5ͬ{fxcr~TwAٽ"=ߠXu+'i af'3YMSxm^Fӈjƫ?4G%v?jۆR?݆x́`؛Lg'ca*;쮊t=>ύ56OJ*Ew4m*F!4vëv{B~:W OENū4Wx]YOsi yh}LҼ阦|I*wЊcWg"c*;vfZkk6 ӏ§gjy~W/pBRKad3k$p(ktd4?6 mV)?|x%Xz)cm[ݔ1 >^t~ZK~Ru ^u.w7OƫtLxUbfc%=-̲ @փꆊ*\ٓF6>UL3Ur-0'=Dj -VGHm734y^6)6}M חb"I:ṅYS U  iTt~$O,p]_lB}*HH:s]t~¼BG-o9.>bsVBc-67#@P~WH.p20jϙu7B Ab>|͉Ff4͡Hz$~!IUvS6HHuEC3Q5$im:Vt{̞800BNNϏf~JߵI ]I1SA$ {n+MۡAȎ!TѯDr:~ }(U';ι8pnnיfj˴}He }GՖ >/>:o=O|@}@gp3)}tγ泤$&ZIwH:_$-,JGP*0"h3;%M颺i^᫔SҠzCH_m` U|==zeH~fcU#kc~|*oÁ5u+=ef#/WKl;3;_;5BSD`x9`i/Mh_wϟ]u7`@z>Kt;%4oh?TR5ifC5x3{.= rHZF^B豸Vaf_WM ``|^ “KZ:0u0= [F5g IDAT9m L}nJ{?O+Nt!4UP{~;|D)fvaY+ަ0#^8"Bt8.^TmRyy~fu}_;Iz^f">'^%oF$$S[,@`f7s~&itl"p^??>&7y5r>մ xE^BA Rhǒ~Tt~ZUZ]< ~GU | nc06|]03o[zMIX`FYixuUL˼,{x^z,D7I<~+MOqpx !\T7u Uvg0GOE}D.7&)UTv#VBh=dfnxϓ.^lz$|6-P2&DFfcf^!ik,| !DI:`C_Ei%uVV4_`!MffOY6 ]NTAOHnNF6B^$iA1Gلbs5«VNN0׋UDIٻfv*4`[$+i`٫IIHh}3^}̞4݁O]Et]IK).lmmfӋ]QTI+  efj}|1olFg !Jm#I$3{"|fVwλ%8 88?B-B҆Ec@$M)!,D (HoEhiL$`lz]?v&Dul 7?DglXS½B%afS)=x+xUT!4M$B!A"BM$B!A"BM$B!A"BM$B!A"BM$B!A"BM$JLn&|f'~"H0F%-KVTc]D(K#N4LRͿAv4_>kdb, %=Ҭ!Z`\/2}BD(93{Mҥsɽ^>mՌ/øn~3oSsBh+$ÿgEHII:4'4;v! =.}No|_b%m |x!D*IfD'7&H3ິ.;P$ڄ ޅkQql` n 3{!; iYI$=*!I?LA%;>J$I/JzFDI+ckH$QTIHt[$,I'3%iUI%=ܑ+si6tg%.i8Pҝq FChC$K7:(iy`(0 oX#wlA$fJ}P\x`t|]_.w$-\ | x }0qO=l#'!s/>`B.H TBdf`Ie)]g۸:. \; qcWAC}OK̼2|6ܙgЏEhCm޼Fg}#};\%[ȵmC>B;xo?/R}IOc7#=w}:͏{w-wRߟ% Ļڸ!fJ^UE6'i]3|)D!:BM$B!A"BM$B!A"BM$B!A"BM$B!A"BM$B!A"BM$B!A"BM$B!A"BM$B!A"BM$B!A"BM$B!A"BM$B!A"BMJd87IENDB`ipyparallel-7.1.0/docs/source/reference/figs/ipy_kernel_and_terminal.svg000066400000000000000000000305101412533144600265540ustar00rootroot00000000000000 image/svg+xml Pythonexecution stdin &stdout TerminalIPython JSON,ØMQ IPythonKernel Messages ipyparallel-7.1.0/docs/source/reference/figs/nbconvert.png000066400000000000000000000565051412533144600236770ustar00rootroot00000000000000PNG  IHDR][6sBIT|d pHYs O O0ntEXtSoftwarewww.inkscape.org< IDATxwXVH\W=pF˕iife~ղQ?Դi,-4g{V 9XkI 7H)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""i)""""ic̐͝}etksonX`c@kk_NёX &00Yʖ-[8sL1`1FERDDD,Q"Yt1֭[sIIrtaMUjUVZEbbdUc*YdU*YdE*ـd5*لd%*وd*ٌd*ِ8M%2R'D&ZKTTT‘#G2(QB*"""d\v.]pҥTmb jժ38T$EDD *)Xd ͚5СC7ڵk 6 ;yȘ ///5jΝ;iܸ1Km֯_Oݺu6m?8m۶*"""T"O?Ā8}4mڴaܹDDD0j(7o޽{P?o>>>IHT"S'|o<4jԈ͈#ؾ};-Zp:n\,X O%2z){ (oN Xz5o6rTZJ4)"""G%2 :toF5CT^=#㊈8J%+W2cƌ4?/ڵk)"""9Ju͚5#222]^t:""""YJu bРANtaJg-[Xf {PBj֬InpsS'A%2=ȑ#?>$Y&SL}NDDD$DŋjՊNf(S ̚5kرc:ub4o""""J%2=K.{*U*6.]bĉ?}{,uD _|Aҥ?M/*{el޼-ZdrZ1&/P(  2%'Γmc|jEf+u8H;vFfR5ضm[,X֭[U"b)t&cf+ k_Nj1@7b @2cK`d56. //Tm @XXXecn1ƌtF2+ ^43teybJ dQ l7|et D#ɨY&lذYv-O3t/V$AmRTaS?3sNĩ ۰م`>@c?(G:Ӗ?x_^|Q:G `|̅"9y:C=Zȸݳ;6¹"YJd2iѢV'`IJFGG2k,hԨis'cH`ݽ)K䄆E{y/x'Z{h1.T,`*INDi>`8J%2~'{1vymJ,ɤIׯ_&&^"1OS f @SIjm Os~.ce8c `&xnD7SIj8}qq1&4KSog̟?uֱ~9BbŨ\2ua85W0t&CLԮé$-)o?mˁCos0G24#]1̛7u8EJ%ٰrӧ @]ANkYiIeM#0")RLAtt4;wxxxPdIիGrSF ;ts9$j^>De]c?O+cHz_+^8΋~t2dJJd 70vڵ3,S.A!䆯Ͷf~6g //؇"T"OʕY~=Ovm۶{Iƍ3)dq\E=LiСgΜ9у2e@HH,^ ĉkT*)h׮'OfԨQ,^ŋ'=>l&'|: O?4{aȐ!T^ooo<==R {fH1SdƘƘN*4Jeʔ>Z˙3g(T-ɓg16<"TZti$05ϓ''FC4fcSDr= Gt//"ˁNqJd*3~i6lQ1QqwwOpÇsaw}w1&Z?-z$QQq14oR QZ2Yᬶs1o1Ykr.^}K"u癜&m6mOz!"]vqe6mzM6z*JpWDDw$I :˓h˞濅?.\БӉ+:ڲI G#5ʗ -^֟<µkZhώsi<=)Sprݍ$Y>?x >K":ڒ'QQs)[H6O˔.YyvOn,dZ{@+iQQDs}mtᎻ.Wւ;=p &u dZ(>ypI*/o~fkc^H2'(ZG>Gpr(5֩E"HWg'jժqTmQ5jdd+O<|wyJ*UkW;P5o5bUPHst2:yM·<ԇq>]~^4zjd l=G$x'+y [dǮq_[>4|Ӹ݋̍/BڏRPj4d8uZ^:-STk$n{^;S3uZ@9w>7o;O{ٵ8I:,?*",KsZѢEYh6mA7|'/_]fbʜߟ~+00]HH4`?ybʖ.`OWs,%zR>[z3{d݆LfM~y{h۲&;vǧ7c)/.Xˑcg͇)Y bY{i^z7=4FCNewصD4;PďҤAe\ c(Y ?~ >ޯg۔*Q 2r\Жu5;hxԨدOx!jU/ʟ>f1ݿ50?wcaֹͿ`ș[| 8z@ ũ_VV.scbv4 ׿0>ЁG{,72k?g㾏D$D^Jxxxe~mN:EPP ;D*TK.q)VZ̙3z*˖-#O)Kfʕ^M4~(**jbZ91^^, ׆(_6 nkLy;:˄1PZi~i~q$8,xo<2=*s|ۏPp~ ceޘGwbXN׈@ԌY_룢y/}=^/nWxՇx/6ypа5›ޟ}`nAurk cL'`1戵e'iLvj1uRG; n&VM`3hXO?%kDG[LOzti |6mf~cڝ.>y}z]DϿejq~^~YJ}^|fZd &/Rx'f:üE&Οpmɟχ{z)OP: E%XN@V_^7 oihY̚% ؃S:2۲2tV\oCҡM>#$v$ݍSRHOyJbUs"~q['ܧb%\ʎ]Gkx^>Dy H&RLX)z*| >Jd:h۶-fr?J6sEӺEM RRIn: K]C?c{%Z{E &zo%.=, ic,/]+h:pKCiDΜ@E<8b:zFҼ6]:PxA~f >>}&PRI/wunbblޖ|I|3>J|˓2᭯@>;ɋ;kZc`m='< kbըZ!eڌe<:8J'] ؋bOP@}H!_LXI}_L9 ]o'ZܥD$D^7aF.UhWJڵmۖې:mנޭ&%1N-J/I8mQ+Kck]wi[pZ/<ɟχ*KmD&6WLfo" C޲z"WyAxo$;sKbx1}F=݃~˛/f?e+xzȑ9ٹq~|yYz^~w,u>p(~ߧ<.4,hhqsKrŌ¿2Oh I}Hwu \r9培r ۷ [ d,U*\H{o0^xge[b/S*ELթT۶ms"f1zh;SsޝӛtWYWb ~_3ٟ̦ m5}WD۾bo=+бm]LLt Ouilʗ `|4w?@ʥx?}'k =\Ft|}'eѲL y;vG̩x B?sаzvm̀{ZM;)\(?vZh8hȀ{ZRJ(2kj~ۼ*K0qjݼp5y,4?g^R-6*=4_c`mݚ$gؐϗsfX4o$_,ڀ6NG cqr74Ww}i3MO%"Ϥ ysccXtc9m66mJXX+V^˗ɗѧ6mڰj*6ow DTPwwwh1cCp\-bc̛x|tikȻ]2.H|ltC1ڵwx!qfώSq'7fJbx[m6q:HfHd ~߿?'NDoߞ+W&ڶYfZ 68^"!?1ԪU+:v`СC\.WRrǦIzkc3 HNsα}v֭Ŋ\șˋ˗/on5t:O|.!P:~ܴi=zp\ool%n9  Dcj_/Xk8LD *ع3 5Jf׮]b\4___\.N/O<cfZkt&U)[+i)dTP'NeMbǨW^eiΝ;G6mCCCq\Yu֥[IfO;'C ґaC:Z3%Kdɒ,YիW'ʕ+/!00BCC ?y䦰ɫDReB92:HL{Gi߾= u\3qoƉ'Xx1}L0…SsIn=s#aaawZko~?Łf08F%2]veʔ) ||qڶm1~Ç;3 octT \Xk;FDI: O<gԮ];www*V]wƍ>}z<9#]v-4<%/:rE>d̦yPx \l֭lݺ5Uoժ={̨x|9 _ד7.~Ý<BTTѧߊJ 8}4K,aƌ,YRJ%;Miܸ1{Mg ds2d7.~uU\E%2 >>>у;+Č3`_G!d_ )П>?!vJH?$WѐY2yiժQr=k%`#/BUIznYܬX`tcVly#R7Ӄd-2r8A%2]tNǐ_\Q|FH::s?+v;oc?r'sH:|%?pO'd6N+ B ԬYnݺ\Ȍ%0)_roxz;IëSNfI|Cqә$Lz[\ }[DѣG9r$Z65kdʔ)o>Ƙ/OV cɿtiޟ>?_^t2S|OA]ә33hVWfK)x8p]tСC̙3)S0sL.]ʉ'xo߾\f3v0+t3&L~yG4dkq`"kt;SgrbWLWXVkеc:KRt:nm9u&%nٖ_=BmCqjہ>|EK7ѼIU:ұm]*/`Ka7_˷p|'(eMY)onX`LPPKf́6mڰj*ɓ?dС)R}?~I&lذ`va21$S`>!:ڹ=7s?w xt}d.2t^&>> ȗr6226 ^֞d"YF"S0c իǻܹs;Md91Yf9^ sмsH> [s9{N;$xZskƘEC@ `b_+NK;iwP6q7:v:HV[oo{;w2{$+Y$&MSNPbYk#ww1e.@]~-*ON{o`>>4Ɣ fm?Yp͓'O>}x;ωk׮oDfT!}68,~5Dۙ?>֭c9rbŊQreԩq:\w}`9DZL{]{Sʕ[v1ωwa[k_LD2Jd*ѯ_?tiI3HI3Ύ=z_NժUOD""""YJdLÆ  GuE$DH31"|ȑQ.uVÏ;8446$H&1.Ƙ#  )"LED$M7nGFŽ;sƎ=dȐȨ>*"9F"ED1x{yy}aʕ^jJbȐ!sEFFa]@T*""c<<<͓'ϐEyi&nݕ+W޽{ڵkυr03ZXhh訠9spI6mnݺ}*"9F"ED$]c~'?ӈsZ{l"T"ED$cZyxx,2|9Zt&*""Y1SMfn@t& l]I'"y8@DDRT(t,kcLwI̥)"M 0=z8#K7n[lFER$өDdիW{NRnvڵkHv.w:Hn)~DD$*P+W~7ƘT"ED$[-")iT"ED$+P+VHw8H)""9BE[I)""9F HRߟ+V/c9K$Q"C̈H:RIER$cDH[$6lɶT"ED$Gg"IO%RDDr$F$c8K$[S\Ϗ+VШQ#)ߩHs*""k|rIt)""MdYcd;*""x؇>@dK*""ҥKNCutl""9ׯ_:$L%RD$hڴ)VJ7˗/gTX>}PjU *'N`Ϟ=̝;`-ZD,D$P% ,HV-_x饗ի-3^z%:wLϞ=ٴi+W"H\W_%**z͍^{˗/3eʔLL("YJH.qF .L%RܶJ*l޼9cH)"py.]+cH)"5k֌hF۹\.F;QbFD$1b_~%ӧOgݺu 0Zj3gΰ}vf͚Ů]ȟ??>ӱEa*""\ӦM1c?0=M͟??ͣzꙘPD" C9r$k7n]W/2G;t0d0a&L… '(""T"ED$I p:d0cLN P(h2|N;k_FJ=HI 88}qy4h@R$< q8@xO6>޴^u(SDQQQ|G3gϦqLpp0gϦpND ^ ޸P?^^$3ӓKq!$QO3ƌfXk2;[jD `޼y)nWD fΜܹsy3!cL`P;vYC@ڴIqw-<dzGlFEDr? &P~e^^^7CJҥ ?:tp0dɅʗ/ȑ#9r$ڵc۶m={LfÆ ۷3gpwо}{G39SZ{"7H\W^RJHHÇRJs=5z7&خwx{{rwzLcJ:BfT"EDrRJfGZK߾}:u*Sdҽ_~3{LN //?c|"H\VZpL3gtRJ,ɲe˸p~6k 6dfDǸ{zzV^d='%Q\.#^jsIb ΌhY[oVXFytDr#|[l޹s'M4Iqk׮a%_|+(X VΗ/_OOONO%RD${eȑ\z5S߻BRIDAT;8E[n%::uft,\r,_cCN)"ծ]uaf͚L>[r9BBB.ݸqc^|Eo]XXX-ZHNꫯ<=<<ct:hH\o|=z3t3x`f̘Ge̟?]v1x`Ο?@pp0}۷ogԩ8qիgr󺏏t!22/N:1}tGy cLsk=7+Whۻ󰪪o&qQ+ (9<5C9$iZY#4)Z(DXÇ[...lٲ=zرcMm˗/g~~~lذ''"c*VXU~~~6(s 0 #1 9Ev xy-8?]\ u,Қߙ 0E(Z(DXkך}ytEv5jp ֭[ʕ+9{,;~~~ۗѣG c>4s-g͚e{y/r y^׹]4iT 1t`lll_|+C DX9zi 4`u׮]#>>h-TWvhٲNO:9:0okn&" ab<_5iI㻽'ht}R+Fަp:Lfo1 #Y[-۽[!aoM)iZW:#MYYټ5g#5j__EZZgkx~ 5il\5|~/>>q_Ȩ+D^G<._WRHiʝt&RDD88;;[=[nulѢE/mllzQʤưr9gEѤ4 e[q+9KoiW}oȲHLJ;KIGK0'?K֌ sn-q62Cvޭ[[DR<٥11o燓+Vķ'x(D+V`ԨQm`"33Xn޼?@-/0`YGEEѫW"K..֭֭[KtC`GA*y V)O횕ڡvv9K#23Or9 u etFNСM=ޟ1tɹ0\smȿ78šrnd:~@^hT[[S8*W*#ǑEvjOJJj:e`ċF?ʔΙ^mdF>/.Njon韻f>ֹzH&L}̌94#v-OEE!RD=zSD f̘!C'<<+VpBFAӦMPD UV$zxx8'֭,QD|ZZՂt,:(]ʙ\N횕Mϛ5c1^ϣOgַhӲNu&w):6.ҝ dgvײ.ܺS<)]ҥvnuglll8/Й~of*gqp_>/0/0|11iS{t vݿ? B﹖uaґ97snaԤUEkIZ-"b.]JZZ+V`ԩy$/ ,`̙$%%矛uZjq"yG[={H. ]qW8**x'~1ײ%j͉Y/z~9Tr/OuOp<r|s~wwZ5+k c{jHBNFq̥\ ͟M౎X9guvmJ9u CTq'GL?h"EDܡC+Aby}ovfzza&Nd63'6;`ގWoQ|xVVV 0~ْ7'Do\Kz;H^:>9gt7?jewJy ukU1$xa@{}/`$u*馶-jSww0aT{i}&PӍ+TΦՁ3v}a8p:uPLΙwr1z왑50Mw[ʼn͠wsIy?/=߉GQʼnG}rmI3nd7ڴCعX222^"[εc/. >u|/kC(jxW#~.e|'ʖ)/^(N^jewigc>y9x Xv5wj׀3s9"/\ [Ԧ<|{:MܕJr"Q Wh;$))OWڷ*a,$,<>^4nP=V4-` ΎL<ݣ]P(DX7x 6aN{{Tɓ'goٲ״a\t=溗>""/WW"[}gK ?9r$M˗pŒa俋>)vnn9SΜ9cJ.[n%(((+++a,])Dp#((;wn17Ӈ~O͉Y&%Kd۶mٓf͚©SXlivaUF^̮afalp)"y(DvbС\t)O[LL gݺuԭ[כxٲe6mSNe۶ml۶-WVQ.]"DٽkɏB O>$&&R\9zMڵ"##:9}4ݻwxzzu)SЪU+6l ~:ӴiSlmm~:u|dee4 mK!RH+ȓO>_c^JN8qK,3Xڵ]vK,^#88شwPVgXz$@ ?=U߾} U'fJErr%brƯXPA kDD\nݘ;w.SL!$$デ\r֮]ϒ%K[.7o3.M[V̝/cӸauK"RL^~PrSNedeei&6mT`_ƏϘ1cm5j˖-+RpPuhʃb"u9[DOTR͛7}vjԨa鲥a|-]~{ylAuRT0Ֆ"EDwwwV\ɏ?HF8tL8 &L@hh({tb^kדZGBb˒r5QVy[I,+H1iժsZΝ6mJ`eg tdff-aGUVK񊎽;60v"0-X^'RDDalܸ:pqvAZ?> pb.0ҁ!666!lT$ ?y@MڷG%O7<=\)TȈRTRwK1ؽGB":00,P^ C=~;cΜ9Fq4.\h.] WWWc͚5a˗޽{;ht8sLq>o<?~r w] |Ywn]r&00$zSxl޼7R|yCÆ Y/f0 #0Q/9~"by` P0IaܰpMw""Vիxbo~ѡCFͦMؿ?CϕJQ1 # ̳)ON<7KfE r.W_8kM˖dH+׭[7k *qFÇSuR È-]<|"EDܸq33S5"МH1B9s o>۷h""#l5dX< t9[DĊdffGmOꫯzyړ#11KD"EDDDl """"b6H1BMEDPhh(y^ONN/"==kB&!![njDa)"bEV8>>>EP<"EDH2e߿-)D"EDDDl """"b6H1BM!RDDDD̦)""""fS)D"EDDDl """"b6H1BȽO tٖ.A䡦)" #,,e ""@t"0,] image/svg+xml Notebook Preprocessors Exporter Exportedfile Postprocessors ipyparallel-7.1.0/docs/source/reference/figs/notebook_components.png000066400000000000000000000743761412533144600257720ustar00rootroot00000000000000PNG  IHDRyesBIT|d pHYs N Nw#tEXtSoftwarewww.inkscape.org< IDATxwxI2Z{#nADQEŭПW-[u/TPAŁ(*"{:IZ:|ϼ{[Tc1Sb]c1S,3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3c),3Ɣz"/")"WHXcJ3 1e i^!"7Ӏg1x,3Ɣ=Tu4&p"R1vE4Ƙł ";du"NlLigy1eK@U]De "]TuSLJg UMUOSڸ-DUgXg3"ߌwNDe[5{?plv@:$؈GSTTu40fYg) %?c]-jg{~ocl1PDd!.ؚC6[X$c LDRUmY\1(XM1(V'b]c )B_\x@w}IʘeA1L8rd%𡪎 i hʶ7fjyWUŴT5Ɣz"&cpi-]ϔo$"׋HؖИ SX7{~ڢv> 8Kmg5\SΩ,oz&\޼"\&"cZHcɚkMxN6*ǴP.` Tx)?KksO݀:.*EuAi :uMD:YRsU ][[7yy[U/UŁ!dݟɻ?:|SVR5YgJv07MPDʉZR}@]Su׮Vz#\-#sn:%A""RLw){yX&UMeg}ێV*"DU3mN,4o Il$nj_VjI dضu{lHn" 0j_9?cR*"wjʕwopm@bXJݕʺkc|ͧ|ݗkfUxvz)T>RE[ |j5,Ʌ"2JU%$kGH{4l܀լNc m.Lo7mIFmV%].9W{wPcc{´mCA>úL}H4Gq1)j_E 3oot5""AU}[U9 L:ƞn+.ʕ,ݼƳ<⣺3u?)W>Yu /[8oe\xn||^t Ԩiݮb% [}ODDɪu#98h>jfqaq0aiܬQIa}W.wFU=]UǺ\{ +9@D>Uզ'w ~ڵĺXƳy&nz"""-P:/kkʏU{ T5k L:Z2M[4ٟb{WLBD9Eq\KD5w˟j>SE]@볯%uEQL@g_gi( Pf(A^Jr⁸QvXM[~lXؔ"2{շrUZsB)<<(@7a4WsP񣪦幽'?u@F?y7*V(˘"[quwi 3GOE} oƋ4kx{KUU/rd+7o{ketmĺLa"KINNT@\\%*TUU@*&Rn߾[ ~ča$PމH%`:pݷ>eu>v Uug~+ 5"\ʏS yy>Npȹ<]+)v|1ϺuEy6<xl^~q]ZǜwjwU=e2$'nUjRF6KGIRfUb8Gh(D0 3}; -_/ U>sٰԘTӗu"2zޙ}ĺ8&nz/?c7@wH\0ڽo*.+Рx{=!8- ^|UaTwQ[D(XPq-4O{_T`򄯸jM Rw\Yj#kФIU ܂7].4j"R$%k T"dT?3Q Vۀ7/ӜMey͛}3gIʊ@ >%ΪϾہ?UuBqMDpwnn\T}}H5YX}Ɵ_H5ئ >|44v^-&O.c]$Sp/o.2$'&rܒPZ=O7\Ȇ_W=D5Aho/9Ʈ}`$͗ K^X'.vǪZjHE \N?P}_yDd4}# j?-ѐt)4&"R$sIg:_|.k xݽ+mT,O R;ȧ>t:# e.%4?V܃Bi "AT =DAOc<3} lXjw//DS.2i6Т:c폟hO"6P|w:- q).eQ"'"jשU퇿HJ&Ʈd8|99EHn_x) .AsS?C;E5rˢQO1CT5%e)sצ$'$"?֬ӡə?!߷ன2 K{DxsG~ !-SJ50X໔z~OJS9s,+N?\p?SO,rpT)@@5Edx7[@O|Q驪O2lOO-a2˿$ە4*3p5WD¢nr4;ag-Oe9 y)ɉÁ-۝P/+UJ${:Q ^_H;;$g3Ћ@/.XD`i4iڝSOyJ."S{SJ зπ}gJ=1tAVܬk\-Sa#\׸:kjUKUgs)!eOzZ:_L&MuQôblV֬^v'JMQn$VUmUuUvK"rh];}Y5oٔ6[!"ljHLG͔ /%9q]{=o?̈\6oᚋź(yk 9,\Zc*$pd#Df"R3^J"`0ZDyɲ p2F^7}d1ڔz&%y)ɉ7c?D/+4j\M\wBԅ5wٟ;#& |hrߤ?D~5QAD>IINl&հ~}lJF TPx`,U`,EloʼhXn-?8E"誋ouK74lilXDj|ȍ^bbAQ5Ֆyٚۋ/2KIN-W?s#QeU,%9ђ9_>ʃu뇟CJUT+U?U=k=9"򸈜쥟ȓ4۠n:һ̛;/#ߞ~yt\]W?/PG` n_DkCHQjX9&&V.ӚV(uA^Jrb;ut8p2X_^`5f sEo^Z<g-{<ɪsxAZqdػ x'%9Խ1Pfڱ.)5GZjIJaYUyC oZ\3[?DVQ>UV #oJoO.kUO }ziX![Ӛ-NyNDm+d5j֪a#ʁl31("ժӾr޷$@@lͪtdܹ%܃ q@KBfVT| >q}F@`'r,^ )yهS"r@e97![Uu"o W{  7Z^CIa6ߛ瞲?e-5gP.t&/1o|7Zdm̙'8iŻ@ ȯ~g ؆Vm[s;ؾm]Duz /fѿKhѪm;.t\,[&M9p(IUT;"2ZU#S[_" x7)Ngh*n+svV{MZl5qշ2഑ANx4s7. I'p?<{1ǝi؛c[2έpM/e_ZIÆ0c$'zٰE%sORDnus|y"Ҟ7N_%5.wi!3#;F odG;II>гnznfL1N9GnjZu}S>q!'E9/|Wnnp~8aʕylx8kVoݶ%~[6uΝ~ý|4v" y5M>}9|;wdMy\Zz^vBcqHƽ;%\_)"r?zIkྸHU$ iLA /%9rhĺH%8%xͨ>AGcێtι`T$w$dPA#qt;.Df@8gKiy>\Re_N\eӺuo5h5Ɣg\}xA{qt{nvoˌ?بgs-:vݚ6͛pp1G L9/pN>SfuFy=@P('^Ĝrd#8~ܑ{Hʘ7Xnc^,WYRw3ҩK\=8&OgqJs7O IDAT9j338,\û'KbRef0O>~GdҌq4m$u{Yj֪.Y,]^ odCr}w/=:ޝ@y.p|^z68X۹ծ~iHm"7_㥧_#9ykG\ u*_~\FGjƾ9.u}7xajի_l^KzZ:'?WoVxvL南CJ,_߈wIإaRݪGx "?nrt,3eA۫Tkk֪/h({S,\"ϓ39Y5ك5k!]-x{9H?gS̛kSER{,J($Y!WsۊybU߽$MV$& ;`')eiJ|&%%2rfaMJQ pխw:zZ-XByM7sg* $E,G-kUPi^-ZFȨheX.^@<^soժWISY?ٛyѪZanh` ]޻Hhn r Ӯ]iYǸ(vkwщkK~3y!7pG2  L^mi[)clz92( tG{KO oHYc/?7diR!W=f 2'^r^S>1o|FtORr=^gk%vacJHiNM(T/.S쁔7pBCQ%D ~jժΦM[IAo 4ˣרFV9r*5UVѴq7uS;]6,"xrv ps"[r eAgӯqS5[-\ 7y޶-aTTiy^K 3]iTs;HOKGDhMOeV`>yo]۬E67s5[6=q9d>~Sܧ@)W JD>;]MOiʥڶ#BKpP.xrr.-AF{nַh,[T%Gu[n<%"E|XU]_4ڬf1sX4KINK!>! ~+8۴q3)~/;'jzKb|{7?-"c{v#((UeL2[鳘:ez\>;n8E)vk T) Tk4j \x~ =O#\uxz k")^y}`ukD""Sjct !ݗZ8kեvԪY;Xh>&y /9TDVg*G'n&`=zyb_5I0iF\=y ymcUJ& PUnn~'| |:g?tM~'|<=zF|ɗ F&f72oX]s ǐӤyc&|9s5ד;eUmTM7fՃٶu;>W{/éǜǦtQhoQ|5i*cތKoqɺ- piWpiWD答"^xc&6#}W&_2w@(~7xAK`AL4V㚫1c#'mܨ84%UG;^Ԯqu_֏5od-Y5!I([Hm6ۨT&Z=%9eRKD:FHSDEқ4h^۷ĤGB,^/O>>)oQӻ-) ,gޓY[4yALa<^:ot>ϼk."Z~[xc+5jVJ9lO>?=zwcؕGY3kPW>_J8u,#ɔϿLиiCzvHz05w$s׈":\D#cF1K];*;@/;o2Eʕ*ܹ5J!.UGh&M(׿U;tZ4ICCAԅ9/iVCv*Vtoo^Mz?s2ѺU;|e.Y =]σ^v7/t$%HrӼeS>]_̜j"ڑOٟ1s,oIVͨQzd{R${a47~-P!vp嘻V횼?uZjF8|=y*[l]6xQ}1,[9+u7}Q ']5?9d¥lݜuFQI~nhծe=9SDz{wf<\?g(]X]xu d>:" AR:!PvP#wJElnY qwmin{Lzn~|JE3 \9\{!W'W}NBH#SՍ"rp5n'}U2I}>:tEdYΖko1=ơ?g_JfyTIʸ̱ 90o\íӠ~ʓ,] b=ǞȱؾcvHv_wj\a /w^`l޺uӢY+NwG=j&e[0 ppù!TKduq22ygܹw={ٿsnҠ2Uʑ=55qv>Z6i&S YP!!ZJwҬECj*<;=w>=`ú䲯ゼCht+ rbT,.僮cú4re ܤlݲ*UD2 HݙJOOK'>!>0e1%$5HӬAPU Q! 7 #ҕ^ DA/s5z=/ГP 6 D$&?o~:О^gDUŧ^?gѹK.BN>_|Uy|= Q}CRSwql^>I> JyZujqm3eҷT%úߚgP(S=S$THsEFA*TED> *7g-R˪^B>DsB֞4,HM^VtYA`(Dl{ٖ=% !+;e:aMOa4*VgGp|~)͛򋮡m9~aB!.z 㰃rQthۉ0~»<#\v9L'ڴr]Ⱥы7P|v̜5tL<'>`0Ȱۯt8 :5m˘1s*sQ#tGtj߅y ѸaSƾy$ kTo ^xI}V|՜um۷hZg % 7b kO3יխwdnV޺EKcv*UP%mצ#tUޥ3w\)_G.am]x֧3Ogޥ_5nvu|Ϟy78 7J|H3`%n>"n8 wىT EDK|{?Ϝ=&.o=Afy7>;L:t<+|O9R㟿ѷpƠSYFdM~i NZ{!7lbc/sKnqN+xN9?;$֍kX*E*  f^?2^-\&psFto}:n<` g`OBHO_Q|2#}gWśULU#ܸwe";a:Ȓ%ǫ}@z7 N?5naSẌ́RJ5:upXcd߅bR;+}7 ]]SAO]}0v?HVV~fόz|zkUsU=w^L'+]R`'"'4e:Ury0 7^};p??E>[wp#v){틚1)?GN9J~ϝ3;˯jٗoɑU\0Xל lJ3oxёN= |/7,gC{SOlIKM"kՉ z8xظ)2B!zb%33#{qPmdF|d;*\sπU"n26&_T5MUSƸ9~8漻pNkEE9e..V`‡dѲ[ma҅tڋ^y2GM7ӾnY_kW?{k=/Rb%Ox{Sଁsr5C/[M4N qc@A|)V72o%L \n;Z.ûWKH:&ˁEd1MDfZ{a47d7Xd^}\ۗ.vUHψL֢u??4чiY]_y+%C/sWdҔ +|4q,~OkM'Хsa GcO;'U-sUgܗ"%.OSy5Ņ=.&EdX/M~n@r\S"\.]삤k72< HMݕ+ȫS/=D97mU{-]i &MJF"]Fb~ IDAT y9v3IU R'd<ɋ6nx@E /^ 6mg6HSC详jkűtկxJ|r;џl7^k8UH0dʥDذq]F/fM'%&ѥ!:gsMZz@f={s)3}淜uO@V=p--w3bCi߶6,YNknk{6}6)J|<%"pp-0|uwÌ#>'Q5jC:WvG_d\{u[ǹ{8ȻlXꆔ-qd&=}{wgcqu3eƒ}_j&PZ+RJ~$RhWEe RD}c̘̹~3cV34{g{9x뾖jT*_2Z0K24&~p27\kLHp|wrΓZ=.c`EV̀Igu!|x[BiLSSر/5= 3<>#ud876c ժؼԭ]vf?try{fxj>B6Yb ׮"~oN (g.FԬQ5կۀIoO>?,!/ `Uǽir1m'ӿc'cL0No>>c"SVUi_ex?Oɶ^t$jӥ^:=zQu9I[nݾB' &mXr,{FL|:]CWsY \wee.g *k[Xz~I2c /n?ttewR7= "'gQT\_)U2,Y/|̠5S>ZKD>\;nnD;m;guv,y.UEXx<Ǧjj?~_ Eh P:K*2||H|C)Nl"vyu\֡7?^ןNJoe6GQf. }w]#g@IK"~ߘҍ0_>MKæS|,ړreK1c]g7I˲y ===i_'nw*'{D[\@tH;rBHI6lk`aOd @9lOn@]` MJj 6ܖXѳo Os_Lۢ4fxr}w*܎HLL⍑o{Iޒ*v֟V|1%j-75S_d߁dVM {V)ۖQpkR&unm[ ^Lga/{he:K7xx =M!Ϲ4,R #[%i'8k;f q'o\CdPd)];^:0B!L1 fOөT '--)nOK8y-܇vrH8&)Ņ1&s`xzcѦw^ZI+!w] %%V|?X ƘK:Y?`lM8u'unLV|5+.UG|_>wpS;o÷*VHYc 7 Q#&cL7?-@.'b9w)Yks] >ZBݺ] F[w}wd7sXb56ldoPN:ж}lǷ܎p1QqV/BUhv9y]Cdɼ&nׂYcՊlݼҥKQQ=."Z9/_ډ+дy ?Rmaƽ([J7 c29M]Nߝ{K&}qA/c\.^/鄸]\pnyh{-DـU~`DQ\=\OG$g8~HMIeՊmATT$֤n`&$$ײU$,\gظ~3JJ5_q&׬JZILLRDɻ#m;HNNZ+B l#rOHHd׎TVҥK|4%?-۞m٫3oǷ:K,Nvyp}wMW绯l2\kyС.tKkխY`*ō_WkNtѩPwy~c_}.ϺL]ź3nz-yɏ1>H yyP}VzY0lN`;EpѨf k*4BB?p R\Pr'811_{1CtL|:?>7oP1ヤmy('?=vk{ʛR:2[^I [w![Ve9i#x-n/jBVWt%kg؝ y0+<6DO(7O9?qҢyn ۷zsg\3nqw1!CGz$nҙ[QVW8|5rž17#.v}5va/2"5TqF}NCx-~f./y*tA Dbb۵l ܳk/oO\ؒ;84)))\5lXLvs˝}YL޷܂v3/u䘐Ujů Uuº ptoT;\V!/YB7 (ouT8I%NBՋl3V,ЧCL+"׏מ̶/h2%-˷l fNEfg3mKBzg_gͱ[$WnT k7$vY}Ys_{xp}}:wk5n&p-jQ'l{>@'>ZN'/ gƛnȌsenii#xLN̑ĝxeC.lٺe2)i{Фbq IDNPߜG;5kn|{5?#¹߄:^nɳ絾f]4fϜ}%M $4{V Q}?ȒrNz<'P yCHhb FKC; 8SdXKyP2rʲMvoGb'Xҗ@ cL1\ OD2]rD`ӹ e ?/["I-ia<=U yMMe{>BP7#vk))TVի!n5TUg䤈#nc\j B8%ၻonjV7K v66 Ybz11c3699m$oYwfu93Ջ_cȀǘ .5ö-ϴwz{v gnXMzZ.YN61[W^{lR}kWu{B5Ԩyf )n-_!zcM8m*}Iܥm[+jrٺe>!)۟=o0ePzj;~çȨP2Y|]kw/'zM~cZDL%JFp=7s˝7^WߡEvl\:Ӫy-{3c3s>0~w?Hn(W osP"R|OMZX ;m漴]O-~៧~k^94 U+DEDUNͼyڻz}+pttQoyNg /_Y;˗涻oˢ%7k֮[7eƭlٴ P~m\.;bw\3883/L͕mGF}tI?vxr'8aŠbqjCUaSd%syz;[ߖͳ欛ɺZp_|?6)x!gw6km1&@#DF$w:g/̼t|bٞwOϿ{F\)7ܹKlhjhKJqW>{HOHt,4.Sػc˓iwG_~c0[=]9YhϐwkrRkomI0Eb |LlϟU<όkn?yύ坱y*U^ö kVdR ڬ*U'3X7/-NOح_Ke4n(еȿ}[f 2ocxQ?gn=s9Tt]F(k]h+Uߔ,Qr%4 .wRS|$&cs27{]-λk7"Ƙ[??4Ro֝m[֞e=: [y%.`ͺ@UkO*&1.cΨ2]=\U~a .:dɸ̅?.I:.GNs_᝱n~Z2{Zl!/t%ᎼGś1k?†AOƘFL5O*4yg<0.A7q1f80S9A'.`M)jƘk=zvOx8K\N:wMKM6thB^mB^^/)M2^Πsl֠ ?gk;n11fC~̔,ˋtݹ+.ZZ555cLIc̆* 48k,d]Nc/@ɳ>u.GN?@ة.ZlM[Kȱɱ.v$YDmg\Xk[kGgv&G.F|l;vn7׃1XkO$;o砓Ggo5= Ai3g~V-yɲbFaZr圇/y)ȝcL0 |= tIR1_d#~XkS\)eץh.c 0?e3 T$Ln&{Ks]SG7Ɣ.r鴦@i`/Y=6gL ޟ[gR?S_s,\Ҁޟl{1N!xh)Ƙ-re)lcA k<oʖqJ<͂vN"c=8zQc̣>{c_WcHnb(cg@K|G9o^@M` ײN5k| \o`cR""p=SKH d|K}(矔 P`Ybjږ@(0S!z\FYEDߝ@ B1>z3YkYkG8/p:sYG< ק|^2Z5Eˈ[YDXP<|~SK;`g<Zk:bEDN5YYD\P ZcC{]byZ㔌jc|ct>/鸉EDroZ(*|E$U־ov1YkqxF;׿'i=?pK݆BHsZw[ 7N9[kg c.zg.ckO~Jyy^D$1yfHDB:cɱ4?GƘX0Z|sk']3Պ)nFZD4s5hcL~70@YeZk'jeZc[레H0/Fي=_e0cLC Gg]B?g~2^ܧ~|s+"8ƘvwƘEbQ!?}`Xays1cKB`%|kWcccc*cc3;ocV+cLwcCƘ%@SKiSkHs\ WcL|}^㰩}Y[N^YkϏ?ހԖ#"`tib("ufy~W8/}OE}"Xlc7K` x;5[WcXk}E)5BDD'@#kM׭-N?wp6 +)<hܖtU"R%OD$XksDDDDDBHR B y""""AH!ODDD$)䉈!< '"""DDDDBHR B y""""AH!ODDD$)䉈!<35ƼmUcƝDDDZ8Xogiiȱ(cLkcmƘuWDXk]s/u.pqnݓkmSV8ZDDρD̤'"R Xk3 yG)DDI8ͩ.DDO!OD p̗ڔQ8BH1aM(0ݪ@!OD9-_N[%"h y""ň7Y8BHWkE!ODP)~>ZhZDġDDkV`A 3ADDfU8BH[k@#"Σ'"R C??~ZDęDD=@""c9@KDDN1r@3B':iڽ'qBH2&rVy. z"GH  F?;v tE>krcD <S ,,@Q$^1 4HAO )7nڌW15)䉈H(/ y""Rh z"ŇB=A!ODDN)䉈IQq6<矏?==#Fz*MAOĹDD[naȑy[v-^x!O<^HAOęDDꪫG;$-- k-cǎ%&&5kJDH!ODġF̈́ 4i\r ˗//ftܙ;#efz^)0&DD{aΜ9ZO&NȌ3^zeDFFzRELkEDcǎ\s5r7$)DD %%ʕ+vST)BCCٴiʕvg߽{#剈(䉈8SN׬Y<I!ODBCC:uj """"AH-y""n:{֯_!::N:ѢE@&"'"`>(ZdYӇ>T&"N۵""5~x^~e Ξ={篿⩧bԩ<.SDJ-y""o2`xlϗ/_f͚y P"Tjq4֮]K.]=CaÆX y""JʕٺukbZj2).DDK.ᥗ^bѢE-_~SLT'"N>y""kh":vH5Un86mDŊ]8ZDD\r_1ڵke֯_Ojx'Y~= t"Pjq%K2|pRDQKH1ݻ] y""G~(_<*TJ*DEEѳgOV\DtVDġmFLL t֍ڵkg={6>ϧ}.UDH!ODġ~ʕ+ʕ+sͅw.rnv֯_ EtVDġ.\ȃ>dǑ(?89x  *_CD@!ODᢢڵk^3cSF <'sbb"o߾uYE"rzhYkeѢE̟?͛7裏ƲeˈQFnݚt.]^GDG!OD{=4h@Zر#]t^zԬYW^y{R]d  &&&Nv ,ݮq3h ׯСCU.X F||C\.3!R\^ z۷/cGXXj:(Qŋ;вeK*W̑#G(_<{/֭uWZzx饗ҥ M6Ͷo< 2$s ]w]wU劈(䉈8@jj*={\XX)))4k֌hVbݬY(jժmUXk]H0&Ο?N:*W|R{nÏ{oAbb _|'tNTTȑ#Xk>"<#F`'|^^N8䉈3(䉈ΝKZZ WN/FDN <x<|olٲ!Cdch"ʕ+kPF^9S]8BCݻ:vIX`֭ Dy"p y""usN&NHΝi޼y]t#665kZ[laر}ݜ}|l۶Nf8BYk={6O=|\dUZ5S`ț7oG\r<䓼;,X:4BRqtRRRp npĭJ&M8xHHH(< )DD($$ƍwAddd=ƍsYgx]ү_?ڶm @ҥTI!"Χ)ED7 66J*ѫW/2e vk?d„ ֒%K:}߾} }O y""բE /^L.]>}:)))L>O> h".B]+** zвf""Ed5+HZZ$''S^="""N!C0fBCC)W <<ҥK~kiY3gP<ڻw/DDDJzOLL$.. xW_}M?ҥKPBr "'"PZ_s뉉aT^rߟ0tP*VȰaÊnq<bj׮]۷3ऐ'" v駟 >>{:nϞ=,\3P~:OD<)Qaaa|$$$0g̙븐5j/\ 93)䉈8H2e;v,FiӦ 6k&qK&$D"?}B8Phh(&M\r-[6H1'"Pmڴ|s^xI&(Q")䉈8،3xذaC+WSO=T8BC-\f͚1rHj׮&..)Spb{ t"@ZLDfm۶%22۝k/ٿ?ƘZEI˚8+Hn˗s뭷n<͛Osu"R(䉈8&<S~avAϞ=3f u tY"pjq+WrEqM7QT)fΜɴiDPDDСC <.] /UѣGKbDkEDZG}İaسg}aԨQԨQ#ХH1'"+Vo[nԫW^{=7$$D">DDvSL,Y’%K u믿'"SAD!5k]  B y""""AH!ODDD$)䉈!< '"""4)k.l2A!OD䔸]B .(䉈3.3)bjR B y""""AH!ODDD$)䉈!< '"""DDDDBHR B y""""AH!ODDD$)䉈!< '"""DDDDBHR B y""""AH!ODDD$)䉈!< '"""DDDDBHR B y""""AH!ODDD$)䉈!< '"""P\>CIENDB`ipyparallel-7.1.0/docs/source/reference/figs/notebook_components.svg000066400000000000000000000603301412533144600257660ustar00rootroot00000000000000 image/svg+xml Browser Notebookserver Kernel Notebookfile User ØMQ HTTP &Websockets ipyparallel-7.1.0/docs/source/reference/figs/notiffade.png000066400000000000000000001113171412533144600236270ustar00rootroot00000000000000PNG  IHDRN0*tEXtSoftwareAdobe ImageReadyqe<qIDATx |+gy&>۲d˖||;'>9%-BRڒh-iKK)lnв{n[RhR`n R--t $$\_lY%i^,ٺ7d3h4=^|a`0 ɧ`0L` `01 `0 &0`0 `c0 `0L` `01 `0 &0`0 `c0 `0L` `01  `0 &0`0 `c0 `0L` `01  `}e~;zE[ <,3 Fأ<φ~}1V aY/^iЇ_#?{>0g`0Z&'{ {=Cԧe=Ko^W|L` %oiTNe |(ރe LOQ8?[ i{"5iDc0ïEnj̮_qE3Vufpd_BD7qH^s"ER.(2;p/$ѶNO~8p7č6xئ`(b/.|I?20[߲De0 R ‘Ϭ}A\sN-|ۏ~_~ b _ZϽsQyMYD m|O(*z'  e {!ܶ $O5 xֲxh{{[*y`}q]q; 0F}4 Hc*G}c0##{HD~&"j_F C_׿=xyϸ)V` EPpzh8vt\` oΰ&OT;A@Q#$D~O>6SOuCePRda6D$"?tx :koG &Ezwۍ11 aFJxǂﻏd}}y/KKi AhcccR+2#% m)z?ԟH떗ʰBziq ɨC7\o;:S3Up8G2Ǖ˝IVaFC3Xh?R/+0p8Ch5Tg0i@UZCG(O=uj$hjĹ͗aY۝ x9] ^wwwaRbZ? %.`tyyxinKh9vZo_{9[|J{Wj73.nlm%oړOɬ7 6~EӘjK YL˭cۯ:,#/\D3ON;wϤp eLM]5+64JF$"ZlWi Fݯ>_$O r[k\a["8:.L҃e09bÀg$]y m~~UW6REmL$´^Z@Sܔ >$N#vO^oW(,"6,`<2+ IA\ rvghWJRRg\P}2Go Z_o?-W%>Y|NO_}8:Y9A-!"?D* \!f@dƘr1h`c0l(Y>+T^_|x̌DPקy=JI\mZ\}!(a9EUՎ \NQ }u J )?=a7EZZ? űX`3=LvL`Ja l1c''&&r4cH=GAQKY; 4B^*[d r٬^9C˻ՉSqdr3/aGe*PY"t+EjAK™i\^;,as.Ȍϩdc0"'x& 6Vx2 0: FTVW\p `0'"/T#/|1Y`0Zm-pzFc$2|i11V"{F LE]ZN崝]-{~ voY53{{M,'2(׽džU=~4jXAx`cȋHߠe-ޕܰXZv{W$8p Ժ(aIR>c1u-= Ԛ7fR2~hƾկ<9hA.[> 015TvO[/:ӥ]}|B.^*X /BulG cM$lnnn!H?걁 0utK#HPw>Vx/.ߐc1 g\Ta0㓤L?`:r T3@zQ 9qSXwĉכz5gmݗr M<-/rSmu`t{ ro:qy#OaP\9iN%-sQEJ+c֙mIz88'l2dk .O\ noXzc₄-0э$y K}\# @P:*8KͫFbz]J /F ˍ!̙ llO\W?ff*0[D4&# o='Lu;$vXA_M[⋢p)dPfy4ӫdJc( jTwwwDP-ޭFإyuuuɢFO_~(S慛 s+Z~0y&}P_^Ts=Pd 1j ڎ@ $` \-)\>W(g[6/@jXfoQ쪑NSI@Jd Tf7{@PH$E)هA13tya9PU+G,-/$\.\TDJ1Ԯ7n5CGqyo!nq*ÃQ dZ^^8 @rx?-]q 1 TPPEƂDP >7E_/.,/(O[I>#Prb6\D3Gr|c߾`%Vy`S"fL3*lK5` r9Q@v 9mE'n Gޞ aDÂȒ0b ]MeJ?pW eq*?H;^cVXQnקXP~ J-&JkHHt4٠9?77AJD0b @ٯJDfbcxD: tI M3_F1?Ṭb01h,Zhe8|& 啒x̬-ɀtf3FXᘩ)YEGE!vy L(9QI &DRx?}5rq[@_{d3Et5Fb9 b=u&#)3 ;n|D>`%*TT5 BX^^S)Wہ cW)`Rģ`rê88r݀@Fd{kG,{q][̴dp0:?7\__D/ 1,rn&0&/F@<@jv#/^VQaHd&帱>hc*lcb0Q)¬W8f F ǭrH1C9`t(GFd;Ur[d`t8pŅ +rKI1L`L^ FTҒ%5 X_g3b `(ę';y*M{7|CG@$*#Y*lq.T+k(̋N /տi5ܐ#XQM0< ~V0% p_V`GU, fRAK:%.aN5.f3vR+M:7d 6F'YqnM` J0R1/gsH^u^ Fm(5%걆e0Z9b2^P^ F`Hp^Z\S8V0/gC@{6^wB@*x~/vLȷRZ"6$0 `py`aaX^ F@UG&:Eٱ 0tJ..b^ F}@@VQ%IKqb A׫Sr\p``4eU W7_ uw187szmb^OFw-}O>6(q|Xv߽h1}ܙow}>8-W)A@:w__GuTa^oG?V"24$.0α^`)2QPi0vD|Q?W~w_ bmeEKR0h^OÏh7h7=yڽ?r>ʸrotϿ1BN `0H h Sto-G󝆒=s?hjDBpvJ.HBg4i?(d,ݴdD1b O<@-o~N~F~}P7?N>5=Mb`mJI+U~f0G01LzL*E vaf&)TBUx` *QQ(w}WS?%-9O>כ҉k6!yV<d2L`⋟(U a@( 1dA^MN&w~{zKoH0~_[c.,]Zs _rwb?PJPZo<#_L4\}}}Y,?T?C?7&DHekt6=ÍaěF K_ x䑇-yaLT- H)뮋?;=G`/ oka{/so}/NGG^vKY@3?φ>!B:̌Õ/`C_{b9mop<##xI?=E[x{i(9ݺſzcJqA&)7tmOh|ƠH,/7ǯ=o9L>LC*~G St JBdA8 *HdFa|r=~$-7H۟8u "<}:";~oF$ՈAT0-oY)CE?xmiģ)>0v A/2bv3)n1rmǷyf "' ?Dk/1`*9f~[}O{gO~x\aWZ"anà~eHW@a/GbgE/EA0;R8g:o&] >+??#CH"O5O'sfɓO"jQ\Yu_|w-;-=&bW|Nť1]/u E*<+_q$ "EAD.I ?O oD`0'C` \Sk dRDOZ^:Ua oW_rr*s*c0J>cr' 86) v%Im/B\-rY}ɖݭ U끙@>P>#2}Nr.&M!LgU31Q?D!!\m9n!Vf2JLV+P$t*m]X]?sGYb53H aKKe|OgZOш ,qp}c8yYNR}1gLt:,aˉmI:lVzh9-{z4ƆL$] J[Y^LW5.m;ć}bYJMI؟8_r;(6lԥҊoǵ=kPP#B}}{_g,-o3k1az"(|/^n`mUϟ\Dt8Pg"yMc4mCa4%K`nU6wt+/ۍڎ c`3,*+@8,k@ F2#a?Xk9 }1/  pxMu"kx3ܶn{шk5O2ŢlFK?1t 4+y`UAJ ȇg\R\ KA3WDD*5f}Uʁcd3 Q[zG^'"/^'ihK}!j 6ϾAwqǾgFM!AL4U=ƾPH:[[L`e"eb03syO911"((o}viU2:ת{(HsP'Vaԧ;Gxnw7R^dz~nnӧ=^1\ NhpaFeQaUy`PSxlbbr+ylGU6 +MQV-ƑMX"20J!v%/;BH2Dׂ>2꥕ߧNE#Z+~f/ -QQJP*dտ'g8pWۣmܞ I}oo5&*]w`р~{{{`1J4:?0J" \(5Vq|*mcb|NX Zj1hQ7jgcF!Wt}mm_.PBO>)5X xpdr{d*$CX>}n%HD,{{߻AfYKg!Xp煇V1LV;6mRbzba=Vg`jR~04$Z(e|vz*2# p8V׈F䫤MF;D- 6`@my€ nUZ L5W s")RW>z"a3 gZD`UL/)F'y`t-g Ԕ0n?sk/qΌ0U9ʏHw[72 .6ffRB}QҲZk?V/qZWWK$v$+ a1>vZj -5rFF.oA>| Om%)mv&o*nj4jVV܏)Z)GbX 'KkCr8 6aKh]Y}]"~oMUČ1kR2Hc\3Q$S, m'#ڕWB#u B|랳_NV)HdEe$0ZmДtE$X+6szҼ4b9ݓx#]oM!>DL,s_w9)F 6{3՗ަF>]|H (ۺT.ʁ]ݾ0 Ịo%e0F(mmS齌֣/Ŝ-` Zt@hPfFLXɖ`5*@6yALXZs@Dr$=$9g2{hY$gG&LSOQ} mzww7(n:HCʋGcHZ%nXEu!^)=ATPVdRkF^@l4~'jvFK]|h}?f4wiѮf0\5lu?̈1U K&([7HV@XB}:Q4otWs'2OAX3p`iv}ģ y^?W$/̋՘(X&nc[ 䳰ZTP_Ԇh"rT{NZ y]nve'ɛw|ZƃңWH#zAaTWzLFЌ<<M^&F`L ~Q.InV3Kck^/- 0-3uՄG5 | . wV<~~`A^8(; 5[!0&FCҮ{B'J)S˨,A8Ln-)f6TBj㹴@BƜYT-rk{`I{E%̷͑䀏2 /֘P<ؠma?m ,H_GI?]UBBJ+GФ~wAdNj@FgCҰkŖ(0rao"TXiPǾiZ1@f5GveFmPq9$q+amSRg+`%%rwBsRLr+Ff̧|76n7g4F1XmjAm? x\7U(fw]:sO\a:Dt 2qZC^P2UYDN͡2!kUª6v td.gi63y40,?(ԒBe kDn&@ %A\MD&e{0d{ DAg-%Iϵ'U^7 ‡HT}޾$;,2W*S 5E0#W+yϨc7zXb'+o`gU\T'FCH Ei(9Ihb r%&՚%Yk<,X0&r2SÅ11:p>RJP@-+rrqQ&S v)lp5Bj_ܱ-P\T"NEfANTK4Ue!k+גVŷVRa&+ր[-!3|>sOWhȬ^qm.;b~rYͻ|vi+L%:#s=H"UKΚ!;pIL`X~AJ%P^q-ir/at+*t| .~: IP AZkFdc*% 8@̧JA###S~V|kKzUn7nv褮ߩq[7<(/+UhTTV*q7޼Dfql5g/SB WxXzз`jDH=ϑUf&՝h9";9jD٬ eJq3`o $W:)3!Մk$3\Y1D_* /*ߤy "DN☌:G?YxQݢ:s5&SdӲp*XvwSJ=LyQEa(tAVUC^T?H`0eC{foJ#22QvF<)όJZR$4lhU?0D.c3ԩ5\nGyc粲aNy!I4A/ت<&U)|.,@UJ}T 3rL4-'43УMPF<3U 2T|\\i%LA0KAgpy;l2#a* d/^hI!!l>,x@ S'^c͘L`E2>؇t/AlI!iz϶QXݍdFf)6$b`,xD 17h+Z)\GfD:ҒĦe>)Lک@998kz)(@0XiFl$Mߑ*{P("6β s- LN J~}VGjI@ۯh//G~=i=ґTX#sN\E-|0d38q2۔#)DL$I Ĥ2)tE2=0)%Jg "t+0=cu_W, g\-_́80Q;ݢVSB&n;2g{iEi:DNزepqR)/EB!Yy`4K3tAGfZ!"d2$o3uIyP ] N<ش+ ^`V퀥HGf(HHNȶn>DC=*[cH]e5FbkDQ?,Qga„rwwd=H$$lq >kWA(/?1{{E2)|PmLV QPd{drud7F|I;1Eo vVfa ?hTۊ J7٬!I(mr?`b*wsn4LR'6ٰUI5ONLL5%M _VI lw"4p[_[]YmzM&jE2Le [9ȥ@1ȊT6N P`Z'&3+oh2IrJ*/ @B%D 0ŀCk h`Y.*:2wG -K`~Ud들bƃ߫qQ7+KX(ꊶ~`yK64VJhģuJGf;2.f)s; 5l5D,񭭂{53p(ƴ_$h?0T+G(T)ו%otu2#] <+nkuwwXlgUzQiao:#3+1!b#8qb&7X_HZ6:D6 |kv}.VK&Q=(߿c<} wh3*0jkCǏ--.NP5i<]PX`:yAuuJ"/̖Nu!f bN|Up R쿓|eS1[sdvZA`= ysuee-c OfWY pr>uGSFN?0?qb\E(1U6 0/m0UO* *xt *R60SlaEӋ 0IfFT0RSɏZ{꫸qUMm0**#)th]p\h;B z_'VARtbkQeUTPX]Uv$ ha~ H.|pt}m-( `+a}Фb=lO`DLsǖ.-ҲPx fG2<^P.nlg jBz 2ьd "egasPBZʻtvf_s\87Z!#͊?T| {lc$'r?{A* ~Z4l?!e=^KF&#'+5~ i1+?ztJ̙Jm%Q TR8f3UļN͹#.>8HܛB~E3a`~-J%2cpQ &FcW\Ο>OYn[A}RYt tr7H .r>YJQD3#B2SKsE;49`3Bj;**Pe?:< '#ўϡՌ Xo#0MJa-(CGZ|VU6YO41\Cm*(}ŵV 4t5黷$ܥ$y@'g -ch8nC#}p.h`Snjjm u),Q'H3!6ʲl]]3B bς{bv b;=WڙJf/yRs zJt/' 7x v_ֻ߮Yqd/nNX4B>r\^-{!*/TޮXL^ Z\(/1HH+.0_{P(k5L٬,iY0DK77N5:_ r)Ш' dEEP_XrYB* >8^\Rn(|Xue!fE$Pb!±+{ѦMkm]}5Va*偁t55,.6do L%=qHؙCsnI5f-D4cZH'-3Tmd2P^\} )H G<2 >1\o}d'?>q`e B{a~\{L^ rD"3x/LJfW0)yAa,'bH_~$Z!}eqPc ʤe;=L퀜*HĢ?T{cb,n(bF>t\{~~*NZFd^ᡑa5止`q܉Kd㸒M|ہ;0˩FLDX|c}Wt!޷tN>CA=<Zn"N\rrh3Qo?Jd"8LZsDx7xȫ` -?HCw,w{FZ(, 汒2H/籟-̰{]Pt}U"0P;6f29-a[ ? ڌ~`F2Dߗ.zr9ČLJfOzfɌ$"D " kn zT"JQɨCC{@j4O*&y.{5Miy\h?0;/eҊRبOJ0o1I ktKF@05Bolk"ݛ+':sۺ|%Ru\Zz'bK ug0)J:nVW`/|_eW tM0礒M!OBhF# 'Afr2 lB G&% k_(r (`;\ɈYnj}@^PY`G 馝XhW?0%FS#3> mLm&0qR*8# < fhb8I^7VOjQk?0KN2?LaV?0CYuL{ޤx(Jis[akaƬηQK?0Felmn* M<_Z3(MDjq(TJJ%irȤe5D=wd?̭WFG%bO?R)-HUe m _ZѬGHMY=0Ldn}mfg$Sj30g"&UF !l6 ;<."e µ(*e0,( ig{}|lY rb;q[&2T%\- JjTgXbɲcHB ؎-aroav*T$/"W_+qUɋ lC Jy`-%|Vvkz$ Tll SS$>#Ⱥdzv - 2]r15_b"zj÷߭`2y`oODr>.7X=loڀnWj 5ؐz_%{(N#H # ٠כ `Ф (XU B+%C Iq}/13fW3@M.-D4D"nC'y 0-Wa}% <\1r&0 n&D y`Xޗ9#⁐wjQEI[J3 ~ E͚)=R`2ެdv5yΌNEH.u9S~l ٪DBJU`IЗb/7/ 1TYLŤDn|)H`:ߪdf ;n\B-f*\,b2 7;/GƸ iQ(ʧi˵*@\K/9=͹+|.q.<@elNr؇Z[Ď/6KK#L`ӪdP(Y,LbUe܂F r+U\uY٠0vHrmg4fZˁȺ\gM[: 2X'I GE3b}j0-$`0v: 05̀RzS:h-d8D.LY'j*pKCݼˡ9wZ[$+Ṷ2k(kp5O. 5G`pDVaf(ͺ5C[UL>|W(0Id~J^zOYu[/l<0LeMԗAr.44 ===VF` &A.!Hdj-Pc  dީvXR+"Yh8 At7:7z %; ( JGǃ貊h!nEfX=K7z5fwSy<S"`V@pGQa_^cg ֩VP.]^א*8r=P5Y5hʫ}DŘל%\T vxi:D2^\\Z5sZ[[ S\@;g+I`^B̫CWa^XXTx;U$J@p0?ek8w OoyWFwe"Ų~$1Cw3SSW͜??y966t>]6m#(P\"lmn-ℶJX9/Jv.Qh a}'-1 $>dKG )oTeL`Q 9L>9Zx8^_[ᲕO"+1/,//8qbmf 0/?d%2]\X8}OX-Ce{9]eMUm2ցrq_+T xK-07;;a̫E騉7,aDB{\]] 8 Q+@ &G1jpKl$gxddkl||#Roc6 ozc^K&fDb WCoHiԩht$SfA}WARq(>+"X ѱ3@RXް_:߿&s AēX bURg3f֊U$ĉC?f]H$ )R< Vcv"@~PK@6a@L2 DvWxb5:6&+Ae;f3 8yrXBWHwj2zAw g$a6١?RQzBy3'm0Hc+AA 0(2BBσZX⳪8FiE#ܿ H6^o 8^ӋJ)^ ub7v?a0A0>"Mخ~scN.FBQ__Ȯ0})1$+WhT\wT T-SB3L>J` F> 0IfT7S5(*}ʰ[>_hؑind!^>L Q~cto\nܧ=KAa\VvCqL*UGdl{{s:D*~˖~F1-kE ~8EV' ݝP}ǥilĚ0Yeky p? LfkS5Dh4@M$U-J YtV=0vS-/$&RTmO HP<0J<>h>#EDX$gY^.Ҝ`>8Hzp0_6 =/R}V~>Suj"BIq6"TH *L<0J~`̇&0D׀(^U8'2W A $U0f_a?ɩD{^ms ĩ Ǧ~[']b}?rofxxngT:>=OϿF׶k>bݲ篧w3 jQ_~rG׻7nǿ޿]+aB4"JcvxOxk[Sؔ{K$ p8*H4BwԩiOBmϝ֓S$}ĉFݳgo#f}񻞁6wUߋSx^:LFuI{W߰(yP¾FiP x @bn;E"ofhR8sgoM<Ƞz' EF ^1Y31]7v갪=a(8> AraFa;8{S-Lss6m1d2jafK@>Ez p9G& ت={kry`#L2u8ȟ3(Ȑ'jJu8ujjiqq ^pI ⠀! &|is΅ hҌIߑoЪ# ,YY 73":8=Do"JfQPFe ~$J92oҸ 2ud  `bV|]R ]%#`ɀJ i 5%$1oR Qf`/*00S@2t a3\/ef+ML%%JkOy^6i?0;*0ĤQ<VꃃNrIBip0˷OSk-.MT{ -7moo(8ɡ EU 0lc(̂0:~\a7S"aWM̑ێ%sGC9VL4S"_f j%.CQJ',PƦDq|hCYUdn^M @e3>͇6$0faR@ kD mJ`FIiڨzDQl>1Q?Eګ1D.d` F-^7^t„ݛ_2Vu|hsz8s.8_z Fg͇6'0㏻eFH-H('0щgۖưlmn͇6&^݌c3]]YzC YY֫[|Z (flEqZ7B`t 30/,,e`D"sWwwՐk*Aa/}v#E 33g>@Cb2ihf(&(Q{f{՟;LQU RVQ F807;;lj`"b~v}V| fE(U9n|/ ħ/ ZT'.2Id38|Hy +0U9 0)?lI$+V`;U94%|&h0?A FAfvZ_[l>0lʼX*u'Fdmm-̗&Єe'S%mug-qW(\ҷ|8;XA]5g_w'M͈ѱ9Jr9 0Ύ,vuub1iY rF2fD;p$9?;=}ϗMNNl> jMgd:%6lp:n&S˳~u{v mm{Sa>Ė0&U͈t %v+u4aA` U<׮.rzzK i>G>5 6=S1.WU.;XIEɌh777Ôd*u|YS:vAV.6 &c9! 62/gYPj9@`s,;`v{}ҔȰ(ӢF`[]gT̈a#ڠT^ ζh6DnBV,(İ)Af`\N*K9ޚ Nb[z4l3]"0|X-DBY4tIZ5ۈ53*rD1^ $ Iͨh+(UTi}` k 6Ke&{OOvm$K*%"U n:E9tT[[o(RZjee%mBW1.a>Q~'*{)RUj-'0 Ρ( VWW] $Ut؞P9aՌgΝuiq1b$zֽgPŖb0 ,ΡJ3 ޵tXQstG/;~|3mBgomc[ _i%[K`Z:j?tvv64Q@;$gbbomuho8lO&!~ObPXLphO$'0!񛁓'GS=! % N~jѧy!T"^Eۧןρ%4;vlE>ڕ6J{MM+X XCҧx'CM9&|!ȁmTSS򩡡'6yq{;dbHm,9Syy.Q 1BHA:C`%ԥK}>k,r}n{lk1~/ h#1T4o]b7ׂ,aH6,/GBF49~Ϛo.ڌuFGGzzz.擘X,]^Z"5ܔHl3YX3my$[cU8 C L@LhHE~'0|$,p0 c*s2Kt>fVۙ مZ3q:uT6x7^ۚoPN> *) V*҅o!x,ҌiO^8D‚4?Yk:7&iIucp#@>I[-@QJJ `"$SAӢф~D䆙 mloq\L>Ѭ(fBgGމ-3>HJ;:;G/\D;_L&h51PE63RȷrOEg8}o_Mn?qb9~v~b`h wEծ@aW显|> 7^Uڟ?QMhř.i`հfE0R++d5TW sA\3cBg.4 =ExܯGKW4);so $+33QlAF '$I~*gF.dv9;%9i Zhk3gBPfE! 0R+;dC~HpC!A*8`z?u,ٵ 6<|/R(m"9Jҭ0TH]T l//sAt6xlB&$XrDZ0ZSg*TvAzbb]\\5; F~߉ko!Qw~<)T>H27t<+]ygX]Y 64K l_z&z?" ATаNl B F"e2d`C^$\Nxc`ojy" $߹_=lhdddK]Wʗ~ĉ/xAѦT36u?:jo{i{-:{o[g?[tO~=?ھz2|;~? 0 z7$"ɭR Ϙ !8K1iy 3$j8K!57vR#ڒ7"!x " Bb~В_<HrYJbBB苽7W}_Թ߾뮒|kgshM>7%g~knz엿 Eӫi =R?vPރ!LoUoeccɴkɹ\Nh䱿ybFG{gFRb(F{‚#N;_{pG?e k7(KshM?YJ,K[U-!)]k+WDX\jI>%ml JT2^qPFhHǎKd&M O@`~LMM^^WRo~ ^î8#_FWOOO37ǡuHI[J`;,!\&x_pODEb ɁD`J%x&|s]uɴ;}N}`sG{EȲ68GP~_U%GW}ZVzکKe6"Hӡ0]*+HWjtA_ə'̥.<-ogB|jz]r=Bُu|}^ f<_S.5o^Y,3UY6tUs3pD^QDfB+(ɋR&t]Z-iO>$G>> /?ؽ|_2EG;:GzY{;| ߘ1_%*SovOdqJΞ9s[>_p7?^)fD)%nX7UvӫJXǣ`)C-oX\XhȗK9?oi* N]S&vrEϒW*T` VS[Dͱ-ͩ_>Es*w]$&k4>0R6@XH>ʻխY1[$0EGT=Ҽ 1lP(* vI7z`_]w'c ljAvUؒmp|G9W ԖZD -k7=6~k ޵isRSq}R2^7|TR-~%4NcɌ2޳ۤM^k*V5ɍOpҞAA!"Ov'7 9) i&sWz3g|Q(c0׹ZWiXvM)3ЦRAgH]cܯwlEvXw9 D&&4;iӹ% ^Ys}R3k[!`b9xe:*) YTW:j[X<BJpU>d 'Q"ه]7w8MM!;`9rӹ4yY]E_i'mw8n6$A9e݄A'46!Wkr o9ߌ^-t>umg|6 ¡&xGt˶֝kOB4@"IZ^JMUϟ6ayݵ7$Fg+yxmݣ9r%)h69Cqu[W{{0i ;[~hkowE=37Zk- D#N|yщϺ:zt7̑ưXO:s&7~uilW׻^R*cO|VbMjlS(U<Lܠ( dr݉.dv@|r䅖 M U2H+U'jVFjBH5W цju#S"&N VŴODa} "|ϭnPڭa:.ҺtVF>cIwϙJw!_tvNq.^BX!?77׵bW>K4妼堎h].- T#02SlԚnqb D0ҴVf Bs ,q֖ѶntX&üX~Ӫ5`6]Ȫa9jk"Z,[uI SmVS(ݳ 5P焦לd׆qNG. d#<3殮VЦFGw #D)qŠјV ɿ>oH4*)!lua3>LZj2$hCƱ0(fLZ!'6hg9# CKqCr5:ҙۉV njdMN pW7cvЇ0 ^?hRN10uA`ZZf:<҉Mm@۲utvN,MVh_TBҐ|Ph_l ‰K:gTjFX\[[ q~z! &x]A,jF1B\S0Ϟ9s[Vv 42Kg̬Fݞ]uWpÍ7QY#>! r~yn Pi:0¸|ޟnpX`sy~5;;M ɄO`8@Bצv 0crMMm0saF1&qz11鬯7(mb1C2yf6r^9cמc}E`ʲ+dv<V躹?z/چ)0\C#f+E# ?v+t C"0H{BӑOT}G>qa(EjkkaPlvDػhToM&ڬ6HD^h!#mixٕ(D=e윚|s"QDs4xn 9sNCy ac֟3 ;AP}'nT9G"G{b>D@@+Ex0n#{ZEP+j`$Y$(e<|{!ZHkkܫ=-fr,JYsіծȾ4p4hD Օ0<^!D$gBMck[[\H5ڦ,$֟E:5n\r;1!JGhR |hifDOr?qLLrC̓j>T @2HPtl>:&L.fb0}mGDORK&mwRx' m3qĹoԞ5P(Ĉ)qAz#uAH2_ I{wa!ecOW躹6CnTDmZLv?~-q?m}gэ L]'R4}cNxww$ mR٘{ۇG($[Hl-6>Ndʚ%uSs7HB4d !%}]Y DP()y 13fVw~+F/\-P(J`!4\%^KPDrWxŵ iii)BH:[?V-PzR $F{XU"gd&O&$nwTͦUB'~=)/WV5f(_R&7j݁(ZD0=vP i f#طg@e}6?D=ɜHlv7C` H#ٕˉ#[_& h i3jb8PBPU$Wcwم!;R c+wȎLL.jbEfrf&W3HWUt1H*7dفrb VV( PտCYR*(!ҒU%Kݸ$O͎rbXoHקXE|H^Hc<6J,/PS RjyM EKXҕ.L@MN-K6Ȳ |H$Bw5)|YdUl4#qB +Bc1_ŎP۲ȣcO)cÄ́ә)1#`rD$Mc]/u[Ȏ)+Kcs!6v4Bڐ,dgUgϜ-\ B LQ_$=D Qb$z*~KP()Z[={l.%+ֶk.!',\BPS( (wr{"G 6PT[yvxx Hf v{.F}5ץPtAKBPT$( B LP( %0BP( BP( BP(J` BPS( B LP( %0BP( BP( BP(J` BPS( B LP( %0BP() BP( BP(J` BdiI1IENDB`ipyparallel-7.1.0/docs/source/reference/figs/other_kernels.png000066400000000000000000001203341412533144600245330ustar00rootroot00000000000000PNG  IHDRcYsBIT|d pHYs : :dJtEXtSoftwarewww.inkscape.org< IDATxw|eg! )@:!HB)]lZv-Zu]b(Hzo -zcKM$7 +/q晙3|9y\mA "a````C$ bS 00000p!N1D)H8 "a````C$ bS 00000p!N1D)H8 "a````C$ bS 00000p!N1D)H8 "a````C$ bv#"@' vSN~7+v׹ "@G.&L4S @9P'l$yRjkBRD$ -o]9@R'R%nR||[ԮZD%لik(O>⃏oF?*r. )40a[Pezh)/eJ<нk ^|9wJ˼RD \,f4c&H"ΟO>G8ArE9nR(1a:e:^)UPub @R\-] Ed*u 7"=h҂1XJDZnE9n$ӆ]|V.|` 8x[)EDl`2:A%H/Aƚ1B ]"%dZֲEhO RjK j"Q-v_SR]jX1" "L޳bO ]b\mV0Y md*W0D&/CJ]kUD>TJ*W " Bm#DڬjF?(G5A>S'R\mW}ZBD&RJpU5 o7ŮX;u#^6F#,f k݊UC$j>RjMD 7W+.6ɠ{_3i#a '& 餗P?+pM C$jG=>x/)vA<> ܡrQ5 Bݍn aZ#OP,f1KY .T"qH(=MSD:K 7(v$+@D"Mf 8ѦD]mR]LgՊu(#Շ!W/ȍ.6%TJ}b .}߄&>r\mK8 2mnSJj\IC""(=RU R||'"o5 D)A~%}w @ Lf9/`ؽzUb$.yxxxXyy}~Kn#"O?}`C[kUbl u_}^[["QMD3ڞDdE=ED^O-js%w|j&HT4pRWSt;'cR=CD^^Iv9;)Tj C$H J\mOC@D3b!yC?a$js;4٫blDW}S]lNBDE_G1 oyn-J.[B}Pw\ BaD%#\mOCDDl6#k(y[oFiO{W nB}PX",zI@\>JY-1p / rXq r&ȭ]mOmc$ ")}G Dd ģJ7]mՄ|;tilc=,ڞ gr]^LNWӘs>]gD$E6"d! Y2Bohb! "M蹈)]lR/=F)ߵ5nD$DCNx$&M\mRB{{Ά-E)u64ƜDDČ8|"p FA- "nLoofC jAMMik4be0Džs]Js1̣Ws^Dw4.o4Zq6n1kh. !ňdZW)N 8=o.6!"*=ccjNsWiJSnfB "u=51'^X qM=L ?D!-`RgW.Moڜլf.s0D)WHDu.U c].q@7s"X)]{D^艧 3fK.y䙊(2_ަe rȴa;bö =k+ʉnvJLW mkawqZ|ɗ?a:j{C$D^ (vu@7>@W+V7܊ ''^x w+<+r ),ad(3E-JcW]w-_ G&L}(j)DFDP#c(G9£%|KcGKRye܈;F)5Әa .tq9W5[0T)Z(o@D]"Ō":xYAI9pB JSל Gr04P)GJ q]v:Sh Ӷ""&1p3k^bSJu=,jyB_Ad$lHGiALO>ZfƼw/R5u Zn^ -X|M l|)DWs\"!"݀e5CHD"I&LZGUؙΖŌ '86Ml**Afڰ},јB{R+6*@DG2R:!T;QJ\mpՉxtLΊܨk6NtrK!jzIEd'Y_xf JcWrn v:Y#7RDۄiGQh,Lcm';3ljr]W\"<$XrÄiBoC[':c*ȟ,:\͆/w[DtWX<08xЦlڞrU(^sjhٰO:3>`6ֱ,,,Js.L:*6רd YzTz36jj{&1Z+.TXwq  w!Vl\Xd[zЖZ>J9JAciFK_|?ZӼWcĆ 2ZuWHH" G)I5e SH{U)'8\x#& |uVTH`FʎrYs#7u{×| 0!RDb }Q(7azۆm@ )j 5#fI%-\OnW)Ipu0iy3udcMDS>+XU\"!"sѓnͯ1n( ZF6r )dcXʂ9i lRJq'wE1)K)5TF/"b6GR}A *#j 2 s>͊upU2 ֕"b1aKlԭj< 3k΍ OTְ8&LOc!5LS2najhDw{R5am `XӨG" ~RJ&LspKxK8ucU+eVPw(}q!g#ӎ֍ u0+`XƲ<h WSkxFKdC &nS_K>_!'cS[WoOCG?WapnG]mKe4Z3R*v45DdKcOQG?DC{A"PPJWxEUihD'^6 qÍt5khF+#u"D&A$= f<:RHb"5 [L/\tDItSKmPWtw5D P+4fxQ)N-ȗ=i5Fn4WM4lví6Qx@H""&LO'0oI!E3azQ/i"!"5'[kh^Dod`:4 bmӓyNwlؚ~JctOR.!"&L 7X n0 򜈔+Ԣ:|xMKGC{:"cAg#@F0=j[^9(„&4 [͆~Ҏv%H&mulZ#"ذIz02zSbm/"]m#H"RT]6lCn&'uoAMobK#RTadCG ,lMkWbPKDIaEZM4*pKgHV_j hFMZs5tc X߸E/ Iv-ҨDx8 إ/<`K2jӄ& gY'DϹ60&j; jx zшDq WegFn}-|JFHLvz6>}h=5;gtش }=ˌ 3ؿ`?pc[)S86k.F鯥C&?HRji@D 2" πFű5m2RH1eoTJ9ʬcHi2__}BwihFb-aa3ikULu>E[ڳ*"Tڞg۪tWK_Z+.`׏+/ @'&;z7|p^gv:;p_ $("18>0oV MѮF!!\k9sSChPvmGhws;Z`˗[tܛs-KED}:lc0=`BڅNvZ6wf׬]v#|͢O=(P7W;Oe':ۉyg 8,ͼ9Cƾ vsٵO-HSf_On8pZ@J HL~VJ.QCl>h:-\O4QlIĉ'fs ho/Fmv 6wv XV/ۚ&yb2ʪ6"{E7:ϦvK1;}a\ )t(D+`<AV*J8r@K&^oNumǮdϪHIiW?v @Ztjws.XME"kc>C`\ㇿ37 ^nf?rJE3Г%/-AYw w(&w Y%N&^Oxwǥ9{~̑GJu܉K92c&$F6ލ!5Cq,y/e7dX:c\r3r f|I@L [ity m9|FAVKs}XԂ 5фMnMfڍkǁ9֯0.v7cݔuvm#G n[lo=t/[\(ҁ7q疙?t[{;yCs~TVCZ\wY&M`Cs(ʭ%M fP ֵ:M GL†6}v6aͷ6kc.1?O҄ E"vh,7O3wMmH#73MCxp<f7KG:ֵnJU5C KdⰟ]F?w< ,v<[]&۾F۾F<1E.rx̪"=ڍWEU򥕹/Fl qps'[=`='ybH=Eu<3=֍snR9z ;UPF$RDd ˺xa^18`G]uoeJ<=h{C+!yb2(X{swEh9%Cʔ) mNۖ7ԗ!)V΅yl1>vERI(.`ݻdO"zD؍J0{1efO3o"tb/$Zz}ƾyܟI@&:u,#Fg.V6[OGT>I ;mwU&`0c0‡a4bW!޴gv!우{+o(ͯf*yvvtJ^ xDJ!7܊Kq|x.~}XC*Za]]hh#alfO3۾#9hMܙ| |_|C}+Pח̯2#oqd=Oar/p혶,{ٙD6l>b ]$FiVDZYVJ)~ ~u0bWLK[דU%0y^(\pB~$nwyû,|fa,9WVuo[ۣ5Wg&I.r3r?_6%=8.F7Ơ IDATu!*=%jע&4)lF3 ~ͬq ZK\MRK'&!DX,deoKpqckC*o// 9'rjeBnO =s#6#ڔk4V1mHKakv?B)>'1 sQ0UmL#m>|iˑGXr}@&9/΀^^(,N=x+~t -r}dOºAVs"C+d{xa@Tu$MHb+YoU]E)=[ǡ%^+>dn@%dtR]"{A&[Zms'&ck&=)}‡eg|䝿YniOm/!xΌqe^ NiF3fwm `v_‰Gֱj$ޚfXz~զ2_+X :сOt!vQDU[ksj)P' Ѥ(%]0>}8$~pB0-:`g׹H-ɥ"n}ڇCK9 b/"nJOl%;PT}FDbmhSa;eU,|f!kkoz[C̟_ !$1ᚉQ܇2a=RVxkۓ-e9 8gچ4>C롭 *۩ΖZrvA옾ick]eگ:Tv KYtUظi">}grb [b<)y0 I󝗐m*_LJEZ`e#s?g<&w*_ ^mo~"<6I xyzx܇Qne\|\EYdzu,B;:-, >ǑGh5Ů;w/~4ў0sL3Ėd߃)vӽHԓyѼCs|XrcE's2¸ml}WzȯZ işLƾ NVd?ڟ:]Z~lz+9埍s;9N2g:!S(b?k^F5a잽< Mm6rw ;4'}AAc+#;=:GǺѲKe̝lx#;X6(>}왳֯7 /;SED(sϱvaTPnP`r;u2DGq]v EHu) 1qۿ^nƐ0.1N&p9~[kl|+iޡyZ`쁳 Ͻг+qziҧ?誣去;FP|P51WUiC69YDg9 R$ᎳJyҼ<9񣍕v,۾fZ:ֽ|x '>S KXYrvbj̮3)F6~\yJq.#lUAB9@HsJN:+@O4ml/6z%}+~;R];%<1R}tq qˬ[LovYF]7*7e'헌>6^H "X8%ڵ-u #iBRi\%-

`% Mٰ bjuEC lWJ9[.mSzf6_F 3}#@YQ`zi(hFB_*^_5:ߙ;3p}čc׬],yC(XsN۽sGx|)5/gv]^vܻ^ZlޅT2)~)KeS ~e0>/ ot{I֯:i([{rI67_7&Lڕ9JqǝH"mlOCI ʇ\L.ڠ^G'k"JaB_A4z !8!@»옾áQbC^սKbkӷ񞎴-?\i7{)/˗þdĻ#H- &b+qzir3s %e73 ze|9's8w- B!?WXU49ÁECt?M /O=XM'lJR8kP$M478aqW'48v@]Mذ'0Rw&w.UhX iFq[XZtH1coFƞ9{*Mη,.+ВCf_xYrYAOr1}1chѱAAY;9ۧ'YJ,>@ǺB@Lgva|}׻7Yl|K\3o-&7'gGӢc BڇŅS??c,pqGk&RxV6a:EZfeyxSJ9\&"$0&n27BpRa\f)S㪉<+/fhIJD-T$ Ydz$zk D1'3Ȩ\uy#H!P8}z_3@욵EC hh : U$ȺXip qc´@'|Յhh}"4FA]EJ iDJU6l ѠapÅEUVoth+֠"\mA='l<[~^EHp}PHB)uĄ1j`ka#4 ,vCUYb4njي sCJJA*Z,qSDJ*v)lBҽN NM(Ml2e,?MxU;5oƾpS}`/{'߄^*FOQG] Rԩ˩!D* CdY#xzVXrr RT1 ~~]x6u GW`^ED41z[5E]R1{icaQEu i$B53)lV4kqF;:0۱,xj ^?Vz},2^v3a]XGG9&ĦO6L? o=zr{~'@fiL&wш̈́Tz S=/0̾o6YvIsh3 k^Ì 3X^}+7J0hws;n|Ywb7_Hh! LV**.Vu@ԫ" 0 cqKM5tJ:8W3 EkVRjY[8UnKNoIߘ΄|_fN  alŖ/P],~aq9#zD?& n?dwkcڲ6<`/fN*ϺY۾نB])YqMS῔O7̗o|kWCLu}˼og󦩛X܎[:]G9^u/GDAAU֠r|^ӼrE s ڜVClhBy@j(_2mmHX>J(S);"o4M›vo+t*f9фaockCpug3\7I's 8˞3nT9's*L\$O";-\?~RSX *B 0-RJU%}&2- vv7#]Ε}63^ yrU$eL1ڢgcUYCG(4}:G>n6\2DnO3kG#&)SXRЫs[mik}ӽDMt7L m7ԗWZS0{iф .!s_&1cK9]b4!5{V˭b8˸R"rx9 Ҵc-^,^.Tf3MQ9ó'&7' omtJGo f3g&drlbRTHa[׺fCxRj=6l|, pLڊϕԄM^{)=?qzK_7o'}\Q8jUh!+N$r3k&XB=Qd1;g#  Lluxp3p9m]ٞO36fk_ewh54ϮoHb#IhP4Fֱ,-5n3NwAVkY¿eMG!eXakCxOG~MdHF7SzV,iMEYdzΗt{ڛ6&=pof/#Y~E-b#EEE]kmD-4c$q m9/A)uZD^ŪMtǔ`^*k#I| s >e8u|tY~ (;Ļsfea-pg'(}Mctׯt;WnU HB!5K]1bmmhݳwsrIܞQ+ث7V4!NĖϷpxn3 c8]=.aLZ4 }c-׽qv5EX|ؓƏ7Rxnv%S-L=fO3~,ԢfHJǺĝoFGnn`z?ӛ;N+)/V\Hi3{y?I '}";1ď;җ^PhF3p>|aH+mU J&1}9i7˽?*LտC;VI$z<уZ09\8}ߌ}K j=5ׄo|+#m0fO3ERJxIPmV}T+RVDcP|/lr3>:JX"uE6lW3;[j®@CK &n:)SJG+__eMZ`e֝g=N{hJQneZ ,>Iih-=5JHxpvʹ>.⃤ܕ{w[a]%M =ɉ'dm[fB҄$6MāE e猝L7;;IɝL$B: `ZD4 ˜{9SȊ#_wwt }N1 6~/㬛oS>z>ٓ[[:V4@/GBmϝ؇E=LZ۽>abU[ٿUO<< /ּJr+R}vg'"-*mYˈņ-&zGѼCs:Z*gr1co -,~|qk9!CX7z?,zv]!D̮3vk_ 64F쵱;AؾEW4'q9t3Yc[Ktҗ}/j^r;WѕG).IXuL0VMEq!$ԈH(rDL27LcZxƛ5gZ`΃s?wmQ4rW f3,u99vmd# ВS $fP GWe;5+OdSGRNWNi5t%VLuZ̥}4oč GWe~fg;oI[ٵ 1rbku|rӷ'8B d͠yxxh&eNZy+ t!ZjVYYos`;ۙtBMSG+P5PJ)Ipƻ:I^#-yqþ#:MtvPUob{7VWj?_OY=^(,7Q@@In("qEMPJ-.80>&4KTr3 RZLBl.n=eΪ}g%ޖHܨ8~w_wRJ!uZ݇PIx{[94R,xz9'r8wK_Z_C^R© ,u9^A^2}"[vy]JXL&4FHbmF)mW2<14EgSO|6iP}_" n3(ۙדcg)-*rfuqnuPJzyZnt^,yYΞaw%"⮔rY7;q?LZHDtAeuEkYsWUV]V 6QU z! RfqI2ܙ>~3̽N{[V$f>2uu/\wtStӝE)$sϰŅ&Y'uԟݍ7ϋ+UXxطj?x!t->q V}MM`HMn"p 5ЎC|t&=}^Kz;.0Q }d(._(|r,c>å_GL\ |5+dp XxOW$#ђZP`Dd/Aʅ\(Q5E;$&䓿ҋwҥs(4_Ղ7Dޑ;1oʉ+9SH蝀lvCpeo-c봭ۓ K8 +?\YZ鷝N8=KnN0E6OyUO6o㏋w|Ț9|Iu/^3WyճQ^MaD(\:8T9TQe|+M-2&^㵂<o7UYT5QuF4Bsp5.AuXJV6 ju:A?yePU[}1pJ"CڡZBc$}5|1ܗ'36o&k\r4 vx{}(%јƵtKU 餿> #H(!d'YkYS߀Ac/[^ q9[OG\>|w%Q+9UAU7|T6IOOɜ\MQdL2I$gET[? MisBS]N*d l/wXKD>ʵc7Y/uLIlvpL(ЂH.ܸ&^!XJpQa>>.9|lf:=ӣ:щ*͆3餳U$cQPm/U3 v5 9ҡfRpW MnjGQvV\uCö+T.a)])G^n/޷|S“\̜NCR:}6rz1&E0$,(=ю8֜ߊN:)ݺyd4ԍ{`1ȋwu:+KطW'5: @|x^ҒҊp(7:JICU3]^ƶ3ww<r'%݄&F_񕩢˱r;ġTR]Dp}`bz£XhDm)IQ|#n~I*W}'(GI榊`Un\e`56IL2Oqj W_SE8>ݸ iu<\G)Z~u̅0}V|ׁ]BEϥھ1(g/u_cK@ rmlnKIkl: L~J "4Z@ /9c|@&C=Uo핧 os!:*?¾ӳ9+gwD)̥>c#'ja'C\ue^t;J$4]}JR[J"lc?hN¡ 476 >$:}+NtZjŹK~V>k>[ctK\3㎒pڦ!8J"O0?#hvr31/|]m.|)1v'wCpDŢn$cK.[n[QoI8 |k:y%90PMś_4B9Mm%MMRs* jD1a }x(ixhЄ~fW e-$oxQ[>pV$'|!TbI E7CҬ}3n$4!61nƑ#lnÆT95?h;k8-sɎG> D5bǬ=p2]OE1럳XX0jt>4P7ӄ iLWu3a_fOMžT9MvF8M쌘sD1 퇶/khؼ!7k׌ѵ-bXS[J"vp#e nYt k&#B<zU^g|V"( {&vU灣$3쿟Mw=>+',G\c{21|y՗<3sF糄sO!s6Vw%Wғy!u,MZ5!;-9vle+@ 8h; 0(g'*l37 9N&;-n1"qmX""Hc,aY;&PQ[pWaE8ǀW;%"D2Aјppml( P l8 k%ہ?w[0ai0 f"\D:V@AMQA$B$j<14D&өfU} CQ?(U= ~/"WJ4ܰ p$݉V'}RD |$"}!]3"h b۬CD_ee^V!@' XGJMDaO" l ZD:.U$"PXI8 k.{%\Ykl%""Xy( 6hÖ}5:5\߁'H"r/??)"cT5)F ̜E]wcXssbVuv.} c/"V`aOqSʛe "xcs91\ bKt{U%3l7%,BCH&ESj:ᾓKUp2e_1?]-"/fc~}"_A,Uu\j;p "0>RK"Uoa>C_uIRB&"cV70J*\UI{p09DYdy|Rk:ᾓGIt?ȍ"81fav 0},ŘٕG L5 1s.e$ }=|#^ja>GNl [ Z?~OO1|\19͵4ޯw+{G:YV| VؐA/yѸ2$r4s=䄎'U]4&Yf-:";-k O<-6к_k4k{I]y;K44l֐sv()uU/v/9Mhޡ9톴#_"-: +-ԥlnv+)SƢQTZh ֑%&rOìtuqM9y$K$OG%-)] vWlZϑ$+ ? c]{ܣ75}ZT6R^aBwrp ;\0hta'v/]DI?_CT="E5b#E}}Eξu~l܌ûF7(8ZPb̉ϜgW[I o0ß.~b1nı^z^4iՄ_k?s>; $&0˱R2FʌGf? {g E%7GI~v U-7qNUUD> _TuI[ѩ_ǗryqUXYIq8RI#xF1 ) &oX1N_ST׻]ܲOK$cK3AZRrIȀ;ܾvϑkf1|tfw/g}S5[2hzRSzB%\+{8c{2oP_)de:69^GT(VN\I]'66?;[7rPVvT1ˀj[׮Hc"ʩ!?BI4wfYb2dp,)Sx%~&9u_#s[&2}v%K^_B\8><45 ZtV[㽡e_1[2hޡy/ uh&o"8m:Ɠ/g0yOcǬ%cKk>[AܚIf`RSPW@HI6?b4 -%E9dt?$2.]󂹥r1?YEMhANnGJ f<<w7zQn~@ $,OslO:ȊV(D+z=7W6=qxGڞٖ}9yrN1YYp {|X7W PVCi?a$DQ"Ү`ZUw2%"Do܇ a)׀S"r?a#U9wYcڦ$THm(4?9OiNl6~w8_KM4m I]JQ]*t|Sly?&. _2v8C-O#kO=+c׸^]û?HƛWzU“㈎ASUFp0d'-VaP'"1%"0 }dcJ;nc5&4BExm= x3 g)H&ȰlC$=Yt;c>CvMKdl(> ԢSIsRT(P?WG0tn4o7K涢F\8~}WubcOU :O0{Ƴֱ8k #PtO5u͓sdzŽϯj?JsFo tBJ?P,ÊVYչrff HX.ML2bMNJ񠲴wשׁXsVsG7 ;ʫMd=gW"lHUql4Tu=`l~J;g*|Gjr6$f:b播8LϏeu93?c{slOVN\ɔ;ݓW~Ao1C}Lw~lV~C;9"xwF5)wN)T3&q_%{ܖI˺m+^욿#FA?W,cbbcYq2ywJ"a'bO܆ynAŵ!Z ()` y+|=p7ek# mMۖnZv]GYD5W+[nE׋E Y3i эHl̚JwXYZ2fҪw+t+YB0w%~ U]lUE7L=OTµw=Y"S\ IB&%giX1z\у+-fjsۗ^-a)L'?;n<-uS+ܥʉ+I[fzsSBCZDwWqO+.۴mSz's2)S`UpN/d(&0+p9xWޑCʀaXRؒZ:y|}Ee\U=ᝇʣe[ٗW"uiZdf19ψؗ&0_=76v=[2;G-j7˔e͐9iߚ?l$?bVޕD\7Gsrs`l\\߳d_^%>-""EIlaBX) `>y C6'+-ľnJcgnd'X܂6<wS* fҒHz'(!eq MJ7]b iGImKVM8)hдg3I_Wk\/ZtiAIYB4VˌOg眝%|K^[iY! xDd & Ymm"rh,"r =#uڟ7Sy!eÙwW3xxF(Dp!u,-ֱ|uWFJUEy<!;qOo$~p@D DЂlAQ9XŲ;Q,"0ğ#NA4$ԧ%VeqpU`!>3y^.r: sGi3Ř BR*lU}=I٘9` D!h?4L$`L6 <ѷU5hZ~/HgJ#"gbzP־pG IDATtL|"r~U3a Zf ~EUADNƔ8 )gKB*Μع { "Bth<GZhn0ub "nL`tAqx=\JFL*+ 9Sa6ؽ=y 8r QB`ڕP"r'{ V6EDTe\r10,P՜(ڗNz W.K%-) ǾUXpHHגw$؄XZDžLIpx[jJbQM !y0Nì{Y8XDc6y(6b@O2{WՕ9[XrOaVG>Lu`ryP%!"]1 " %/"/c ^gcuFsEdNܝ `EH[BTopWBjv9%X)N7y@U?WLUݩSTvU-4FAW UPm905+"+30.%^!~ ?UTr=cs,1Rܹz&Ad%ǫRUUu ;xU!V(`7&`G߼ڼf)@N+u~yE,_/T31lRViqSZGX)qf _ߚ =z,/1fsEqqx| D ̖"2 O3UØ9OcW wrdo*sT ^QI$ Ik(61JBU=;k"+j4J G W il/ڿ*.nw{/"uo1(;XA\w5fO2QdZzLߘ۩qe1خ7*pm a7qުe 1$,RJBUcV 0f]"o_÷"A h0%OU}mu◳CaKG`JB/DX^&5^jn92*pE;w;Jb'; xu#QIlVDwLh3aPl\?t`;&Wyb]+ x3 ,2>WyeNT`l ]@XI 3Ɍ9Je}uSLf 4ox𰗽nlϑ$c5@U7M^c*>*e~R ~,?dkyhY,(Uk/x4Uz~%ѡ+RaB~%@D#bc~ e= .DU 0}WuJF!LPڈqq堦E~v~pEv# 9$,QJ"kޟ|e X&ocyUMV#lQzXj^Ye:nʪj2`ߩF|bW|U)v|5%#aJzbM$*@3Gm  V+Aק*qGR孺DUS1yQ|6_GcV_^DŘcw_+"3P1vZQt҉IBpNz,R;Aĝ+E͈n\zqoTiۦ4hZG+eiIJu5Nvz}ݪ ",Y/<>@GgZxMUU.JCUͳ>\yV"0fGQbm]$VU+)Ssa҃3A>IK^&XD~|'D*V+Dv{TJbDTLD׬~fywZMnBWO瑝pxa~o,ziuW.")0/iL+fA'1t9!$fcF393?f훙|oX9->_2L{`-oS^6{1Ec|n%Fa"!"0!gaV/~@UŘhDd:tnL1Gb}1nP`6mODVLă܄3UI_VTSqX+E$CDu~ń׾W✁U s޳4kINڟI&<6|mrpSqב?9a4Ihe^FiEkІwgܝ}j.sÑ=GA vxk_\E>ZoFԠA b}Y3i OL løհaFqӠY.|B.|B6M'~N~"W +OOKdڊœ'搕Ű'qɻ=׋2q=pYZD{æɛ ٌ aLN; ),ToRƼs1Az:bLa ũ" \)c*)1sG0ǃ$Us[ ಢ)2F)*ro17Ur)V"&rbaDJ+;ZS֤|Np_ hlD66[*IT(1qB;;0 }d(ZƔ蕋t N4;VE:-2v/}o9et_ޝ= ?"p@seΉNos`u Xq }|Q hv{>K.׋%2a4c;{N l(Pt]E*#YIUD֫li2_~*%g-go9zX|!"ǀi9>SAǪjs1:Ic"Kl(p{e?k#SrC9A4UbK8zN;\i[t ZtjQ}OOn]@vZ67$Oq1jyrNQc ?f$MN'T>wU .E'M= g_?$rJ?;w-$2f2牒9l'wtܴ0YYrTR'"$cVᣩD&iDNjͭjA"ˁ\!BPvFQϭL4?];Ҹ3 5,A6!hmxܮ"}~ iԢ't< 1MLBY@޽bnkJF9@*JSAÜ~Ew/Ȏ9;r3s5^>;|/뿩~_-f r$T@DaDe"j0f:1E]1Yr{PAFI-.qĦ F/X R+[lɈ(^%/+PIxr=*!7bʣ,_І6mJ3yl5̎Y;hѹ }Ɛ&6 zj^S $,HSU=R1ӤavoZ&*vXZƊ^,`VHLADl k"0R*4iՄ]Olb,sêOVqh!|7Ͽvg.8Z@#n .{E3?'̂]qP?Hf]}uovQhjZY;^/]sD #墪5q#,U'mj0ŋl+]06q$.r,u-4iՄ6q]u[qp lؼaQ_H5H]JtNT~G9l)G^6 c5YJ̈́:D&11n7*iyGR>wh t,sz ~?ctѩDhluH &eTn6W6 74Ux* )ȲJb8+F?Q x~ac{Yִ]S.負&yGANz]݋ш[Oϱ|_5 0_n?\Mi[@zChޡe>^w$4QM`B_x+RŠFdsHȮu!-K5!f7W%$lvV#OW>L󓛳uV?7`'{ c>)(;f`km,{k;4g}}xKpE[9CIfe^ܕ\ReN:8yJD+ UMqD} }-.$wXe&u}j.ނr>q2aθ h'ܧA2];R4lWص`}Cޭn͡XrgmG}JZR|iy|;,.܌1gGdndsػhyǴNcgkta'&dl`ٿx8mŒ^ܕu_Û_$}ЋwzOVM$Bw慈_1DU~zCa?Ze#y$AwTd{ o'}jˇyѓC+@D2 iDdL g))R0|]*#`311'[2*(ml#(d>GJZOn[Z`(_"2e niq.7+X}0;62 5/A,|~asSIV75Pԕg"AUw-Li0DD`vxNƶ"9|`=1&ޭ~u.gz!(1YAUH|Bu1?[ [Ūgqݲ8a]xU,c2C]Z|ʕޯQ"qGDW!C SDȸ E{L_Qy*x{zߋ*i*"ZaKI(EkDd3&us""u]"2[D2EKY]%"EeƶtJ Fr0*d;:ABJ_uz:JQՅփFL)ar.c51gLƘn\7݄i y|ُEdVv,1Ř 1 )/& <3}oh랊 zcr Za"Į}""/[ט1U5q,?0Mf{EX]""ϒks,LӤ,7d[!xRJVuć/j%a_QNQ׬٘۝9:U<a~X[Ư<P S'ʑύzwQ@/1=$"U,Es`gu?e"OqL^a? xQD v"DI`Az[f/Bm jV#ʰY^71*S%s;qlLo@YI~fǀY%t TrD =Gx)ǂǘAEڕZ>eeΚ_5~ 'Gb ϤcjnuƏ O>$-C dՇuJIXDKAV`Z?`&ڋ9"2e ~ oBR%.U2V]2y>(#s *Tz t~QՊ4Xyjw̚j|h(Bg–y+pᚬSآt Ep(\+c|6uE?ֳ[`h*nIUN@U`UyRV(is"r%F ԕj 2B[ƭym,fW'X,3@k)U|X3ZiWtPG< U]h,sJy;z~oumk^*oR T"r2p0y'݁1gHEq1{v}[ҨJBUc`zSaAƦ`փe8ĺQKy kj 2T;:ۍ;y zNu,P7mA߈ҨJy|oõ%I8k&wc,p:L@8[>ń@|Ȅ*^/ڍ;e! 0:"!H&aR4btP"?yDUtLrW!cJQϳp"8cv ?H&"0GC_$ bVVp YR?]A@I SfDmNzcFVvqi8V:wr[vPy&1ɷ;xOHi~; :;o9WarC)GA $$+0+/{wӽP;6xd' "_`"z[܎w؇I+/ŔM\u4?hWYE6xoWcըOJ#ƉwU}nyGD.jUzYD kF1=Si*jV5_+0Q8Qo VYs GACU7(LfzFD;|O-dǡs([Ro|~DčL#" >!"^@oUͶ[pFDݸ'VnrD&zwsoHl[fU)0UaoqDjiɂ"m$6u * ( O }lBDoՇBBUW),f)`'8Tz1U#V;sqazN4;Z""LSUrJ:, IDAT{Qndl;QBN/ޞ^į,N +ބy(=e8ppp *z|M'9̱[zR./k#YA@=V㗸_D"&!"09"F1OS)҉C AӘ^dF42;zfZDµN}'wFEݖ1a{TR{]1;%N!LgQ}Tw]L+@ RHѺ`fSnn햧 "`A:7BS,[zjV3(z.[P( U?9ny:"o-O]CU}e8uTR"-O(>p UjoBQj8 gQJ~GM#;.*pDPTuppDNL [TՇoF62vSgX"IƇZU]m<5$JAU'k׀gT3oꯊ?9,bD<+X//Unyj'Ӧ Tmi*".U2E""r!>l<U}UD/əv,g9?vS8JT"^Qէ)TD <(CbHEILf2SvS8J떢xRNA "7ϫc TNcKrNٲ P얧pDQ7E e6: s)P՗E7(e,G~x;JX> 𶵣xnxeKX> tWQ9), PGsLWD])na)yxx>m# xc ".aj pxGITQ՝Jd/VO0s1E"2΅D1.)MVr b*z[&pD5n7@+ZUflADN+T ď@D1ĴبS8nl!&1(G}.Uevd'N2]5P ,'y\oLo ^ᎂPTuo|X(DDb8pW/핪rX\_4 z,?<Kk4n*V#?(<Q g%Q 3$aT5VJADܘK3{Tur9"r *zikZ{38$<>ֳ,fw tRpD-""C0LL銷&G#"-?w`3WUG[sKDd >|3@$eMI,aIA9Q.\𽬪?-[$( vw`:&y`v WyL7Uu8D"r2p 6F7 W:"O )o8.f>oEQ6b,{oWQ/7 i#T D}QJw5{s>{̚530k!`l;W]]0s>!:98}rt+[aȢ ;a0Vb6!{:YLcpsR+wCp#M᥽-0xx0t&i10/Fa=`10>^.3{>WZh2`l4b-&1]إfCd& Y,f¢'ڇ>Oz73{S?'7ݐ"ۖK};рrxӻ`՘VK_D(H@xwFc@. ! i`|1uZ@ X0zgB+S~ D8%p ePJ}(=5N]D$0xBk} {k Kޡ<>FkMzMfo-$B0@8݈aPZ*Iڝ`$f:|؞> Vi2NZTƩq q }f9ݔB#!icIƴZN*I9C:ZO qL!qڋ2< M%L=*$~3csʏN/)jR~IDsK;mvz; ixk44in+rk%2wjO%} K%#ΛSsqm GޏRtKfF+**EAOII+}'٧74P0Ik-lO']N%#pru-g5~)xn$e6>%'#nL#ey. T3C߻@\ _2qIwr@KD|ܛl?4Fe| IJzYґI$!6II &I{dhII@3:'u_KzVq\b9R,=>ǧ ,儆ಊlX>n‹}4e+t%Y:^bFr'&0' ]mGh(I{KAIEIC~IIٝII9r)=H,Nϯe2Se ޓ-TJwVNW$ #.X{(Wؓdοȿ)KGi.+'1/K_Iwykg%%&G}?{.x4$wJD&,a]X;ILyFΫfv埴UH% t7 9vShm./&yPSI$mmPpz6ڏ*>1q+߄6ݭbX̦ZYfD~+Y%F$E uR (H֠g!ѯMUx`-3jϳE-@8#aq?BK] O٧9:KnH;^b$&P[Qy(b]FZ#OA[-şFO-<iW.16dx8ݍF%t!!C6+;x`ENﺈ?Ӂ(ǩM6qxڸ}B Ej$ptGʱGِ|.afSɱFwUR $L<'| \L*"I;H\ޒ~e&FrI-4>t~d6/Z}\(UL[8r< t֒֬Fz%}ܲ%Z2ua*019 h~ ο 󁭓҉=e 0S#釴5hB_#P0qnMp`42旄w<힒>W8@N!gFҪe:0󚨎8NSj$$Ip &,lW<z2CIt0EqހoR^L~3_$[KOdUKO06q4k2{QfpI/h8=(> ,8KMF?bauOjfV|5x/r_+v^gu3{*;S=xdQI7Q'^` !ǵ!}~ef=H i٢ VRяP5iDH|Xzv8mk8aa%[w/Izآ#[!_M@nfV+itvӅw{8)+^ ,;[I=YS$Ӟ64WDnJ5% ޕ4Kҩ={!(?GTIH'߃ p0Eْ֏-\$-'iIE:N5nNJ:SzU%}Yxmj۠N" aTE*@R?IC[%$#)W_*AyYovA*/FԻ떧i"m*)A߄(i-Ort./YAش=-W3N$Tw`= ]$żKyصF eg9- ۘO99L( t)5Jʑ|+~?5|8x99AAtNNLS#QJDn~F3JyogtZROM{e|Ql>y IvNkyـ.mW6BUPoOn"bهʗ9I]M[fߏ;#?6)P#Tro@ܱIZII~HL͹n%ܖ5S٤$$u!2$'[l铗l$Z3 5G'e_ȱ@%?Sޡn mG)yVr9'SZ/2-KeW?&+#%u&DNW~D7/L' ~ZR_3[3{.7'[sFIr]tHof=uE uֻt9wvxWfGG9rQ3ff%#F_؎04e wm]l ilDvYK*C0bT_.EfݧIN7[藜Zp}W)%W4tm~%N.k!`nJ%u4-48j ADI4 G$321FbL\@D&P6lT7%>AN76Kd,S> )< jf֠˂dObʣ{vg>#1,e\C}F^/6oƱ\JjLv)u k4ۀN?_:s)e[RzbIٸx[XÎ"k0.i0$fif 43{^3>Gqڃ=%im+Q$6ļS$]cfK;}B>կϫAZIu?5^ Ih,ť9b^H/EܻFt6ΧOq#>ϳ)7Z\3k%^C 2ricn3۽ D}tg([mcK;%XO$z;]'Pt\Lb2"]:m כͬ4(NsC5'Ԝd(i<.: /NҚټN"e_Iu. df%H8W_lCu a>ޫ=#N%X Sޕtտkt{pvlfĕ vYHGK|[&<+*$j[F+G]$0'L:k#8eOzv)bKN03%J1>Q=KrPuN-!\hvۣ.#]J׊SzWSHL'Dm:NOj*)ֹ<$,q-t(paÔ"Dć܆Tv&F_H7*Mn^s5`v/_\=O!"ɵҍBAu%8= ;:ᤴI]%]aff6QBb4>ЂԖ !B2$2& tb4]C2WU=a q78N!n$qBH88pq q#8Fq)č8S q78N!n$qBH88pq q#8Fq)č8S q78N!n$qBH88a IDATpq q#8Fq)8 )gĝIENDB`ipyparallel-7.1.0/docs/source/reference/figs/other_kernels.svg000066400000000000000000000337111412533144600245500ustar00rootroot00000000000000 image/svg+xml $LANGUAGEexecution IPythonJSON, ØMQmachinery WrapperKernel $LANGUAGEexecution&JSON, ØMQmachinery NativeKernel ipyparallel-7.1.0/docs/source/reference/figs/queryfade.png000066400000000000000000001125641412533144600236620ustar00rootroot00000000000000PNG  IHDRN0*tEXtSoftwareAdobe ImageReadyqe<IDATx} x+gylYeG>^񉳓BRH½eIrRmii{on 4嶅B/)ДH[ FVYǻubKr|:cY{g,yFF3jIj ` m|  `0 `0L` `c0  `0 &0`01 `0 `0L` `c0  `0 &0`01 `0 `0 `c0  `0 &0`01 `0 `0 F=L&Pȓ.=gffưYf01DD0-., CXX́x<3ж@WWWphhh2 `N3z*0*b ӿ֧#~Yg=NW hZ|L` 67f[[rAHT̀S'NL`!ؐ c<͍:> &0Q3XX[[I0????~|rѩ奥vwwیfC׭|kޏv>I#?9>vkﺫo>'f0L}Ύi}8򎎎Nk@XBuy~H['"x677m/|ыB=Cni#}-,>n?'/؆co? PPKu_$`@xǏ;mP`ukDC{G\^t_\V'?$ʫ wDjx]_ ~K_WB`lBd0Z ?!BuiAnpd4׽P^7#y oa"_~9^+z{ni A^X/\teNM?y}{K/ =|O&0Cq\෷]Ș>yr߿Cl]Fn/?!‘lbnnΉs;or#KKΟ<;η2.c/]pAɇ\\.qko%4൯~GFG &DC1x<: "X.@dgLLdCJt󢫫+ٙG)ѿP*s(p8,M/͞ $<-Py>=w$|b ?ǧ^h q5 f|O@($W^wc17E{G?|cy>_;66c`&)Pvvɒ%s~گHi?BkV> ? fL;G`(''RUc9s_?ٗeW} q Aha|3 O< % S]_OuK ygeH|nn>0_Ϳ2vE*t: F@14ͮY,X%wtuyM0vD3O"a2X,vW%!!0L APe#Rbc0 FM`Lb^`0 S`}-%kk>c>P(L-j;b #/-.ʀh3Z=Cz?jVؑθ|1&lN;eڬwttlaVSg7{Ed}z`M=]]AX`h=LPf1Z|}}SsUQ"pz*II,39咔15k4n+v AB$!̉ߺݦ%Q! 53; &G8轍 j)epH}}}^gWcQOcLQh`01MxթIpzRULIbB<̌DPxt .{R CP^z櫂ݪ" 6XzXDZ?=a E#a`^zC?F%`x0/Q 9KvFY,ӹ$#ccC*@pX̙DAPŬ:AvrM UiV-X6hiID:{| F3 PY"t EbAK-,_)}` 6Nv(()c h舖,XaOkic0k BPc[]Y qm&0`~SXl4 `5'-JjS+Z4-/nqc% ..>wsuQ˱NRR __[5j%" t] ls۵AVbmAmVcxߐb .+,oHݦiPCƆ- N8Lhb\6Zr=ZoXp"7 'w4_2zVC3OG\)iǢ(VWQ駞׭aQ̨r'eJvY|u?qZO~7|ۧ-@2Q90_hv=X`;{oKfvJ|JOfv+\΄%<ɐXM3F ㍉ pŠ)a\}`DCF )>]9hȵ3D/]&뿾*t/u<HK{5񱣗|YۍIѹgd ] \fחz FSN$94e07\L"P\ѝ>c)ɡIg$FGb(Hʼn]ԔʫE===I^>;O'oaw߷uyMex'ͬ~.,i/0"v3V@^[E"g>Z҄hdlMt %52 JᲘ)ŋ"iY5F:M~J̠Z!&`AÏxP\=u "׾u>2w_XT Zʭ/ ό> xM# +ϘLw֓ Uձ Lqay!!5r"R߀.MjȮDa!n篿e[I>#hL'5n $r `9k_ZU+.$,؛3ͨ-ՠ^Zyq4LRVn\$HE[?1ta33c;;;lVS ocIa=w,1__ g?뀲:s7- {W:CY@05X+wȷjXPh/QDkMF 1<@L7r 5__x BO?-|d~0px<$"&ȍムG}H?b *4 HO6ESvd.vZb)ͺzUxH¿~x?u_NX"*' 56}.Ǻ{)f6͠Bȗe ='LC۵m0+Rx=21=:{ p Ѵ./ڬFUS};׾/ bA~>b/| /7e\j 9o=e\W} &0''RBz($-C5{e8>_ F&| ̫NU%sIի}c#FGc0J]ݓZ:]H$"I|^mסY_ X@¤hJ"!/IFW1u F@n$p:}676|RVk۵39h5 2q\^/3DDu%i dv'޳ ., ^~YTrƱ#ΐŞUX8RBjNdnb2˕%d2U~`4#@D .(^_ -낸%|rDL3"L(.  2)5;you*Լ^vİTP $iJ`z43߿]8|.!AhXoAzXP!=Ȩ-nw[t.>T+N /VsB+@UyA`S'P(64W<dIA}o K3"#P6׍XB+-#T;zts.˚[C<%Gf̌ ZDv$030q0ck?*LFP ɫ⛪M#KB &BG޳t5#>ߖ "/&MF#@`)0 wlnbbq ɫ @+ DRx={ycrf~KθHpdB FCRE# lU[<zTf 6~cuV}D>T oƎ&) ɫ@1+rGo$NJ_ kAX=^o\w5n=Qs5! ===fƿ$$HM{~5#d0YT|_pfE scphhPaf+QQ_YwL`L^ FI‹ #f9fWdcb0ZQ8%ǪСVaL`L^ FoOOt0??fB@aRa(z̿"Ѣ@0UXZ\4E&RakklF. qV#tZ0zi=R6 M #cnvv. 4҅ kruL`U "tK:.,Qtvv&b?|XY]pucy)/t:= oDŽqÉFlod x|o7|>ޜ7j_o5n喅FS'NL?5U߯}<~]~`cr?~F{}Vm7MU_-_Ju{VS`0[FVk;"dԨ_yu?}1gS]!n]C>|c1D!pA:;;?s.ʸrotϿ1BV `0H Stԯ-G~do9u'}ց׭W5"T  de8^[%qM!##ar\rw _"eZU2"R_n1bn{[2O~rFW'wu>HU4[U^ܕrsk[}/^&Fc]EB=7=* !Yxpe_η7M+ܜ/#6Dݝz|_*8d<Ɔ!QtQU^F!W`?.X:^Wh<Ѓ<>T&n$zr!藂iK|ɃW:qޛ~c;>;_z "z?t}͋xh_ /SLOO;3W(f?FG~~g\wι byvʆX+&MDڌQiz@Վ LV'K>믦@b How~{&?}Isñǿ?~ڠry+^$~GOF:;;臏}%>~/?,-/-9?OAwDW_{}׽5A%L`nh:afcydTL>PcPl?Z#=liH?Tan1RIV$DRI,h6̞[NGFG%mnnڠ@x񱣗  ǣ,"R㷾MIPv?T#ڎ`*=Y)C]q/BY[QQ1IqYá0AB3;S!L<56r=>p뭦g BJ&c,D X[ s}w0ęgnAaGbg^veq=fG Z}̌4C!{?rV)S5?0~쬔m 1$iw|u`TwpC/;?=& ƨOWWFDp]: &.8*c[/}ZP3;_~J̆'9_o=OA a`RBut,`"G0~OJ流^lCr@ڜ̨dRF"=5mook6kŻ7f˨…ҭ/ DIu^O{mnn*I`8&lJ&ٻSY=o PeYz+_$C<"_ׯb}ﻏo0nsTAIo1?0[Z\֥b\"mk-i-/.jBIdI*!MʒܰP|n3UF1{UmȩJ`BVvw3G[b3,/*߅嘙z{t5׾J>uPm/}K7] GFVd)'5FLP_6* ;c f.$1 𳴷k ^WRY J)fw8$сެj VOԱ^}@,^X )t.:M|"\}?%Ϫ/o33)Mh{6000CRU\q.{u׵^3VoP !J 3>iq]J=e VS_b*}g*Qy`SS'\{WY0#V$"I /= d~a?X /6N3yʨWwO5d3>{5]tENϝVC$SY?XwwwP^ĄkW* T++y``£  (#))R t($78PŤ\."_401Q[zG^+"FHq6C%;:ڂ0w= Yrъp{<(dNDCc0#/51!\&f禧'C9HG13kj #Ȝc\OX+d% L?b|SEԗ-vqegJlFlItϱf22mv-թ[òb[T(=*gWwwU-@ܴFE)ѩf.Eeߓ3,VԉLm\]N vTJc@-X4`ûN=+t!ʅRa_ ǁML][wb1 xQFOLV=7C%T ktbkkAZ?;)`-`l]5#S!"fIIHD{?AfYKG!XՋp煥ViTz1uPaRbzba. ڠ԰L(p1 mVNMM&a]ǪN=ш|ԉ~O"6`@my€ vQZ ÇgL5 s")\W>z"a3 gZD`K*jF構/)F+y`]b:3jyBxU~9޾U8PguGͩ}8 ׍YŅaP_܈c-5 ?Z۴vl{;.H>wcc4,m+YI-eoXlydCh\g֬[q?mk)ggF50P6@Rߏ1~_P+H 0T3L@]!;-B&(Z+(Xl{[&VHhamP۽ڹejah|-=v٣!e )lM*ȱfT)ile׍lXg[_σxoۖSkA^ f#^{otww1dԗNѭrbeXV7+ʟ dl __TPS'*W1>GGýZpQȆ ,Il~լ5-ա@M Q. ( ( 1444K$̎x xs =e5용Xe!0/i~p15 4DIvMlKʎ3KDKrGKקV"Ż* jf&A$/@cH;ztjML81ǟz{//O/䎙p>AUq*F 䄾ZdrQ(2,E+@PL--@3r'fI5kdCKvv}"lv- j¨(w $ b/79AnuPsf\Tnf GՃATR_ce:A@Zy3w(3T6xB=]_krr@Pk‡FJe fS 8rҩ`ʬXic,~R-♈ӔMK9fRi]fG .51K%]=RNkij_AI``o@-٧k"w BQ,ٕ fTcxffL9eAIh:ZL}@AѺr ЮA-%5]I [H`٤1قq͒] ;Ė[wzڢ=F,md?c k@`uS7rLK =y^⯼/\ױc*lF0VC 3?UP'O.i{BY9GF* @=n{+.}`A^DV]=?|LzҎv-=Cؼ4?:%+A!° jnĸZbn'0==jy?Ƭri jV; YA]s:!A:[q;Ph~`A^8UBìɱҐy -kPahIF,G:HlN$=ri5Ć?K ,Tu̙Ee~~ "~(# >-URj,tM `|U@u`t g@V -W%u b@V E-I,Aff`1.fXWʞ!4M ǎ֗29/5"0j《CbwU +LI~ -sI >0iF-)On#lUR)*(`A G++TmRb @TmY[-{`I2-LmVAN=Nkm  U@*+Щٛ"aAZ*bd,͆x@P0%u#bCRoZF(%L X{ L`%H,@J0#lLLCffIveWcRk 2 VT_+++2򰫻{u?N* KYA:Y] 韛֊"3ɲY&NФ yqd-ME-/,tf|Ig'.N[Zd_ʺ+bFޮL)bf L?SB]b@CnGf%2\({|J;^Ǟ"}Hs*MvצE;tQyʂ2a auvvp(6@y(M%' Mh4J&}a=Ym9<- X"3&,r2c11:NtJPrOvRrqQ d@= l_SXnkgotX9> +6tsMuyz'/*Q:!QL8$&`ՙ`?׬hDXm!% M'/JՉɴsy[$zIXc1 d@T]ID]~IlF?Še;yVj|+e0"IeAăQN IbYl;C 1n,Hk6Wh'<8 FƲ-&%A-3n&a"lK\S ~T#К$@qQŋx<n):M RY ,=b* ^ZSliailk BI9""AT ɍ)s%v0by*@y%KNWf@iJ;໢L{w:!OW*|Y^$[y[Lfby1󕋌`$-_O@*-!>p\Vi!UF*11qr=&~x>ki:||t1*!&SNEa:ֶ]VU"to3s=7kc+1וmBioגB y)|_T"՘_ 99TφF%䟒pzlBm]Phi F=`:kHDS&^h"C3jv /c4]u!d$6>*jM4Mdz+nj oogՙU<0"KwVJ;m+RGf8uS^j5zJ$UX1E3YasOO7P Ȭ^$,G,PgF%ygwޣU?0L[DD%V|G|34j􇮳}^`9#UPgd>* Ey _Ǡ "iʱ"%$ngh[VQgG>x%4#T,HP AhPKt/Ȕ5s) .V` \ ȼYKU夐e6 F+nD`N֡ThQDfuegM'l[T1@;.HnՒ h߼~/e_V +(*)V:2l_]R.C־,=ϫa'aۭMy`o"eH@2u5RU}(=tE2B;&T(HcPҵأj\xA(si̷D禧(ā*|u0+(z2t4#sfY[Cn .KX w,sPTU^5*v9ȪtAj,DDZdH.e 3V]]-t-cX#&mqζ ŅR+ dEKFC`VlFQyl3S6d<V_ ,-A`B4Tkb6V}g L3DeȰ;2g--П·iPbW꫙a{%aWqBy JPH. |}Lq/u-ViB=0-XbPZxq4mGf ;2.f1s= 5 7MڝC?08(l&FqްPc\/CNvf'Ӆ#E*hj7eBw)@-ÀQ<0N*7)r>sԫk\>\MբQ@ r; -sHT5ۑYZ{&̺y$udNflGfLJLZ NFGGC IZ݆V˥(\mog#O!`JA sQULwdf`B@VWW)Ysyyy___C/ 䆤fD&@+_$rD%#A:OWe_0~+'DGG 1XBê͍ _>U:2S~U80~kQt:`v>uĄgu*~y\O?blv~[ j ш'U.g൯zo-Ut;dB+;Wyux&YDL5Y8blŸ qnvvՃ==uƯǟhn5c@&UmwŅ1&G T5V<0A^P]P* (: מyzR5@W Ϛ ΦOfѣS|Sq-b2; jF`0aA`Ox`\9uj0-wߡC =y 7Vֶo~UC`4v:$zdtt*_E(1U6 0/m0UO* *x *jV 3I7c8TbzQlF=I y`~k@=?:2#[^ʮ[ZQ$ڦ/ᑑiC(VH 5A}%I##u(lnn.3;ш5]|R9+0E4;33ۭrqܸd;E?0{|H~/;dUXUVk G8)^5IayQp"N_f(]UkI8\"*!Ys1  =uafd\Jv,K'ږ- "sޚ[ZvR: V8D0*.  1{CHrU"zngTY~jS&!kԩYUTb+A[E/*8W~JBFT`6lH=9`|D[I~wQ)m[G="5GlEQB>Cw*B=@` J;IWL%YNL}TNa'5JB^Ke:qjvą^Ϭ46j&^XF)AXIR /Ck?Kh}aA'*re BU}QNƕS@ږ5Es(/I^mV-etK2.;F"h$qY*aZ@[/7.KerpkRHUȚqi _>amm_ 0 LQSP~`桶Zz=Gt67 [[ƨ=JÀv=BA>0|\xDpWԠUzp̍ض(،X KU-*]Ui*@AP!c\{}kQs9Z{RJz5=Y#A\ִTRK۵]KzzsjOn4;قK5Q.6rvj o?Z,T/7 H R]l2,8!-wx!<&bZs2>1  Ƿ7Aȡ$I,JUU֦%<0`3{FBnIH򊍜#-+e㔶7r{1IZ XTWU`!㨆ZьPZ9SAVQ]I[؆r$*q 0tqo7J 2ܑ?Tr /z_bWAah 8̳^ .Ř&9*{Ie6ҁ&-T yhHprGs}f)FA͘/ \H-[Fnh;rGHPju%|,_^~lnn 9zaKXH1S4B*2ąI'Dwa aQD35?qAq5kx-T7@d[[[^,bOk@f Xw:˯u[,!f_iIMVdpGܟwfۈg&&#uS].ˮILK5k4هͼ8-C`C i:Dסz$P-;ۚm}0):Z[O=BByAg@BUXq1C_zVѨ7L:pbY[[<:@yq# Pʙf" dҒ-֥m{q'&2% >8^\RFQHC ̊x,PS2}H!#Cc);`6ubJvyq[yFC/ ,ፍ ?aq=X֦!]c`,*y9Ɨ[Dz,Z[xG1"/99ϭb}ZݸkJ`2FFnyA9цRXm`~#+;T G}b f1@]6Qi!gNx{1Ge*j؇e>#4tq 57gQضc1)_FRȭP%|W_ߒZ&3זO3_yLU g|HG?q>IChY&"AN 5tHaNo0%E@r`+={ a'Lw P''0I .[bvOK;$l6-H $IY2MDƽk*U41 ` mom¡/1P:5"n 4~A0f-t,ta٥:c0ͅBAŶtrx.ͩ6$&xLKޗS 9=&jd%˗x@^0 ivGi #LcGK&TJH?&DaeI`w\w҉)MBVmT@>c,hk>@l}r[ߵ5 jN`CƄ6"9muuSx\4# Lpe!.6[B U0`W`4677lsݶYޱlYV(E)@jF҂k ;e41fgՂrIm_R5|b_y` Fm@gYU*;PSVU%ͮ)If=8 229vy9I_[Wzfy`0Lt˜~?7F@ełwJVEI[.ܺڊe3jxCvHu!}9fΔz)0Jߨdz5~$ΌVEޕ5* +Z홂Ad3E r3&6וLtXI/W^:ݸ(<P Ik\LZX_[(;8WFJf̰U/⻪ "Z*) RvYϝOhCrd됆@.OE4uVbٶhߞj iǻ-m%Ji)U {6A[:._! -Jb* r$*1i$y0denA^pl#pSE̬5Y٠0zKҲm#@dV;*-vzbElۑP=6:`0jyC%A`#B&*tT5Q[:qBE\:2OTmniEkNjGg uVQV8LkH]s!֏bô o*Ujh 9oi{{IGRh[4tBI"s4k]ʪSl4qfD[ZVA}:$eEcF3lߩnF`b(vqXfkm<0Sp9e$mj7!)%R}1LfS`Dw}ǀXcrtT:Pȃ.xfTa)m9MPvW&pKA"g  >/aJ *P~`U7 @|,/sHxM̈́|=P5Y5hפʫ]D ŘI!J̾.sfEtdTXZ\j[> 'l% рbf/U؟MU"%*CqcOjT@p0?:ikmg_;>$/l(D5%]VIb؆y]{vGPJFE b[ mm;m ߷M|,6K425: Fkyi);^[]|O"9c^ _^^nP73G4/?d!2];~O-Ce{YzHZ{r``~~1_S5T̘1J &ްE#9!reeſ#//dhsKyt>F:Ъx?"pP=)1c8&^&^߿@)Kt x^rzU]n r g`ppkxdd #Roc4 7+a; }|~}*}t@"#R;ztT 03fo qLGwlC  )|,o/|ublY9 qq*F֊UDĉC?f]~?)R<fc4's@-a<wq1!ЍB]5PT \<E`7zȔ(8.g%4GIl3FHBb>~` Fk@F u#ͬ VDQ#:@`PdyWۻ gq܊F MA1l8qU-Uڽ0 som! ~(|a`DBs1Y'#P{dWl> P tR4*.;*K`tZP*ĖB)`VH>B`;tH~-V`_eB@Maf<蘄:\_ nx= DŽϙ #H~;e{+Ѭ@5Z{%TF UAnZQ1dQ:8I"8[Ps eӅ$9 d }=!Z:6{$ ?Q"XLPV*ZW˯p:AF[ŎUmmux% Lf*`ņ#0gh?z>ɷ_*_R޺c`T(Z>"cD`K>cMTAⷬgBZZ.A^0hjPwD|4meXf4 3w3`Wݲf<VD)ԚT0Q1x6SV$bځCV8U#fH`&4KL1 gkւA]*ZQw//9TÅJ|%UHy`4TgPEW^!aJԦ[5 ˗Ǩ'7Jy8.' {`l#,j]镌P` Dg|hlDGr<0 g6 Xm-4( nDT-djf3b́~$ kTr<0j cjFn[VCCwb& M cTBkޞLh*zn};Zy0yh<0AŔ$E`4s@Lu*$跃擅zvPof4y<O` 饛\*^+ u0` I%PT *땤3"_" FkAhT"0E[JdF˄(͐X:0+WZ b f7*͐hpX@l>l4@M$U-J YtV=h4!LhP(\>JES.+UXp}]* c<"(F`rC~2ezźHsѼy DRՓ [&6a0+ge솭r@qJnT2_@H=0Άg0zD*hDQ>& L^,.WhGJ  S89X!""1y 5 ʀW*0c0}F A $U0?_/b݈F|y矯?2tjA?g:c ay/w;o .k_ǫfٮ шx,3Ͷ1 ?3I o/mIY&kB|@!3H$y`o0%N? {ǎR!vĉs`=266NJ٣ӕg?~@jW_7δ Cb~nnd}}׷xx``QH-F5 eM0QQuVpsU @d;a72&D ? HBkkk~$o24<~3aJ?~|riqqE{S$_R:A8 x߂);~qgs))tE4)v~~~d'wP&Ztp^:ؠnwoD:.084}RS' 51נHfHY4twwLxN goEȨ4!&k|Lir+MF`yK|0#񸲲2jOF0(8> A0Uݍw[f!3>fgǚڴW,?H8-os-*ʊĹo@町B&Hc"- 偉0 t82ԡ: fr|H̠D `0v0#oɓAC AM/fgfƦNA1>ФF|>~GCXg$hf G̈h!~UW7c227*`$ʙ4#\ ZQ@F||01T.b.V.̒ooel%8Pq~@X__Am`чBe(mf`*00S@2t $"fKU6U3*r..{O^ܙU$&͈bq8qssG,JYY|]S՞hqiMr&Q` %'9$`M}_M@`n3ap/u4)hV#$  &ޏ"sh(7 D AJ]ȗ͇MF`F)1A]|@n A`a͂̚/$sblJd0*4s|؄F,TV*cS"aPzaaQRo6)`N$U S"e0C Zn̡]z餅fo~` &'01444;;`6|fq7MX %db0jtpԥ10>lf8šPH? |(<~<;5UUu~1Q5|[Zw^n1FU9XZ#0?lqaaa7xɫJ3z>,J&z}V|fE(U91"v3xQ`͊e9Y9Kws9Ͷi`rLZ~|Hy&'0U9U9/`xs2}8x/ ^`auf5Vh=! <8#|_2Rk2͇-F`PWأ.](uq^__d0׺d䠭#0%d GFF8/U|u"504<q fE1u?̇"#&D (=|@ Y2>a|YT 2#* G󂼾hvH#[̇[6I]Cbh.-Mm%VfDEKOAlDjUؐKHɱnMF "fEl%p'5;TJ(rʹLk/WS7bhuuun<_SQ Eoim%)ZJ+1?} L߾ȴ]| aޅry L|sPU'pXVbYRJԄšm W/F+ |WTCb<•J`Y!Yn+$|j`-K~ɋZ >l{r H:(z+HF;J"s,HՂ99amdѦǼH|el[i&1KF P:$ԥKBضK-tJÁ4rBQnpK=M(V +<37C|v̌/׻PKKK5VIxM+뜰R>~En? !f#l{嶹9\Wb};Kp'*Ґ:QK|׻}}-OoޑļW҉3C>6?uרA.+1}a LSG)8OT܎4VZhhlϻ]7J@;? ]?s?^2wt{O^{Z.{M1WR/t};:wsHsss->|w}C%n :J lh;p`c.F)s#ggg}D:Hĉ l0ǔw/ۓG~O:={&=`}!^ k~W NVs~#iڄ4}+_>}O{oD9`/%"&9(@Cmso !kNu_p/O`&?W!"Afl8AށP(F]1ޮj`d qŎ| &\OG$)$k@?7:z'Tb"/Şos*{k g1xK_2+Moxey7X~ )<~BT9 %ĕ/vpw"ᅱF{G[d驩vl)/ٯKld'$՝>ɜq^Ą BYi Zh}ǎ .gNΧRdrs?%ˬ*#\sJ?Bhi=vibEKC>Ǻl-}BiɑqMr>H-kKP_'sAt6/񸄒jhtʑ-[4њZp]&'۷Azbbeob FNi״_O P}57`04:t<+-_5,]T+֋sR@_3 1**}DwlǴK1E>f Yz9~ucg>O':u[L97FYiwjt󊺐 * sCǻЀ0v( XgH3%[ >-1/aaH-qm ԕ?^ ;, d؉BfݎP21>I@jW (] !o{}O 촜W|ij᳟of$WWOQڻʚd[ ٯރ܌! 2eHm#ӶT1ӱi3M]/n4 [<1 3H#)] D{‚#X(TQ ˏ=X4kCd=>.b 7D E)C4w#b9_|#C$z -yoI lҬd?SΩr1WgWאFeo?7D6`~!bNdgڗ';c~{x,}P}(C_m"̛xCHje+VCC`"כK^9٠,%ZT^jJ`ېv `z}1\J9$e+y6NbA lv*Ikد@#4$%&+M ²X Q3y |o.uSo,#z'!P.hnnnׯ@TM6nϜ9)t&z98ROD A2Mk.몈%<ŀ]f>0Zlh깣Mq!ˆ+ڔEn/57qJL$V"GeɫK '[VJ«E"C)m0]T̼Śl6QU8~_ %dytrk73!f>5b7]w4mh{ȳM f8_.T=2S>0+V8n盋ePe"\Md&tI;p.pU?0V}}薐3Yj_/~;v9LC>J #3[|6JA)SoWnY׸WA%xL&B'+3"є2nPm\Wr L.b(w#[Y*P7qG|$4s~?)Y6|^EEtU4݄.'/~Juߠ%E_{ѐX\׷(S.;<$k(wPޔ}ĊBXUޒWne_Ί:/LS L|D%\i^|_̘TISS(ELˉ5M;P%=0U.xUoլ_kAxYؒuZYG9 ԖrD Ͷkn'aB{r.*x(UZ5!x߫Ɨ;mتlxߓM>˿kU/Xa$7:?6}Irp3=4ϫ(IITIs:$TsXʹZ@>4^ u%sT7T!`%0M(TU\qj`"k-OW`jy]zL 󯟃j@_b)$5Dg-.7R֧@J*bz%(Rv|agWM^uu~9/{;[ cҚ/.HxMv?q3y/ݻ B@.PW\ ]/̮ѳ輷Ӷ3:3ajpsue%y/xk[S!?_'TJMΟ6߷bt5Inu-k`}e%Kj"maXQ[ǯ 5^u_A8tKfj sWk[%1q"ÔeahL3d.ADURENRHW70Bb`j~֛Z],z_Qm{lh%L"y'VfH,HŻmhg+Ui# 7CV[՚e5:ҙF+5؁jzK17C3` Lh]X߱.Aq9EC:ۻh[6n[sK$eI\B" ?陶[6Pɍ]ƥњ1;Vu8iT+YBSϡP+vL:DJ^lӝCF [C#$#  h[fyFFx[ ^BfMV%8~|bYzkJ !)J` gz@!2 BkݑsRq\s^bnn:Iv{4'̄X7 Zk[6a~ 0 liQ;B L%.L[abL[k-EO"vntr X4;vtUUն.9]?1M.K1i>k*ťhcr2^PS( ) #|NATg tݶ+)HP() EHp.fE() E0A&5 \>%%vX L.PjEPl] MMNEX^PW_\+8YxR]]ݼv=";|N(ʊ`zt[!N'2VW^EE! 㫰3u\Q(vPI^!fH|`pOlkĢŅ.3 e#,.//)~̀^jB. $kN@BtsMp|'nUlvPVHDp)15݌ n{n׭"SR\s_$rr'S)٦P(k*{P lW3{ wJ^m pm!͹n^>,ۨ6},~C qׇiLtsuJENc旖j}{3 1 9#3<}H&kLi%Y !#،wnjAˀyM_ ƕ?L^·S5%ufއRNw-/χ{ncSx"9MLLx}I{|۹hN؞JH;^b0:'kjj51RI\|jT5//^6E3f qz11謬| HCJ\p9hi , Dڎ>i\̇h6tM\3'n Ѹx&&!H/I5/ <;uAT30>۸%$)bT@&Lj2poԮ5`0 sяֹ "a\ė&jI=s_ FSdzSR15C/t #dt# `o;p|c 7F7??e…n C̷򂷵mm+ so1ߤV$bWˠTh'2%w\ کuuMbjhx> M5;1!DLإB!nj(2}ƴB t-|N\~1yg o`rORy#BkjV( 0ҭGLv;1`w2lRAREt7 SSOt\#nso 0с6kfC}LvH4*dB hZ \7ܶhafdx7="VMU\m?|h4RzSIFjNo2F)B LQ`@?;4B c0lBܐp%A$ZDԏ|x" ,i{RHA~DBI%*=ը>+29!rpHiK8Ѯe7P4=H{mM!3hR (HE½>Wbuuz%S q44!XRS 0OYױOk B LIW0}6s7|loBPSυTl3`zzz%/BMrzE^.WN !$AHJR2 g*hOP+iYۄ]ko!HbnvUJٴJYį')2E*JzY^#t6JbF@c9h"ID|´##w]q@f-mpo5PaLDbs;%YϜ9 IA<\Fs3yl}4ߢ$ Wù))YE&P;R~Rw'#=)˧i eQ9JfW3HWq.c ̽᛹LnJ)|Q&A ٯ* E)w2w0IJYX#BZRUD#&SP @^>$/1RMάʲ?eVF֚R*]J)'T:=_$c6o eSPsғ\dHt,H;@.lb_ږ6!>j @đal(y_Kg!+L}LX^UBM9IɥjR.]ԑ*g$hRtj) eXHF=lN,RH\L!XL'&:l(߼RụcO)c 3 SbF@ xeф1\>|8%N|7-Hђ($cxvŜ" %0E#uj$ƹOȴKpt?M|16$,Y\u'vz.B(sd/}Fd~A>hBd$XSU{y.B(ubkhgy)]Y;%r;9\2@  C& _P__ al6 1ܿԔn*,,L #!VinΗ[P@:&5{I':;; LjAw@F BbTN Bde XUYYIXj---v>a=}k\jk1zGw@F B+NNNU_X}FcKFFM~LaQwb=hkkSo|Uê.x䡇rmݪe%bͷ?AnOpA"2W_n^^MmMMINnnXi6Ǝ[-aIV!''ANK.yyWWzBi:::O~u;6 GnhZygzegϙc~dɨ@P0`-ڌ]z,/O```0 bшm߶M7klYz& 5,IO=4 ?k R\w)'֯[ @P8 jzSsss$aG~`\H=kX^K^N^+p]E~x`~)˗-+z]䗥ߙ=gN;}/.ı>XՖYsf1r޼yfIF (:Β"(໻udTcːc;HOOża?}OeK[A͎񝙙I'[A`%ڇ'tR@L $WWbsF qEg;(?{Zx˯] ˈ}'p;vW~{|ߒY`BU5P$PԎpYү@ gN|`Q#Ejy'ݛ+ ,( ݅HMMumi<˳W_~/poIq#>M"WiӶӯ{k /lloA^P a~X.4@>X{ D`Bg+X6GifZyWQqP_A\.Cڬٳ́ [&Q$/~ӓ3tYa@[x3wv>dddQ$鯒 1')͵I>@vJ^D`@ I@+ؗ + s9@9yB_2"J= SO A\X`(]2 3_{mId1 Ί}R#  >(IXqqV;d(珂o`X]ȇx/)B(a)VXwuuud>57w%iY^js8180:bw<\|6ɸ1$Rq0{gr~}OK )rι XbP?*Bc QvN_gy.3djLZu4~,'@ҲgBpw^^U*Ǽy]p-DS K=<5~]cZDϛ6`GMo*N1C_{(,<zU5x?и%xf*̗-_ބ'`S K/9!Dqd^' Ns|SiYE.;{ʳ?/K"ayp#:1\@q{?{tl;w^,gG,,tpQVaH‹9+u;lD ơm Y"ND`6yXȎ4G[1? ;< ~ ?;8VAZ۶r$/\J' љq<0b2 TDF#_4"ވ1\f@dUUֳꃍ X<6[To=l`^~ 12&T}'ryfO061#}M7կyj%K?R)558QbzQ#ߟ0z| LB^5W͝jt>(DB/ڸo9 1!𡞸ڰ~lٓgj4NW/HMK뚱 :WPA>0o5b̏{U0*7 lQ#Vy?igǵ:A;`( MvZkbB!&$ ^v(H *BXPj|byw/B"73sH 8v8;UJJ*e%犑y 쀵3J~yok 9:Y`AaڝqTƭ(eU<,/0IH $'?6@\3q$0z:ƳQuAHd67y=K"Hf5x Wp~Ŋ[ny{׉xbcp/b=Ax‰' 9Uыt!"@fJ> &.p#X.Dݓ%*T@P}]$DP:; #+Y}dyƙ*, 6*'=RZ#b# viBכj@RZ[D J2xX''0! > yR0u4Q@gFc#k٧1Sm"0@ĞyzǕ,M #!APEC,SN{ 6ңEF B~"KQ31h"[X>tzf죊؆I?U8 ْX/ub_a8dgg7=z4_צVI=Bl1㨭 5m@z8Jp[EW;zx~sC_cE=Cmٴ4Y,FiArHvKZ[EE5zL8l.OII1hAAm*.g3%" "(zH+|/Rض,{Wĵsf%;WZ޸㿊A0MMQklh(hoo7fdd4wuU +4uu%f5bpݝy8&BV`_ܚ^m>tC U5r@v UUi %Cv_spIOOۋ"ainΗ≰#fdu{2D5% 6gd֡! 9B)ѸcYDK[i\j䶂Ғǁ`-=V4Y[SS",E?#a72T=fL=q7;ݫ Dhq8GE rUC *3H˓ޓ ⋔$=PrRxXYT %eʒDlI^p-cyey+:@ #(hRJJ`$\<ķv#BIN5Q0j@:?\uގ#h,GHe Gy\tisjj"KQ[ 6>uf/{]#'Z~n c94\;}7ۃV7s\뢶" LNU٧vgUj233P+[s-l^456j9,`<ܱLu*4=ccVf .ὮLĪ1X4wd~շwx /W\X]UGj%==d˄bʔ)y樸p#¥ե~ ):y!nu 46[PCa}p=F3ZڙNslV $BdLp2NiRT܂#O:'emߺ-jz^v)Fo .8g /Ī4KVUYf #XaQ8 PI \UjNr,nnu~F#mcB["+g+b˪=ZZ:59%w#N*xZ4#C""XV6GauJr,0*0'͉֮EEx;_ʧ_,, $Cfc,tH\0*Ŷ%u^s(UsG۰a({0#:M2a aD4\տns4+y_eqK)Q!b7U O#ndH^콃"&OSbW}J7X\+܃rJT4 =ծ.G*3?_oXs-_ 1IVX=H~I~2x1Ժ]$qb܏ ǹg#0$2J /<|6D`D^B?/h+hyY4V~{q\sYD`D^!;nmt!Zr^zˆ8x~kI4rK+yq[~zQ?uaF$W,eiWYAvTʝ,W1!λPeT~Sb\yedd4WZZ/7*e$hl?i>oqJ o߿%VKTUxc_Q1fZ%/?VIצV"Xánii19AIooo7h%$%*`6SRR̥eeht Ė{kzch8X+bAd [cфrժWRR?e<W 7ްkzB x2MEoZp.Gz('  D^xܕ DVA5,X,Ffgg7 =„xŚU" B6Zyg Q9 q}}}j%3UJlCXף=V?T%m$%%KƏG@p%Jw%ʇZyr׋Cy-P>ZD\  3;d2t#Žn.}TIK=3+цZCJhN (7> bJKT+*~-#zx}J.W^ 0=_xE_`J>'[rpf%RTu!Ƌh)=w7R|=:11 %u JOi@,0\-LTxU!Wu(֮esKOth9rl6=f hQ5kk5Ji}qoҮ(y'@7\su<q{P< "$B0%n{>lMԞ}~V*p7X6Ɔ!/*.fBcٕW_e?{SI^=HE\R?b 佡j~W 4@,}۶VG8|/nF4/q/=I;7/FGsA_@"}56UV;oeTb^ݑ;٣-1" Cˋ.a4GM K\BHozneE[: t;W.] 7'&dk 8wo>3=cyM~謳LHw߿&_p曆']|Q`mm[,Tl_BIޮp`=['rY~&1OĽCA5n}w>ɱlU|7.1fnB֭  ъ "|%MnUUV[ ~p3D"U3֣b8NB(Zw[ܜчe{D%?*8gOoiǎʈC^@:,(,jXҮXD"DIɾ~?T,T@ G%kz_m+1sl6 I,+A.Pmb04@so1G~ suZw#Q`!vj*FfELET //uh0L=#4QI|`b>c;a~4h=\۾ުkH3=v"K7Á9,)I, 9`Ea'#AjxxXND@8d\%mviYEx@6'IXv8?WLb;pM eXf O:y%"10´=:_u_PPsJ IRw;&AN<=-[SW!\s?ub~/><_/mygT"?Qd\|p/E6U4 }SBIVXeh}t͚='DubhZr?a(:m??r}QE5Pܴ 4G1cG:$. Rm\w_?, / ^۟&X,Դ^`Ź@hs43W GmXR __)XCX'')`x7=#Z/b`]pz"W?1觰N;4SD-00pԔP2!P8fbTVmguwwDiJ"Kuk /8u!Ju>by)6}&4S$^IuVڹqqVa*CJVwlX.))-"^ЂuttMn6nܸ=P-Bh2[B !&΋/p|_bO}-Ұ&BrXIZ v2{XRRz%b3qr1u^}N y>Iϣ-])amߺMk$+}%04\pvrwos_/#anayZ5UeL[7k`B(:zx`:oa>Ez:}ú:OGo/'/JŲGu^ /dބAa?3 hDAJRTJ`"S\R؞jKޒJ Տh0yo- YYקyt- %5{.Si^/90 C5=6"/! 9_RKP z^I zህ /5XhPCӸ ڥ^$˥+8iFϻz8 ;W!"H瀕~^Mo@R +au:dYc_/vƱ_|*7x ʱ`AonF%CBnDEƤ>5ԽĞb Yf}I.!+T#,#Rۛn i/Ą d뢋/qYV 5-2~8֣2V,7f"/YY-.=^rY\2i!s$?Nc<ٳg+z,$|튍D`/? ݯBy/WKK`|yR`# 3APŃaM8Wޮ%s XcE˫Re}t:)>b鑰8܈ 0'Q{KdMw%C/F^;,++9rDkS.TjW<ɖc􆎀4Ӏ|~lb'!=W"FnDD`F" s(,)ާLbA^Ha՛+ 7KET -jz\99'N*1p[2&0=2 KNIqYbI'#FĜԈm֕m0_rE,ZcbǕTC\/rRb*^Eˑ# Q-c"p0m_(! k2$&& LSʄR ҏn_yG59ܞP7%Ec⽹NڨC2gB^NIGH,Kڢ6čJ̸hƍ]) O]ۼy,L RU*Vl6CUq#܀]e@:'&T5-Le Q=JNSB@ YE)5?Tl^"nqJ5%$(q8>g_acص+&s?A->}X QUubkc(:0R:ľVǂ ]7cC6/J p#( \ s犢W `&NGYtb점{s*P"big޽G4/j#b޻.C&"P)͇spƜs;9cL,4 &_pRy0䇼-:]rq '?"S!8:vO8㏉E`⁼M<ܜӣ%<n@LZaBGN3ǘ< w"kt~U aAЛgThZ[[U#Eg y}֜~1<|[}]w@T&_8U~t' "ߞd..%FcدZZZјHOױSƲ/7yìqiY/3K{ucHa*%;X_R2ǔķƃCbKfjW'>mؐ{\0jNlB0!`x"1[zU5(L|5ܩ|‘"/-M{ن p" @B||Jv^U="uu͕|߮a_m{F BXJdi;8?wiЋ,[$-\qʪ|NM /`=oN#Fv=#]ԯboW"h~t+b,0#rak+ IˑF`~;y|8q/~asZ鲲 .^>oײ[o&z+epIU*')(w4`js+KQnE`i6!$1 b ^WP0 !ڸ{*^02z 3'=Յ2NςS L:d}6Du}ͮ ;86sq"lyrB0H6ůczYC^䁕OkBDqE-bTb|F'ߓOu댨zw뻱}{ %w@zphc ̚=Fb r+%,+-C܇ 믿$]_nVY랿S8 .#`Ae^;Sw3Gju1mn5zӲr A{ A$Ub=9YJ.Hw7;yQS`A<' e% 1/ȋwZ[[|w2;ruGٹW0]{3uڂ}'%7pEX$C!V 7Gd$ nb5T?2K)SrbB kDTIu%qenJŽkت_pAvB!6ܦ=炔"#iu̞7z5nszl5Zݏ y` HdLA9}xtQk=\`eD`AJn5ቚaM޽++5(Gii:uTsYK~~>hfY;@RY7,%nm BF`6@mv_}e؟V__FX3fLאc;vBb" - 8SB@ypC y`߁>0Jmt<>10(2}/uM׭}3qkzȑfwmm$so+(ɍBmyLNNVa[p7 %8iRyị|5Kxe1Nb7osp S@h$#RZZ @II XVN<N= )A|@bA#@ %A\ 1D\A$\#? 265s+(@f9/\4uXRk}ȬG Nhn/aeI-ĉe?4kqHwG2BGb?$9-JN~ j>?s+BXgj .B!H:ƆϚtwHm<āv+u oQMH`ʔ)?T'CKfa+7싊$ұf',C«v2|Ҿ8=n&_vլWo>r} hoOqvvvX֯.O2٥6戫}Agm۝i؆vԹn:b+%@4Nρpԇ(?re\+}"+d^=y0V8!vw%o`eNgMcye"OdW]tTI2yӥ{;w39AosH䁹Ă3P,%A^BNeY5?|-#E!rE_Rw}TCs~ˉ-ضm[YMmmCt {3KStF#ZI$#vs5 3l49ݗ/dR9G0*%#,K︵x&zq,uRY'_k tLU6xĶhr WmLa,{Baqպ}*D N+KjLNJWl',:{1{%R^l *u5 A4p SثJFfXe 3|' ӆ )t:fb=== ԴȎ>?_quM_H;݈qi|әBv"bKN³hD~~7ߤyAuw=F~~ZQC_~|]ժv#cJӧOoy s<IJVx#+/`Ng-^}}l͚I5bY !&g9aY OFf 1n)1HJrY Ss]i!UkJ q4H 1{_GJ Ll G &MlEE:-SDw}XKJJ,*_ ZĿ; ({Ѣ3zLf>SCz?&Xc /_L6'<0p0 q*FO=b^ 1X_x 4\1挑ѦSMD`^J_ q*u($c̘1ܜdeY@7*'YCVRSEK̝ @)_zl_ .E!ub F;ؠNdeɈJ,#6\y9=[e&;/<"."vHvFE ƍ㞠F?ĕ .&wV]|,Y`(<~kԃ8I (R!D72C *[]É84 fYĸ@F\9(YVHB\NN RrHBTg%bԹԅO\,-tcI"XfVQjZtp$KW.K,!Z$6[PWup=B5#'ΈR_ߣ ndǜ59Gkū1*^k"*~DfO=4$8rⴚyC 2`Xg}aR<0o1/$o6 Ԁ VW22ȭ7&W .+ *9%֙^R7A 4 kMv$AZ;;LXoڪA5sGCPxG݈̰`iaY-,0Xb p*R4"!:-n T,Mw"F  |v…ٳ$JG͛7/2xAhŰ0ȬΥtߴI=&gnvNNP>Lxy=ȈZQ7"3c^\,|0HQzIDž05-M>HH <0X݈"9Y};O$}JFXIYȇL**]y eQHYzz(.%Q<*ϷW_17:谆jĉhŬٳ8H!r%f`l歯`P<0XYBđ$#,VtԩjE7 -XJ)4 畕׽3~pmC\|ѝA/$ (eDfXXJf:܇RecKxP:*" y(w%#be9([qmVHn& ̥+H~Wc(F5 BIPZ>=}%/Dup$K+  FWd>5fq4"6d;%bT+8G>rY-'EA DW 1ɑfa2dbHF ʒ<}]'ph΁!%4$",Fd&A`ySOVOQ"r"f<0{vw;/&*Նr@AɎ92H-GW|&-\!/.?duPb<,yKdfŗ " 壨)n}$Xrfm4I.c`7HI< 6W5^c_:_>X_0H.yAbĽ?]7íŠV"i9:#EU~X8T..^Fd&D̡Dww7o=4[\s8֗699~{UjZj`D91 /Cb$J VGちCg|󗲮.z%0{]c$/E0 _R/-?`8PiH+/TU2=\D|%喅p/#2 L>W D`>X|J졎!iY^Pc`J}9P(c% `0YB[UDReb}Df.@ ٧h|@SSSAݮ1fg7K j䆤f(srr!q+QyґM-Jj ?0!V0E<0-ˈmmm<7\hJLs˛6K[_qИ* &SP'TV]N^ި]|L^um^87gd8naEq3gՍƪJ7^=~„V(i2rhii16חxhx%WD;l.&$<:&X]8W@$W):Tٜ  \j@\)*# gx; ?p.sO\U$%rȑ!xaټ6 ֧U5j$u)I:ᚇTXD \\taSO<oW-0XX,{EׯfBieӧ BZ4dggsannn=c GiAq/c}ţU&W5(1YP82PV(G~kHCXV@@^USɛݐvd y`B1k$sDXnNV(ޚngjٳKM#H>y7(yjˀ˯néRK=WIOpYBo*it@ZK8ĘXD$"r'91Lv%rH_^PXn#ڇy"/ / ֤d+,qۏDսa!A U* azϋml(鳚 @NFAb?RH /:B={|%b` Y~44 hw<0P@@^*Bmq’ 0 0/t@F=i@b )Z*i%v*'S%0U7:*K/_  ]Diٛ12y*QPo6DXI@-7od"V& c <--w(&oۦ ( *X nd8EB6?5G=lS"h Kh?"ʋG>n2S,XNT^5GAG۵P3Ҳlbuy%1#!z'QH_JB GmS7@^x* ?tDGiD[oV} !2x-M<ي᪋6b+JO$7 @hԌ'J/Ɲ0p"wi;rY a V[wOx< ?co|wIGzpX &)n wC*P&%+z3{UAFFlUPN\bVO8G!Ygz3<["Ml馓O9bAÝ,mv>< vY79F< #S09{~/YW*:RIDHR N"-5܄"% ɳ @^kf[F$XCH l܉k6w*RHi<ՆX^na$RsWoVĠbD|K#daV 0gJJj~ǎȋxn7XM-[b̊|,{|}dmI $[Y&഻8-~&#TjW b)Sٚb!1 Mm UUUz1;p5:`y`Lz"5C*+tLyGrUq%%uR&'8i"9^bd)jf. bg)vVMG~ %TiƆVͦM/aGMוH=.g Ǚ;,HO>QX`Z,Ll9$0!3W'$ <|3/Z+@roh9%5}qҫgx{gsf-%;WZ޸1yCN`'L}$!| 82 @vG{),gVրI|FzOA:>| qDX& ]UܲRIZڇH;[?يXIUEB8R) }R/JEAF-z(%02 !A1i/N\Ķl4Wd?Ln L`gR=ѴYNjր?D,0A``u*.b?`8V~^%&B{<<*ʡjw%ݯj>bj= Vւ,XRw >h`D$m;4%w3>#}6Hq\Jb;Kx@^ee:L)⋏‹9]]f0ȓ=d/;QAnfhǎX Eb\{SQAI<0 m|/<|v Ֆ4 R  df1"34.WO*X6hj ȏ$4,kkv3G֛~lt:!E!?VtT65X_ꏩN=LeuHWӪ{X'[;|09Kbv[} XuD` %sp U2DnA:ĆϥHpp1 .OUͭ6pdy9ғ8i;{YbȒ,E"7{Jm= I͈Bg?P[gq}@B:@`۶n B &Gl\jH5&r@`,Cӄ DN`*W'A {35.2O#n+ 6nuD Y`"L& ns8!H!ZC܁Ȏ0Y'G `q"%2 *Uŝc% msssҩ=&RGr\s|TF`"oIT.7:&ZU]hKj>_܇C7ZdKˊ&.)fhEvn^^XꁈA4y5k:m3E6`a6===?ܜVX4,į|)՟puNF:㢎Dg_Sy=W"` ހeal>ЃD+VFA.ښK^ NQ_ 1X?>[_,-IJ")8`%I~ku>Ҝ}; 96@v %Aぅ㪢Mn2P:~ݣ \0Y_VJꊣF}%앬276T:#"2jd݌O5TWQK4\kM:5'1l|s{Ly`1؋rWoWIy'9OťDN_hwsLވYgs塒r֗@G{Q"}%̡3x b{n]Q,4{D v\T4.'seJSU?T:JpFOA:׭VIiCXu,܇Y`͏8y57ȭ/9Jȕ\R^yqbuE -5\\;CJ)kKy鸢b{̏re%6G Onc ImXak^nk!>H &9Rb6dfڌ 3ǭq;: R]C?JpClTr_嗍ULGY ͓OJ{*^|Vp?~k+_mm3s|1=裹>xß\]m0Z@^=Up^zœ`Yһ:b-0yof4LLvDNAPFEkd*L`!ʫSI8k)-U&V2XAqy ;{3p 7yn:gBA 紴4r? {9-f!{׍ /(*u޻]Rzqޓ,0Y~8 Lf"-G|K^uCė,!,rF_P@o7À駞ʹ~ 5/=_y3eIMXFǞ,H9^>wX f|љSkkj4 @_L|: ȬHOƆH(Ҥ%)%.ke˗7Aƛ y-oَ/>b֛o->.D_u S傒N?rU) WK`bب(f( oE A 힋'lzٯ8̚=ˌU^H!uo-2atMׯXр͛z}nK.35P#Hq;A'M~lJ`WYN-CͦX)ぉZJ(W.`xB&)Jް'Zk*hkIԇS|?2jh}Ev=DLnB/T0ߡ8Pb]*ʁDfŢ tF3Kt)CK YFJ,ȋ S3&w,/;tF)hZ ҔMinS^G*!16KD^!v-IJeqVgouJ| Z~$($CM:DRR2_4,<@ s%4 1+G͗hu<0OvuEmMXL_ۉSnhhT 2.X pAܐXh,e |PڵA.$u=+P @XaU/GL :J JK-# 8B1L/'lq)_Wg'X+X# =Dɧ~/"C`t&:@ hx`&ObphMØ`Z^СjeLB e lDŽ\+qb!d2qu( "5klJS>DFFX <ѠF=KB  FĊatfwء(+ 93(ſB`(9JJ)d u7"="B|Aih)N-9edpp#cB X)cEP`،p# 5bKK_X#(,t׼A"nDD!Pڍ1dfr7bɤh<002?{_Fu=eٲ%Kyw,%a)P({RRgi/-F{RJzr) [6'w)-YecXmF}f93O<$&tUGI`6E.Ԍ"05'jy"u]]]~Uف8+p& pA _'}$?eaHɵyz~{0lL12zZ| )qӲ#a ãkK=0Hm_-wI'ҪBCѪ#PjK.ׅ?l`=VpДq` ~'0 ʣQbU6{J~m }Bk_zIع˥H`UP8JVEklת`hrꓒ,}UXŢE8k"QRaTV`H?>Zqdl޴m }^F5}iQ z` >mg6Q@3,1t`r]YUz[o秇as ߨS2`:,ͽJ;IaG-XXq1=#?q o×/W~r2~ء;v}UWWs.מwտ7nt(7U/iM%5Hs 0HԪ644(-)imD'h痖bw3a q`o0fƂwp!.ǝQrJo7nlM46uh":RWW犷۶@M#kt>W-܇l蠰ostww;(G":r=(;bًlBZdIcgGtҩfR~(KSPP0gS/w<q[")x&𻦶օmkk 8&//*q_6kg~6>_"d@jb_5-\(U Zt z~QPR&ht.^rm?AhVA]UA݊-`l@Mǟ_lZ!/qDS[p.* $B@c}eUU ;-+>O"RQF6y`)H- ,$8pg+tJ Ø|`r#1%G4L$x P(d+D8 ĔH -%%%^ۏf=2E{B>2L`$2DzzQ%ᾍ)VT_ZyrTPƥJSO$@}ɡw F,xd_ǵt@`ǟpR@_&#  -m;o$0VQDLPPlT ^! ,Չ|Y}3STV;Ӏ0V%2CѦڙ6U LK3VVY#G#V*cU"!&SYՇ:%0p0ϤR@V%2h` LHJD <>1QBKJp@]ImmNd02 ȃCXӫQ]]B_=#CT@H%Hj21c&+*SSOv,PvgOIUrRRܮ}صz{{eiNa L]9O$Qk=µa FJu,}U'/}VEծmmm2;xGV8Ò. +/⋻Lc=6}kf]n]ב"{^{g PB` 52R=,X֤s,9<)nj*Ԭ@-&u;6?47'9X  LOs;@`%%%#z Ω&:bK| W*2hf"3 `pyHՇ9z (Ia={r(L F40K L<f ,m`ݜ4R(?Y}e F-Q^0,zj2dT_R&l"0zKJJ Fg5"!LRXxxHwyDY^U?IH&c Wni9I@VB%ߐ7X'ߘquGyTJ_>L, vP#yFˀJ~N1Mj SmId 'L @܁I` }r؈ ɬ*>$7:EG{;{F6K`zS#YW zyGFG$!&%-!Y V -J`_NF!mEAp@}dp_f YFRX!Ԉ_첌Ѩm\CfkAx@1:-k$6E ~vLJgm }lSGuTR4 _/c()9# LjD`4_;9:}MBD5) ()L^a7[*jDO{ #Yț!5E\x!oDFbd͜VhV[:v@Xh̗D@Y8PX2o޴חg ,woDFb`.( kTBA]m/Cl $~?Hfv1K}悲pWT &_xAOǟx"CA͌Vp† mMއҕ\*tԅZzC^8a߇_s~ 5")ќ5/lc~Ǐ &mu>G<,M%^"IR ݮyyPLH}>^ ~%E*K`$\ lFR62$Iid$}m9}O0 Byw V~Yw <*!?•L`3B],ؘ@pI 7"2b)=ljMJ2#\ lZPfgtVZ0ɅAĕW]Al3FrjTHSFBEH K[YT$m Vfg #_a@p7M\.B_dIB8(gN;/fb,7lgCEEE (օSG+]ńcFki)++R4zG2:3ϳ6g|wLPUj͓nGԃE~Z D阐–R@K9dʌ"A̔:ꌳb bZ-z 6 'F=XL8 7t: lހQńe@)޶ԵגT4~ Ow\Bs(&1az}}!];w Bn;cχmvvJ0_`ΩuLxꚚ ۽]],??ZMM>,s0DVb ,s'{̱vIV;80`_eUU+:hP8lP,Z}%дkWmX\ .涒h C>bP,0}==vA^娻\dI>F'ګn EFŵP]]pzX-2[X7_qcDzO-@Odr:TAAA ºz "5*$av]p,  | Zr P#;;kq pm=p}=Aht+Hi* 2[ؼhBǨhu3ԎwAx@3XX)*o1 骳C# BZO Tyb:ঊu2]ss}:Uqy<G_ESaA8@l$5:;vn OX>wGl[RCxR $aTuS<.]!aڳ"y{&n{.Fލ_5u(VehdWXTl/KCKR1R8?I#c8Z$/d̽iL-LP41ARX1Q2`!], q-[E)Ҏ(K` A&9s#%)1=9$)ir%6q@ GLǀA瀪S1j]QjA1`/㻚zSgjfWXJTm1]z+zvx.IYbD"1`T9|isxgUkZ[[ZOB3' u_O ~1%}QS@B$#٠˴_ U}<\xr#o۶B>k+[MdaRsuiş8#[lxc8 , J+פpz -N }*ԉ8g lND-OA89*A9A|yF I2LX<1` ~KM^6t>䴏ɊR.S" 倐eeXF 1)) -aPmtt4W{6@m)7#P7V;VTcF n ϊ?džvX-L>㪯|yZEe ?w-W 7dK{{@`(->DPСDs zEǯv_ ܆F$3$މt͘xP C  & tP Z8׭[Z`A:oz2OY琎>wwppQHf7|s i߫űpZui ƽ\jU\8\$;cܳژF-bo&eO4 `#򪬪rGb$NjD("1uL­R6Kt?cїcZ_%{7)`p~M7n\sWAL?凮7vb WB;j~m# B<︽^|ǟpB`?iRp. j(0Rz-t]gS4r2r 2yiF$1eZ*|$xA8R HH ɰPAXuua=Cx__oAL,f]u3^O~S9n\Ӷ)Qd6,o}Cb_}ι !]΋^E &)9EbAzn9e=گ:X3el)eϴ\x7Vx3SHJ ,..>Gx-~|ۋT9). ; т\eʤ +1A!TT)@U R hqZl9۷Ovh` l*b'r=jM?O>O`?J_c>~vR43گNe %"QpO3 ;kf">_c?ڣXa'?%[omYvԓO !!!Tc+)]yg+*oTBj,d51iM|bRabߴv`np yyQMGV\{UK]'tR`5ѯKRotGY/dD- 2ϒ@ 1oLͲ,XillHU3ys%gfC)c Br 9X q<%d"kt^VW?&0F\zZ'/n8-gQF $0FC`Iy͵&Um`02 %Õ&PGӞRO1vŷr.<ou=!Ρ?nC[r ^ЌDn#YѪa,GN9<f0 8B یJ)[Zs{O-0(J E?!@jƈo ɴ;8yCy2KЈuvxAK'Gn''(s"1³bRh^G#>r9 VMc0|b_E|~eDHY@:ݧTJwoy&@JSKrt@wҩxh$+zAfgu&$ψC}2=PI)T7 L_WJU`˸$)e|=;#>JJAQN}۫R[W`;Uq=2N\ϻ^U +ѐ[Jq2TOP^7ҳ?ѹ^)icBcRS☲.`y+`~ )ѷL$f`UN4T[CgO<3n/V**J%Irrsd3*!G)**QfV8KqR__5eTjyQdΝF6 a)Q|A#Ֆ+*+9%P ).9 ؋J~n_#<‚y>aÒ) =p?B&0}*>GMS $ +$#HWsyAJK-R,(]s;4B)Y5u * !rRKT6YJ 5,3A)r* :KUnUrĀGa~{hR"Xw2ְd"][/0 XORX[?4Pf_~(I HHn12Hp ^4Yn=ijHoUHuXca?DUb45c)ଡކߓREaj:$x~;쟔\IzY ߥ`=lOPzCXoH P}6Go-JR‹Ȍ6!AJotٕg[#@<=LHR X1:_R%0l(Z%"sRS;d@RoSW.)-BY5YAB~gھmۜ#Lua5M\갔*vUv$-[A.Rvrt ~`0iWx/?Fj$}E>G;#4 JD< i ~Q #)oPKRUإR3BC[N3L` FB@1:'dEOl6i(zu[L`,G``0 j`0UFrKT5\?%&)b۩ `-?t=-^7O ,)qJYprwPswǏ `̌k0`d}/2-+Oo|llTY@KZu{F1L`aA׮[Ώ8U$63SbuD;[,d`qW_yły:V7iҸ<  1}⚴x]8#ܰ`v`J` LS{\%/_z! <[ ͷ>zzcq_eLY(3]L3O?m[u_z.* 6ByiƍMP`>_.a0ҏ|~UWynj陬^-_j'&,m޴\Req~C[^XJ'O<Mb nڎRe!N9u+V?H;nwH 5uu }=]q (M$Z d_0hb^#3[&x_'!% nw:nY2h cRKXȏQEׇ7M#σklՅ8czÀoR|rR 뾻'oܺel|w~@o..E\/څ>. zK`);x|gOJ e˖͍oM䧵dTO}2m v[7cyg t7SqCaaBcu=.~N:t^A t1й0YaX"jkk%R"B$`_qÑQ T(p^KyCCFl׍CnKsslAS,{ۿz:@:~o [okCd>Sj^]r%r!F| x:l/>/wAt&$֦@@`^H! $Q!+!a +9!)6Ir l@sTDVPPЯ&2!twGu(5AEnsmGi/[ԇ ټi7߲|Vur`T\limm͏Xz@zsr`0DcH]xb_dy! x C넔Ql2Bjh3C͇Hv *R" SssStv "u)5F^maѮU} Ѯ@׆kEHJ}MM+WjDGHF&3̏yYgyo9d!XZ/ zmx1W_u13f&T{{4H 8T3b!. h Lvނ AM "ٸ@z !!uATD_ aXrT#zb'6u@R=~/fZLP zOFT' HhUanEٟ=Я\Q.E^x_pݨA9m7<`㝷^ {TyE =8lVy,މ_yE _fdqk k ː2QRM:!)lB}7+E)L,pHZp$V! Ym6/TА6!A@H [w755Gf uy hMkEnA UnR+W bNU9Hz_8p3NWjj7?W 5 ,^hW\{ޥeh}6| lJtji;TQ#ۓZuۑ JAq (43kl>wgg-I@OL@:ظ dkD-V?? \"S:ppFGPU7jhRZHò z#בFv.mHx_[/jRL` ^) l~S$0;} uh>"kMڛ)n ExUWWDKׇel Y# m~ NHWh=IzCn= 0a=ԠG/XаtiH糖qĬ?ny},{o#3ǁEg$HB^"@=kZ%A:P"&#:fמ=jT@k޻aP0L` ݌A.dsbIun^`c0ت&zÃ{pgĤY=:2Jɥq\p5 k>ɵL`xG NI~P^w#ulv=VQ:eCHTllfeZ_nD,\2D܆C/Fr':~koMķ/6Le7qQGsF&u9[ mq/2O o@ /;.߶DJ[KWc.7O=r )e ^@VdL޻Kh YQ># aD]!ѧ:٧7^h|.ȴ9"ZP.K3}33$&둁c>;?>mDHv?O[A6~ 23 u˯@kkT2\yQ%cU%[[Ⱦf:ӈľ ;xdH #/qWS(XꁜfP )5U_iˁ6;vL&gE.ę΅}`.#_5#> 5Wet#L3 G*}8|A¶/"+T8: R4kkk]}ᇦJr $eGde2mP60#~` lPể**B.MB?Ԇ4Ƿ u"ºG~AǠLT}MCM7eǟpBmqN}{@ǛLnE{?ᜲa7^Cݎ;Lm[>q}d9{zs.3GAAmٲP|,MMyaq+W"nF8E[ ]^z{s챲{uŎ͗_qyʡ #?sibu|wښ֯/}!1ևB/ *kz26q!|ޮ{bJJKYlvup/ ]"ZIHXQڕ*H:`љd o۶8|wg[iK,vWR!D͈f33e˯z]L`HK42َBd' U:\(ej,C@G].;lmB@p__RCrb$ 6auvĺ|TRGٞ1`cd5IE[d ٵs(jTLeYPK΅~߮N| $* A .K;z.q|+K` #Qd6OHl#$+5b` dkH8<4D, Q5s1 &0FZFbTHBLAt HBX^^! { <`c؊fm^u2lWH0<[;P Rc '﹨ `c0DG .((UԴ8;DcLmunjjSHjz[CCCF8qNz.HBz` \В`0 `c0  `0 &0`01 `0 `0 `c0  `0 &0`01 `0 `0 `c0  `0L` `01 `0 `02_6IENDB`ipyparallel-7.1.0/docs/source/reference/figs/simpledag.pdf000066400000000000000000000120531412533144600236170ustar00rootroot00000000000000%PDF-1.4 % 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 8 0 obj << /XObject 7 0 R /Pattern 5 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /ExtGState 4 0 R /Shading 6 0 R /Font 3 0 R >> endobj 10 0 obj << /Contents 9 0 R /Type /Page /Resources 8 0 R /Parent 2 0 R /MediaBox [ 0 0 576 432 ] >> endobj 9 0 obj << /Filter /FlateDecode /Length 11 0 R >> stream xWn0 +tl/.Ez\r @?mSI߯$;$e}Q9$õu XR/3x`eۼ~ow5<׽Amz/XmVpIG{ $ؿ`]Aޭ .cw+.}'x\?x{V(ₘJ()*RI#Rq~AJTBD ".JXr 6KZ/ K |Ƹ'"-)!IJ]ɞa+:kTpgf2{dǣ JZy@a53@H"3G WPTe[8e&aeAv ^s =Tiǭ*gk.H NqQ ұLiaҥSҫ)'¤46O ESUìNgH Kc^Mm  gytѥD欘aX rZ8ɽ[)舊:j2D9L*(-|'U" MI4o6PtTu@\kMY-)Q9Cn?rmG-aE9ը tfBu7)%j!y^IŒ_ji߫Zr.WRAi#mIK9i<&-5|Ғ3RNZ},:kI2Dҥ ܞi ܄3zOI 60!p naBZKV.2եDvv{O_uh/?lTJ"m1|l?_-y_WKܝݡsgR}L7z9m= `W5>="Z endstream endobj 11 0 obj 865 endobj 16 0 obj << /Filter /FlateDecode /Length 90 >> stream xMA "OPDtz_NE5jK02kP)U0\ 2IL{qIqzz"X endstream endobj 17 0 obj << /Filter /FlateDecode /Length 210 >> stream x5P C1g dVukm;aBXȔy)K>:L." u%ʚ +`p&^7`i5tႦ.B%|u{OxjrvC` jMX> stream x5R9@ } ] v͜~߆_ CVie!U-.Im W%ڥ Pt,6˯JH+kLwIi"Eo7o}=@.^ AS(i|Ъc(ew 4<3}(~_K&(? _osџa`Ś}@*z`yT endstream endobj 19 0 obj << /Filter /FlateDecode /Length 248 >> stream x-Q9AzBsˑ C :-qPO+Uwu9HTM]vf5,?c 7zqxLu5{kOfP2+qSușO \ ȹeƌ#M!RH&3AQ~#aU#j \Ks4;<9GW +ET<pC7ҹ^s0XM7/=[ endstream endobj 20 0 obj << /Filter /FlateDecode /Length 80 >> stream xE 0D{`~&f( JpO{:2Sa ,S`5FR죰n_uzS*Ovvq= endstream endobj 14 0 obj << /FontDescriptor 13 0 R /Name /BitstreamVeraSans-Roman /FontMatrix [ 0.001 0 0 0.001 0 0 ] /BaseFont /BitstreamVeraSans-Roman /Widths 12 0 R /Subtype /Type3 /CharProcs 15 0 R /Type /Font /FirstChar 0 /FontBBox [ -184 -236 1288 929 ] /Encoding << /Differences [ 48 /zero /one /two /three /four ] /Type /Encoding >> /LastChar 255 >> endobj 13 0 obj << /Descent -236 /FontBBox [ -184 -236 1288 929 ] /StemV 0 /Flags 32 /XHeight 547 /Type /FontDescriptor /FontName /BitstreamVeraSans-Roman /MaxWidth 1342 /CapHeight 730 /ItalicAngle 0 /Ascent 929 >> endobj 12 0 obj [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 318 401 460 838 636 950 780 275 390 390 500 838 318 361 318 337 636 636 636 636 636 636 636 636 636 636 337 337 838 838 838 531 1000 684 686 698 770 632 575 775 752 295 295 656 557 863 748 787 603 787 695 635 611 732 684 989 685 611 685 390 337 390 838 500 500 613 635 550 635 615 352 635 634 278 278 579 278 974 634 612 635 635 411 521 392 634 592 818 592 592 525 636 337 636 838 600 636 600 318 636 518 1000 500 500 500 1342 635 400 1070 600 685 600 600 318 318 518 518 590 500 1000 500 1000 521 400 1023 600 525 611 636 401 636 636 636 636 337 500 500 1000 471 612 838 361 1000 500 500 838 401 401 500 636 636 318 500 401 471 612 969 969 969 531 684 684 684 684 684 684 974 698 632 632 632 632 295 295 295 295 775 748 787 787 787 787 787 838 787 732 732 732 732 611 605 630 613 613 613 613 613 613 982 550 615 615 615 615 278 278 278 278 612 634 612 612 612 612 612 838 612 634 634 634 634 592 635 592 ] endobj 15 0 obj << /four 16 0 R /zero 17 0 R /one 20 0 R /three 18 0 R /two 19 0 R >> endobj 3 0 obj << /F1 14 0 R >> endobj 4 0 obj << >> endobj 5 0 obj << >> endobj 6 0 obj << >> endobj 7 0 obj << >> endobj 2 0 obj << /Count 1 /Kids [ 10 0 R ] /Type /Pages >> endobj 21 0 obj << /CreationDate (D:20110217120817-07'00') /Producer (matplotlib pdf backend r8851) /Creator (matplotlib 1.1.0svn, https://matplotlib.sf.net) >> endobj xref 0 22 0000000000 65535 f 0000000016 00000 n 0000004423 00000 n 0000004307 00000 n 0000004339 00000 n 0000004360 00000 n 0000004381 00000 n 0000004402 00000 n 0000000065 00000 n 0000000315 00000 n 0000000208 00000 n 0000001255 00000 n 0000003168 00000 n 0000002953 00000 n 0000002604 00000 n 0000004221 00000 n 0000001275 00000 n 0000001437 00000 n 0000001720 00000 n 0000002131 00000 n 0000002452 00000 n 0000004483 00000 n trailer << /Info 21 0 R /Root 1 0 R /Size 22 >> startxref 4643 %%EOF ipyparallel-7.1.0/docs/source/reference/figs/simpledag.png000066400000000000000000000245341412533144600236410ustar00rootroot00000000000000PNG  IHDR XvpsBIT|d pHYsaa?i IDATxý<]' hb U)s:wgGB6')b6@PӔQN9IOR€bka)PaUW)l@;u]K$E|_v3$`>z A  @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F\t)Y]]Mܹ3󙛛3Q_,..^r֮\^>|8vJ 3`PE$ŋəgsv6]ܕWA7^/swTuT~4*'O˿[\Wrn$+Izy#dv~4*̋/G;U'@J|{9&|}̄@Gy PJΜ=n1s%IH6\%[$_Y[˙g2kzǍ~7s[g? @q9;8n5ɞ$=$ٛpmH,..yŰq=%x1}^wםw殥<ߓI8$oygw6 :~J~O$K.eiy9wqۇ<$K˹|@lg?^L?d|w1](jyy9kWn3HNlӷ$ﯭ… _(2~Id~L_$I˫Kג<:~Fݏ>`e?K$!oJvIo%9䭪/ @Q;wL ɰO\H}x׳%=v?KH_wu5~LEϧ1񧓬'_8{I>gϞ=cu0~0?Əpk>^Ƴ'-a?KEea~>oqܱqo$Yώ;Ƹ7`\I~-a?K?x0z=$ sxCnr'yacG6~_Կ~=x9V=a?O>|8yet6SlǟKsݟ⶯$s.v?nf&&t1}~:P{o^=rۄ^~ϷϜh>Tŋ<3sf;ٽ{؟@Ls?Nrjn.痖y P^{-o$JZ@Ls?ɧS'Nȑ#~:$$Wcyf`33Сptfg}~q<`x O}s]=#I9'NlɴС<(aǵϳɖ?7Wy6o|߿q"٦OӧdKx?~ !B`(m0mF'@-ue"`{lMa<6!@%@!BIVDF%@1$p=v81~]:xl~0e<#BK@!@&A@!@W&E@!@&I@G!@[&M@2D6i rƣ,`Z~0MZG?-xLVif0 `z>ܹs}xs"۷/w*"3 bǎ˹so߾f<ʺ}ܹs9vXW94ǫ`;G's=$G>oϧ?`?h5a<F@ !BM$@bƃI!= P!$A `DTx0M"~ 3 B}m!@ AI"~& 1TA@F@ƃ*h.A 2AhA[ "AhA A?A A/A0AAW AAAkLDT~EdA AFf<g?`D)C2t!L([0 B`;ܜM)ópknxGؚ  &BlM lMGk0<W !t :xD]d?`{f<`rD]b?`e<`D]`?`<N20="60>B>B B(G&&G! t~ Zx@DMd?`:f<>DMb?`ze<~DM`?`dz1[g'O Gb? ]x1{99s?\/-e>;U@ )fs~m-M%ga|̙ *ͼkJVVVr|uohv*d k>o ylWQda~>;vͨ1TELjlq?MgL>6$z܁^.51~$K$o_[a&b?*ESݻwwo&y3ݻwOTŋ<3s?\/-2EwqG|<<+Ifkk9WkL~?ɧ@e<2eW^~971J_ 9~x{1 @8q"G^oCә=H}lvv6ϟ:IoG^6^v@>#Gĉc^)U1>ҩWqCy)T#P~?/>~:eog~OJy߿q"nz~O?NN-(A2;;G}4痖43Ozy0nW?{k&d/da;9~-C4Vq;ޏKKyG=ԂTdee%g.d/X|>|>PyO@3>~ԁ˗/… Y]]Mܹ3{Ɏ;ny|>og?&@L4p{1 h ti8,:"i Po`iPOHˈz -$BA||i)P-psD@5 !e !e !%>#@:DL0Y`4Dd !#@:Ll>q"`4`<0$0>B0#@,G `7%B!@%t¦D5`[!@W Em'> Mm%> Dm#> Lm!> lN|TCm[Eȏ~W0 AA컈w󝊮`8gsܹ&>3 gBۗsرc^;se߾}~M|L_ꋠ>oϧ?aDPS'B !Bz #B@!@)ʼn`@} *!Bi&@&M|ԟR" ʉ`\9 B"@ J|4VD0,L!V@s jI">MP["h>B'BF!]]!B{@E@wv 4%@h$%>MX"G|DBh>V!\[!By@ZE@sn %@h%%>MZ"G| @h5!>H Bz:A@u t7 tr7#@'>B'f%B`[ t Cy"'>`!0:\C!@"&>.7!B p ">J|0.!SI  0B&ID]$>4#!t`Hi "6Lm!`A&IRL% BJ 0A"&TA@|PS B3@LU 0E":ԁ)!ԁ. BN"Bn$B(I|PG ! >+!LTD0  P!$@@D >h 5 BIԄa;M#@FD4! C|TjH@ )͈N@%>h5'BH!@DHD@Cn!">h# #BA|VH4i'@ h0.. p"]!@DHD@Kf2IDATt!">"-#BA|UZHԛ'@ h1R/t "'@CDHY>J@Lj2ܜ!%>t a"dlM@lj010|HF|p2#@!})rs#@[!0>lJl0R#D|`(]%@u-BLFҕ0Y#D|`[!Kֶ0}K["D|@[#D|@9F01M `!!@{">ZE `!ASWutR$ٹsg3777ԋ6BVVV_{-KYr?zYsڵP?3`PEr0xb#9slv+Wrw|1?Hf7eu}=8qx-V{IѣG˿;ݗ|y~0ȿ$gx9oK򕵵9{6+++_001(nqq1;ggsIN'y<@jx gg8Υ%X@qwygZZss6ɽوkߠ?IqʃI޺|q. πE]t)K˹{^MsI`#8}ݝdiy9/_B @QYr%wmqR|AΫ}{Tr…$$p5_> s$8r_V~x} @Qzys&YN~_%X={x,-/[7|T6~1ᧇ7,gǎ#_'0(nyO69$7$\M򥙙ٵk L*{ZnK3>;0Tŋٻ._ $r~i)wqxP;#O>TI $HSO!πY__ yWL?t(/>Yc@efggSС;p {>APk/_΅ $ٹsgٓ;vT|evoB @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F#@bP @1(F:.&IENDB`ipyparallel-7.1.0/docs/source/reference/index.md000066400000000000000000000001721412533144600216570ustar00rootroot00000000000000# Reference ```{toctree} --- maxdepth: 2 --- mpi db security dag_dependencies details messages connections launchers ``` ipyparallel-7.1.0/docs/source/reference/launchers.md000066400000000000000000000141401412533144600225340ustar00rootroot00000000000000# Launchers The `Launcher` is the basic abstraction in IPython Parallel for starting and stopping processes. A Launcher has two primary methods: {meth}`~.BaseLauncher.start` and {meth}`~.BaseLauncher.stop`, which should be `async def` coroutines. There are two basic kinds of Launcher: {class}`~.ControllerLauncher` and {class}`~.EngineLauncher`. A ControllerLauncher should launch `ipcontroller` somewhere, and an EngineLauncher should start `n` engines somewhere. Shared configuration, principally `profile_dir` and `cluster_id` are typically used to locate the connection files necessary for these two communicate, though explicit paths can be added to arguments. Launchers are used through the {class}`~.Cluster` API, which manages one ControllerLauncher and zero to many EngineLaunchers, each representing a set of engines. Launchers are registered via entry points ([more below](#entrypoints)), and can be selected via short lowercase string naming the kind of launcher, e.g. 'mpi' or 'local': ```python import ipyparallel as ipp c = ipp.Cluster(engine_launcher_class="mpi") ``` For the most part, Launchers are not interacted-with directly, but can be _configured_. If you generate a config file with: ```bash ipython profile create --parallel ``` you can check out the resulting `ipcluster_config.py`, which includes configuration options for all available Launcher classes. You can also check `ipcluster start --help-all` to see them on the command-line. ## Debugging launchers If a launcher isn't doing what you want, the first thing to do is probably start your Cluster with `log_level=logging.DEBUG`. You can also access the Launcher(s) on the Cluster object and call {meth}`~.BaseLauncher.get_output` to retrieve the output from the process. ## Writing your own Launcher(s) If you want to write your own launcher, the best place to start is to look at the Launcher classes that ship with IPython Parallel. There are three key methods to implement: - [`start()`](#writing-start) - [`stop()`](#writing-stop) - [`from_dict()`](#writing-from-dict) ### Writing start A start method on a launcher should do the following: 1. request the process(es) to be started 2. start monitoring to notice when the process exits, such that {meth}`.notify_stop` will be called when the process exits. The command to launch should be the `self.args` list, inherited from the base class. The default for the `LocalProcessLauncher` ```{literalinclude} ../../../ipyparallel/cluster/launcher.py :pyobject: LocalProcessLauncher.start ``` _ControllerLauncher.start_ is always called with no arguments, whereas `EngineLauncher.start` is called with `n`, which is an integer or None. If `n` is an integer, this many engines should be started. If `n` is None, a 'default' number should be used, e.g. the number of CPUs on a host. ### Writing stop A stop method should request that the process(es) stop, and return only after everything is stopped and cleaned up. Exactly how to collect these resources will depend greatly on how the resources were requested in `start`. ### Serializing Launchers Launchers are serialized to disk using JSON, via the `.to_dict()` method. The default `.to_dict()` method should rarely need to be overridden. To declare a property of your launcher as one that should be included in serialization, register it as a [traitlet][] with `to_dict=True`. For example: ```python from traitlets import Integer from ipyparallel.cluster.launcher import EngineLauncher class MyLauncher(EngineLauncher): pid = Integer( help="The pid of the process", ).tag(to_dict=True) ``` [traitlet]: https://traitlets.readthedocs.io This `.tag(to_dict=True)` ensures that the `.pid` property will be persisted to disk, and reloaded in the default `.from_dict` implementation. Typically, these are populated in `.start()`: ```python def start(self): process = start_process(self.args, ...) self.pid = process.pid ``` Mark whatever properties are required to reconstruct your object from disk with this metadata. #### writing from_dict {meth}`~.BaseLauncher.from_dict` should be a class method which returns an instance of your Launcher class, loaded from dict. Most `from_dict` methods will look similar to this: ```{literalinclude} ../../../ipyparallel/cluster/launcher.py :pyobject: LocalProcessLauncher.from_dict ``` where serializable-state is loaded first, then 'live' objects are loaded from that. As in the default LocalProcessLauncher: ```{literalinclude} ../../../ipyparallel/cluster/launcher.py :pyobject: LocalProcessLauncher._reconstruct_process ``` The local process case is the simplest, where the main thing that needs serialization is the PID of the process. If reconstruction of the object fails because the resource is no longer running (e.g. check for the PID and it's not there, or a VM / batch job are gone), the {exc}`.NotRunning` exception should be raised. This tells the Cluster that the object is gone and should be removed (handled the same as if it had stopped while we are watching). Raising other unhandled errors will be assumed to be a bug in the Launcher, and not result in removing the resource from cluster state. ### Additional methods Some useful additional methods to implement, if the base class implementations do not work for you: - {meth}`~.ControllerLauncher.get_connection_info` - {meth}`~.BaseLauncher.get_output` TODO: write more docs on these =(entrypoints) ## Registering your Launcher via entrypoints Once you have defined your launcher, you can 'register' it for discovery via entrypoints. In your setup.py: ```python setup( ... entry_points={ 'ipyparallel.controller_launchers': [ 'mine = mypackage:MyControllerLauncher', ], 'ipyparallel.engine_launchers': [ 'mine = mypackage:MyEngineSetLauncher', ], }, ) ``` This allows clusters created to use the shortcut: ```python Cluster(enginge_launcher_class="mine") ``` instead of the full import string ``` Cluster(enginge_launcher_class="mypackage.MyEngineSetLauncher") ``` though the long form will always still work. ## Launcher API reference ```{eval-rst} .. automodule:: ipyparallel.cluster.launcher ``` ipyparallel-7.1.0/docs/source/reference/messages.md000066400000000000000000000334531412533144600223670ustar00rootroot00000000000000(parallel-messages)= # Messaging for Parallel Computing This is an extension of the {ref}`messaging ` doc. Diagrams of the connections can be found in the {ref}`parallel connections ` doc. ZMQ messaging is also used in the parallel computing IPython system. All messages to/from kernels remain the same as the single kernel model, and are forwarded through a ZMQ Queue device. The controller receives all messages and replies in these channels, and saves results for future use. ## The Controller The controller is the central collection of processes in the IPython parallel computing model. It has two major components: > - The Hub > - A collection of Schedulers ## The Hub The Hub is the central process for monitoring the state of the engines, and all task requests and results. It has no role in execution and does no relay of messages, so large blocking requests or database actions in the Hub do not have the ability to impede job submission and results. ### Registration (`ROUTER`) The first function of the Hub is to facilitate and monitor connections of clients and engines. Both client and engine registration are handled by the same socket, so only one ip/port pair is needed to connect any number of connections and clients. Engines register with the `zmq.IDENTITY` of their two `DEALER` sockets, one for the queue, which receives execute requests, and one for the heartbeat, which is used to monitor the survival of the Engine process. Message type: `registration_request`: ``` content = { 'uuid' : 'abcd-1234-...', # the zmq.IDENTITY of the engine's sockets } ``` ```{note} these are always the same, at least for now. ``` The Controller replies to an Engine's registration request with the engine's integer ID, and all the remaining connection information for connecting the heartbeat process, and kernel queue socket(s). The message status will be an error if the Engine requests IDs that already in use. Message type: `registration_reply`: ``` content = { 'status' : 'ok', # or 'error' # if ok: 'id' : 0, # int, the engine id } ``` Clients use the same socket as engines to start their connections. Connection requests from clients need no information: Message type: `connection_request`: ``` content = {} ``` The reply to a Client registration request contains the connection information for the multiplexer and load balanced queues, as well as the address for direct hub queries. If any of these addresses is `None`, that functionality is not available. Message type: `connection_reply`: ``` content = { 'status' : 'ok', # or 'error' } ``` ### Heartbeat The hub uses a heartbeat system to monitor engines, and track when they become unresponsive. As described in {ref}`messaging `, and shown in {ref}`connections `. ### Notification (`PUB`) The hub publishes all engine registration/unregistration events on a `PUB` socket. This allows clients to have up-to-date engine ID sets without polling. Registration notifications contain both the integer engine ID and the queue ID, which is necessary for sending messages via the Multiplexer Queue and Control Queues. Message type: `registration_notification`: ``` content = { 'id' : 0, # engine ID that has been registered 'uuid' : 'engine_id' # the IDENT for the engine's sockets } ``` Message type : `unregistration_notification`: ``` content = { 'id' : 0 # engine ID that has been unregistered 'uuid' : 'engine_id' # the IDENT for the engine's sockets } ``` ### Client Queries (`ROUTER`) The hub monitors and logs all queue traffic, so that clients can retrieve past results or monitor pending tasks. This information may reside in-memory on the Hub, or on disk in a database (SQLite and MongoDB are currently supported). These requests are handled by the same socket as registration. {func}`queue_request` requests can specify multiple engines to query via the `targets` element. A verbose flag can be passed, to determine whether the result should be the list of `msg_ids` in the queue or the length of each list. Message type: `queue_request`: ``` content = { 'verbose' : True, # whether return should be lists themselves or the lengths thereof 'targets' : [0,3,1] # list of ints } ``` The content of a reply to a {func}`queue_request` request is a dict, keyed by the engine IDs. Note that they will be the string representation of the integer keys, since JSON cannot handle number keys. The three keys of each dict are: ``` 'completed' : messages submitted via any queue that ran on the engine 'queue' : jobs submitted via MUX queue, whose results have not been received 'tasks' : tasks that are known to have been submitted to the engine, but have not completed. Note that with the pure zmq scheduler, this will always be 0/[]. ``` Message type: `queue_reply`: ``` content = { 'status' : 'ok', # or 'error' # if verbose=False: '0' : {'completed' : 1, 'queue' : 7, 'tasks' : 0}, # if verbose=True: '1' : {'completed' : ['abcd-...','1234-...'], 'queue' : ['58008-'], 'tasks' : []}, } ``` Clients can request individual results directly from the hub. This is primarily for gathering results of executions not submitted by the requesting client, as the client will have all its own results already. Requests are made by msg_id, and can contain one or more msg_id. An additional boolean key 'statusonly' can be used to not request the results, but poll the status of the jobs instead. Message type: `result_request`: ``` content = { 'msg_ids' : ['uuid','...'], # list of strs 'targets' : [1,2,3], # list of int ids or uuids 'statusonly' : False, # bool } ``` The {func}`result_request` reply contains the content objects of the actual execution reply messages. If `statusonly=True`, then there will be only the 'pending' and 'completed' lists. Message type: `result_reply`: ``` content = { 'status' : 'ok', # else error # if ok: 'acbd-...' : msg, # the content dict is keyed by msg_ids, # values are the result messages # there will be none of these if `statusonly=True` 'pending' : ['msg_id','...'], # msg_ids still pending 'completed' : ['msg_id','...'], # list of completed msg_ids } buffers = ['bufs','...'] # the buffers that contained the results of the objects. # this will be empty if no messages are complete, or if # statusonly is True. ``` For memory management purposes, Clients can also instruct the hub to forget the results of messages. This can be done by message ID or engine ID. Individual messages are dropped by msg_id, and all messages completed on an engine are dropped by engine ID. This may no longer be necessary with the mongodb-based message logging backend. If the msg_ids element is the string `'all'` instead of a list, then all completed results are forgotten. Message type: `purge_request`: ``` content = { 'msg_ids' : ['id1', 'id2',...], # list of msg_ids or 'all' 'engine_ids' : [0,2,4] # list of engine IDs } ``` The reply to a purge request is the status 'ok' if the request succeeded, or an explanation of why it failed, such as requesting the purge of a nonexistent or pending message. Message type: `purge_reply`: ``` content = { 'status' : 'ok', # or 'error' } ``` ## Schedulers There are three basic schedulers: > - Task Scheduler > - MUX Scheduler > - Control Scheduler The MUX and Control schedulers are simple MonitoredQueue ØMQ devices, with `ROUTER` sockets on either side. This allows the queue to relay individual messages to particular targets via `zmq.IDENTITY` routing. The Task scheduler may be a MonitoredQueue ØMQ device, in which case the client-facing socket is `ROUTER`, and the engine-facing socket is `DEALER`. The result of this is that client-submitted messages are load-balanced via the `DEALER` socket, but the engine's replies to each message go to the requesting client. Raw `DEALER` scheduling is quite primitive, and doesn't allow message introspection, so there are also Python Schedulers that can be used. These Schedulers behave in much the same way as a MonitoredQueue does from the outside, but have rich internal logic to determine destinations, as well as handle dependency graphs Their sockets are always `ROUTER` on both sides. The Python task schedulers have an additional message type, which informs the Hub of the destination of a task as soon as that destination is known. Message type: `task_destination`: ``` content = { 'msg_id' : 'abcd-1234-...', # the msg's uuid 'engine_id' : '1234-abcd-...', # the destination engine's zmq.IDENTITY } ``` ### {func}`apply` In terms of message classes, the MUX scheduler and Task scheduler relay the exact same message types. Their only difference lies in how the destination is selected. The Namespace model suggests that execution be able to use the model: ``` ns.apply(f, *args, **kwargs) ``` which takes `f`, a function in the user's namespace, and executes `f(*args, **kwargs)` on a remote engine, returning the result (or, for non-blocking, information facilitating later retrieval of the result). This model, unlike the execute message which uses code as a string, must be able to send arbitrary (pickleable) Python objects. And ideally, copy as little data as we can. The `buffers` property of a Message was introduced for this purpose. Utility method {func}`build_apply_message` in {mod}`IPython.kernel.zmq.serialize` wraps a function signature and builds a sendable buffer format for minimal data copying (exactly zero copies of numpy array data or buffers or large strings). Message type: `apply_request`: ``` metadata = { 'after' : ['msg_id',...], # list of msg_ids or output of Dependency.as_dict() 'follow' : ['msg_id',...], # list of msg_ids or output of Dependency.as_dict() } content = {} buffers = ['...'] # at least 3 in length # as built by build_apply_message(f,args,kwargs) ``` after/follow represent task dependencies. 'after' corresponds to a time dependency. The request will not arrive at an engine until the 'after' dependency tasks have completed. 'follow' corresponds to a location dependency. The task will be submitted to the same engine as these msg_ids (see {class}`Dependency` docs for details). Message type: `apply_reply`: ``` content = { 'status' : 'ok' # 'ok' or 'error' # other error info here, as in other messages } buffers = ['...'] # either 1 or 2 in length # a serialization of the return value of f(*args,**kwargs) # only populated if status is 'ok' ``` All engine execution and data movement is performed via apply messages. ### Raw Data Publication `display_data` lets you publish _representations_ of data, such as images and html. This `data_pub` message lets you publish _actual raw data_, sent via message buffers. data_pub messages are constructed via the {func}`ipyparallel.datapub.publish_data` function: ```python from ipyparallel.datapub import publish_data ns = dict(x=my_array) publish_data(ns) ``` Message type: `data_pub`: ``` content = { # the keys of the data dict, after it has been unserialized 'keys' : ['a', 'b'] } # the namespace dict will be serialized in the message buffers, # which will have a length of at least one buffers = [b'pdict', ...] ``` The interpretation of a sequence of data_pub messages for a given parent request should be to update a single namespace with subsequent results. ## Control Messages Messages that interact with the engines, but are not meant to execute code, are submitted via the Control queue. These messages have high priority, and are thus received and handled before any execution requests. Clients may want to clear the namespace on the engine. There are no arguments nor information involved in this request, so the content is empty. Message type: `clear_request`: ``` content = {} ``` Message type: `clear_reply`: ``` content = { 'status' : 'ok' # 'ok' or 'error' # other error info here, as in other messages } ``` Clients may want to abort tasks that have not yet run. This can by done by message id, or all enqueued messages can be aborted if None is specified. Message type: `abort_request`: ``` content = { 'msg_ids' : ['1234-...', '...'] # list of msg_ids or None } ``` Message type: `abort_reply`: ``` content = { 'status' : 'ok' # 'ok' or 'error' # other error info here, as in other messages } ``` The last action a client may want to do is shutdown the kernel. If a kernel receives a shutdown request, then it aborts all queued messages, replies to the request, and exits. Message type: `shutdown_request`: ``` content = {} ``` Message type: `shutdown_reply`: ``` content = { 'status' : 'ok' # 'ok' or 'error' # other error info here, as in other messages } ``` ## Implementation There are a few differences in implementation between the `StreamSession` object used in the newparallel branch and the `Session` object, the main one being that messages are sent in parts, rather than as a single serialized object. `StreamSession` objects also take pack/unpack functions, which are to be used when serializing/deserializing objects. These can be any functions that translate to/from formats that ZMQ sockets can send (buffers,bytes, etc.). ### Split Sends Previously, messages were bundled as a single json object and one call to {func}`socket.send_json`. Since the hub inspects all messages, and doesn't need to see the content of the messages, which can be large, messages are now serialized and sent in pieces. All messages are sent in at least 4 parts: the header, the parent header, the metadata and the content. This allows the controller to unpack and inspect the (always small) header, without spending time unpacking the content unless the message is bound for the controller. Buffers are added on to the end of the message, and can be any objects that present the buffer interface. ipyparallel-7.1.0/docs/source/reference/mpi.md000066400000000000000000000104331412533144600213360ustar00rootroot00000000000000(parallel-mpi)= # Using MPI with IPython Often, a parallel algorithm will require moving data between the engines. One way of accomplishing this is by doing a pull and then a push using the direct view. However, this will be slow as all the data has to go through the controller to the client and then back through the controller, to its final destination. A much better way of moving data between engines is to use a message passing library, such as the Message Passing Interface ([MPI][]). IPython's parallel computing architecture has been designed from the ground up to integrate with MPI. This document describes how to use MPI with IPython. ## Additional installation requirements If you want to use MPI with IPython, you will need to install: - A standard MPI implementation such as [OpenMPI][] or MPICH. - The [mpi4py][] package. ```{note} The mpi4py package is not a strict requirement. However, you need to have _some_ way of calling MPI from Python. You also need some way of making sure that {func}`MPI_Init` is called when the IPython engines start up. There are a number of ways of doing this and a good number of associated subtleties. We highly recommend using mpi4py as it takes care of most of these problems. If you want to do something different, let us know and we can help you get started. ``` ## Starting the engines with MPI enabled To use code that calls MPI, there are typically two things that MPI requires. 1. The process that wants to call MPI must be started using {command}`mpiexec` or a batch system (like PBS) that has MPI support. 2. Once the process starts, it must call {func}`MPI_Init`. There are a couple of ways that you can start the IPython engines and get these things to happen. ### Automatic starting using {command}`mpiexec` and {command}`ipcluster` The easiest approach is to use the `MPI` Launchers in {command}`ipcluster`, which will first start a controller and then a set of engines using {command}`mpiexec`: ``` $ ipcluster start -n 4 --engines=mpi ``` This approach is best as interrupting {command}`ipcluster` will automatically stop and clean up the controller and engines. ### Manual starting using {command}`mpiexec` If you want to start the IPython engines using the {command}`mpiexec`: do: ``` $ mpiexec -n 4 ipengine ``` ### Automatic starting using PBS and {command}`ipcluster` The {command}`ipcluster` command also has built-in integration with PBS. For more information on this approach, see our documentation on {ref}`ipcluster `. ## Actually using MPI Once the engines are running with MPI enabled, you are ready to go. You can now call any code that uses MPI in the IPython engines. And, all of this can be done interactively. Here we show a simple example that uses [mpi4py][] version 1.1.0 or later. First, lets define a function that uses MPI to calculate the sum of a distributed array. Save the following text in a file called {file}`psum.py`: ```python from mpi4py import MPI import numpy as np def psum(a): locsum = np.sum(a) rcvBuf = np.array(0.0, 'd') MPI.COMM_WORLD.Allreduce([locsum, MPI.DOUBLE], [rcvBuf, MPI.DOUBLE], op=MPI.SUM) return rcvBuf ``` Now, start an IPython cluster: ``` $ ipcluster start --engines=mpi -n 4 ``` Finally, connect to the cluster and use this function interactively. In this case, we create a distributed array and sum up all its elements in a distributed manner using our {func}`psum` function: ```ipython In [1]: import ipyparallel as ipp In [2]: c = ipp.Cluster.from_file().connect_client_sync() In [3]: view = c[:] In [4]: view.activate() # enable magics # run the contents of the file on each engine: In [5]: view.run('psum.py') In [6]: view.scatter('a',np.arange(16,dtype='float')) In [7]: view['a'] Out[7]: [array([ 0., 1., 2., 3.]), array([ 4., 5., 6., 7.]), array([ 8., 9., 10., 11.]), array([ 12., 13., 14., 15.])] In [7]: %px totalsum = psum(a) Parallel execution on engines: [0,1,2,3] In [8]: view['totalsum'] Out[8]: [120.0, 120.0, 120.0, 120.0] ``` Any Python code that makes calls to MPI can be used in this manner, including compiled C, C++ and Fortran libraries that have been exposed to Python. [mpi]: https://www.mcs.anl.gov/research/projects/mpi [mpi4py]: https://mpi4py.readthedocs.io/ [openmpi]: https://www.open-mpi.org ipyparallel-7.1.0/docs/source/reference/security.md000066400000000000000000000405011412533144600224170ustar00rootroot00000000000000(security)= # Security details of IPython Parallel IPython Parallel exposes the full power of the Python interpreter over a TCP/IP network (or BSD socket) for the purposes of parallel computing. This feature raises the important question of IPython's security model. This document gives details about this model and how it is implemented in IPython's architecture. ## Process and network topology To enable parallel computing, IPython has a number of different processes that run. These processes are discussed at length in the IPython documentation and are summarized here: - The IPython _engine_. This process is a full blown Python interpreter in which user code is executed. Multiple engines are started to make parallel computing possible. - The IPython _hub_. This process monitors a set of engines and schedulers, and keeps track of the state of the processes. It listens for registration connections from engines and clients, and monitor connections from schedulers. - The IPython _schedulers_. This is a set of processes that relay commands and results between clients and engines. They are typically on the same machine as the hub, and listen for connections from engines and clients, but connect to the Hub. - The IPython _client_. This process is typically an interactive Python process that is used to coordinate the engines to get a parallel computation done. Collectively, these processes are called an IPython _cluster_, and the hub and schedulers together are referred to as the _controller_. These processes communicate over any transport supported by ZeroMQ (tcp, pgm, ipc) with a well-defined topology. The IPython controller processes listen on sockets. Upon starting, an engine connects to a hub and sends a message requesting registration, to which the hub replies with connection information for the schedulers, and the engine then connects to the schedulers. These engine->hub and engine->scheduler connections persist for the lifetime of each engine. The IPython client also connects to the controller processes using a number of sockets. This is one socket per scheduler. These connections persist for the lifetime of the client only. A given IPython controller and set of engines typically has a relatively short lifetime, such as the duration of a single parallel computation performed by a single user. Finally, the hub, schedulers, engines, and client processes typically execute with the permissions of that same user. More specifically, the controller and engines are _not_ executed as root or with any other superuser or shared-user permissions. ## Application logic When running the IPython kernel to perform a parallel computation, a user connects an IPython client to send Python commands and data through the IPython schedulers to the IPython engines, where those commands are executed and the data processed. Via the client, a user can instruct the IPython engines to execute arbitrary Python commands. These Python commands can include calls to the system shell, access the filesystem, etc., as required by the user's application. From this perspective, when a user runs an IPython engine on a host, that engine has the same capabilities and permissions as the user themselves (as if they were logged onto the engine's host with a terminal). ### ZeroMQ and Connection files IPython uses ZeroMQ for networking. By default, no IPython connections are encrypted. Open ports listen only on localhost. When no encryption is used, messages are signed via HMAC digest using a shared key for authentication. As of IPython 7.1, all connections _can_ be authenticated and encrypted using [CurveZMQ][]. The key (whether the CurveZMQ key or HMAC digest key) is distributed to engines and clients via connection files. TCP connections can be tunneled over SSH. IPython supports both shell (`openssh`) and `paramiko` based tunnels for connections. In our architecture, the controller is the only process that listens on network ports, and is thus the main point of vulnerability. The standard model for secure connections is to designate that the controller listen on localhost, and use ssh-tunnels to connect clients and/or engines, or connect over a trusted private network. To connect and authenticate to the controller an engine or client needs some information that the controller has stored in a JSON file. The JSON files may need to be copied to a location where the clients and engines can find them. Typically, this is the {file}`~/.ipython/profile_default/security` directory on the host where the client/engine is running, which could be on a different filesystem than the controller. Once the JSON files are copied over, everything should work fine. Currently, there are two JSON files that the controller creates: ipcontroller-engine.json : This JSON file has the information necessary for an engine to connect to a controller. ipcontroller-client.json : The client's connection information. Similar to the engine file, but lists a different collection of ports. ipcontroller-client.json will look something like this, under default localhost circumstances: ```python { "ssh": "", "interface": "tcp://127.0.0.1", "registration": 54886, "control": 54888, "mux": 54890, "hb_ping": 54891, "hb_pong": 54892, "task": 54894, "iopub": 54896, "broadcast": [ 54900, 54901 ], "key": "7e99e423-c437d4daf7cf23ee84cae803", "location": "mylaptop", "pack": "json", "unpack": "json", "signature_scheme": "hmac-sha256" } ``` If, however, you are running the controller on a work node on a cluster, you will likely need to use ssh tunnels to connect clients from your laptop to it. You will also probably need to instruct the controller to listen for engines coming from other work nodes on the cluster. An example of ipcontroller-client.json, as created by: ``` $> ipcontroller --ip=* --ssh=login.mycluster.com ``` ```python { "ssh": "login.mycluster.com", "interface": "tcp://*", "registration": 55836, "control": 55837, "mux": 55839, "task": 55843, "task_scheme": "lru", "iopub": 55845, "notification": 55852, "broadcast": [ 55847, 55848, 55849 ], "key": "70bc97ac-e66ac5143885ca8b376d4cb7", "location": "mylaptop", "pack": "json", "unpack": "json", "signature_scheme": "hmac-sha256" } ``` More details of how these JSON files are used are given below. (secure-network)= ## Secure network connections ### Overview ZeroMQ supports encryption and authentication via a mechanism called [CurveZMQ][]. IPython Parallel supports CurveZMQ as of version 7.1. To enable CurveZMQ, set ```python c.IPController.enable_curve = True ``` in `ipcontroller_config.py`, or set the environment variable `IPP_ENABLE_CURVE=1`. When using CurveZMQ, all connections are authenticated using the controller's server key, which is distributed to engines and clients via connection files. This key is all that is needed to connect to an IPython cluster and execute code. Additionally, when using CurveZMQ, all communication is _encrypted_ using the controller's server key. Each client typically generates its own unique, short-lived key pair for its side of encrypted communication. When not using CurveZMQ, messages are not encrypted. ### Security without CurveZMQ When not using CurveZMQ, all ZeroMQ connections are not authenticated and not encrypted. For this reason, users of IPython must be very careful in managing connections, because an open TCP/IP socket presents access to arbitrary execution as the user on the engine machines. As a result, the default behavior of controller processes is to only listen for clients on the loopback interface, and remote clients must establish SSH tunnels to connect to the controller processes. ```{warning} If the controller's loopback interface is untrusted, then IPython should be considered vulnerable without CurveZMQ, and this extends to the loopback of all connected clients, which have opened a loopback port that is redirected to the controller's loopback port. ``` ### SSH Without CurveZMQ, ZeroMQ sockets themselves provide no security. SSH tunnels can be used to encrypt traffic across the network, but _at least_ loopback traffic will be unencrypted. A connection file file, such as `ipcontroller-client.json`, will contain information for connecting to the controller, possibly including the address of an ssh server through which the client is to tunnel. The Client object then creates tunnels using either [openssh][] or [paramiko][], depending on the platform. If users do not wish to use OpenSSH or Paramiko, or the tunneling utilities are insufficient, then they may construct the tunnels themselves, and connect clients and engines as if the controller were on loopback on the connecting machine. ### Authentication IPython uses a key-distribution model of authentication. Whether you are using CurveZMQ or message-digest signatures. In both cases, a single key is used for authentication, and the same key is distributed in `ipcontroller-{client|engine}.json`. There is exactly one shared key per cluster - it must be the same everywhere. Typically, the controller creates this key, and stores it in the private connection files `ipython-{engine|client}.json`. These files are typically stored in the `~/.ipython/profile_/security` directory, and are maintained as readable only by the owner, as is common practice with a user's keys in their `.ssh` directory. The key distribution model is the same for both security implementations, however the level of authentication is substantially different. If you are using CurveZMQ, _connections_ are authenticated using the server key. This means connections attempted without the key will be rejected, and no messages can be sent or received by unauthenticated clients. #### Authentication without CurveZMQ If not using CurveZMQ, connections are not authenticated, only _messages_ are authenticated. This means that _clients_ with access to the controller's ports, but without the key will be able to connect and send and receive messages, but the requests will be rejected. To protect users of shared machines, [HMAC] digests are used to sign messages, using the shared key. The Session object that handles the message protocol uses a unique key to verify valid messages. This can be any value specified by the user, but the default behavior is a pseudo-random 128-bit number, as generated by `uuid.uuid4()`. This key is used to initialize an HMAC object, which digests all messages, and includes that digest as a signature and part of the message. Every message that is unpacked (on Controller, Engine, and Client) will also be digested by the receiver, ensuring that the sender's key is the same as the receiver's. No messages that do not contain this key are acted upon in any way. The key itself is never sent over the network. ```{warning} It is important to note that the signatures protect against unauthorized messages, but, as there is no encryption, provide exactly no protection of data privacy. It is possible, however, to use a custom serialization scheme (via Session.packer/unpacker traits) that does incorporate your own encryption scheme. ``` ### Encryption Messages are only encrypted when using CurveZMQ, which provides perfect-forward security by issuing short-lived keys for each session. Knowing the distributed CurveZMQ key is _not enough_ to decrypt communication from another client. ## Specific security vulnerabilities There are a number of potential security vulnerabilities present in IPython's architecture. In this section we discuss those vulnerabilities and detail how the security architecture described above prevents them from being exploited. ### Unauthorized clients The IPython client can instruct the IPython engines to execute arbitrary Python code with the permissions of the user who started the engines. If an attacker were able to connect their own hostile IPython client to the IPython controller, they could instruct the engines to execute code. On the first level, this attack is prevented by requiring access to the controller's ports, which are recommended to only be open on loopback if the controller is on an untrusted local network. If the attacker does have access to the Controller's ports, then the attack is prevented by the capabilities based client authentication of the execution key. The relevant authentication information is encoded into the JSON file that clients must present to gain access to the IPython controller. By limiting the distribution of those keys, a user can grant access to only authorized persons, as with SSH keys. When using CurveZMQ, the connection will be rejected without the key. When not using CurveZMQ, _requests_ will be rejected if not signed by the key. It is highly unlikely that an execution key could be guessed by an attacker in a brute force guessing attack. A given instance of the IPython controller only runs for a relatively short amount of time (on the order of hours). Thus an attacker would have only a limited amount of time to test a search space of size 2\*\*128. For added security, users can have arbitrarily long keys. ```{warning} If the attacker has gained enough access to intercept loopback connections on _either_ the controller or client, then a duplicate message can be sent. CurveZMQ prevents replay attacks. To protect against this, CurveZMQ uses nonces. recipients only allow each signature once, and consider duplicates invalid. However, the duplicate message could be sent to _another_ recipient using the same key, and it would be considered valid. ``` ### Unauthorized engines If an attacker were able to connect a hostile engine to a user's controller, the user might unknowingly send sensitive code or data to the hostile engine. This attacker's engine would then have full access to that code and data. This type of attack is prevented in the same way as the unauthorized client attack, by requiring the authentication key to register the engine with the client. ### Unauthorized controllers It is also possible that an attacker could try to convince a user's IPython client or engine to connect to a hostile IPython controller. That controller would then have full access to the code and data sent between the IPython client and the IPython engines. Again, this attack is prevented through the capabilities in a connection file, which ensure that a client or engine connects to the correct controller. It is also important to note that the connection files also encode the IP address and port that the controller is listening on, so there is little chance of mistakenly connecting to a controller running on a different IP address and port. When starting an engine or client, a user must specify the key to use for that connection. Thus, in order to introduce a hostile controller, the attacker must convince the user to use the key associated with the hostile controller. As long as a user is diligent in only using keys from trusted sources, this attack is not possible. ## Other security measures A number of other measures are taken to further limit the security risks involved in running the IPython kernel. First, by default, the IPython controller listens on random port numbers. While this can be overridden by the user, in the default configuration, an attacker would have to do a port scan to find a controller to attack. When coupled with the relatively short running time of a typical controller (on the order of hours), scans would have to be constant. Second, much of the time, especially when run on supercomputers or clusters, the controller is running behind a firewall. Thus, for engines or client to connect to the controller: - The different processes have to all be behind the firewall. or: - The user has to use SSH port forwarding to tunnel the connections through the firewall. In either case, an attacker is presented with additional barriers that prevent attacking or even probing the system, because they must have access to localhost on either the controller node or the client's machine. ## Summary IPython's architecture has been carefully designed with security in mind. The capabilities-based authentication model, in conjunction with CurveZMQ, ipc file permissions, and and/or SSH tunneled TCP/IP channels, address the core potential vulnerabilities in the system, while still enabling user's to use the system in open networks. [openssh]: https://www.openssh.com [paramiko]: https://www.lag.net/paramiko [hmac]: https://tools.ietf.org/html/rfc2104 [rfc5246]: https://tools.ietf.org/html/rfc5246 [curvezmq]: https://rfc.zeromq.org/spec/26/ ipyparallel-7.1.0/docs/source/tutorial/000077500000000000000000000000001412533144600201335ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/tutorial/asyncresult.md000066400000000000000000000116671412533144600230440ustar00rootroot00000000000000(asyncresult)= # The AsyncResult object In non-blocking mode, {meth}`~.View.apply`, {meth}`~.View.map`, and friends submit the command to be executed and then return an {class}`~.AsyncResult` object immediately. The AsyncResult object gives you a way of getting a result at a later time through its {meth}`get` method, but it also collects metadata on execution. ## Beyond stdlib AsyncResult and Future The {class}`AsyncResult` is a subclass of {py:class}`concurrent.futures.Future`. This means it can be integrated into existing async workflows, with e.g. {py:func}`asyncio.wrap_future`. It also extends the {py:class}`~.multiprocessing.AsyncResult` API. ```{seealso} - {py:class}`multiprocessing.AsyncResult` API - {py:class}`concurrent.futures.Future` API ``` In addition to these common features, our AsyncResult objects add a number of convenient methods for working with parallel results, beyond what is provided by the standard library classes on which they are based. ### get_dict {meth}`.AsyncResult.get_dict` pulls results as a dictionary, keyed by engine_id, rather than a flat list. This is useful for quickly coordinating or distributing information about all of the engines. As an example, here is a quick call that gives every engine a dict showing the PID of every other engine: ```ipython In [10]: ar = rc[:].apply_async(os.getpid) In [11]: pids = ar.get_dict() In [12]: rc[:]['pid_map'] = pids ``` This trick is particularly useful when setting up inter-engine communication, as in IPython's {file}`examples/parallel/interengine` examples. ## Metadata IPython Parallel tracks some metadata about the tasks, which is stored in the {attr}`.Client.metadata` dict. The AsyncResult object gives you an interface for this information as well, including timestamps stdout/err, and engine IDs. ### Timing IPython tracks various timestamps as {py:class}`.datetime` objects, and the AsyncResult object has a few properties that turn these into useful times (in seconds as floats). For use while the tasks are still pending: - {attr}`ar.elapsed` is the elapsed seconds since submission, for use before the AsyncResult is complete. - {attr}`ar.progress` is the number of tasks that have completed. Fractional progress would be: ``` 1.0 * ar.progress / len(ar) ``` - {meth}`AsyncResult.wait_interactive` will wait for the result to finish, but print out status updates on progress and elapsed time while it waits. For use after the tasks are done: - {attr}`ar.serial_time` is the sum of the computation time of all of the tasks done in parallel. - {attr}`ar.wall_time` is the time between the first task submitted and last result received. This is the actual cost of computation, including IPython overhead. ```{note} wall_time is only precise if the Client is waiting for results when the task finished, because the `received` timestamp is made when the result is unpacked by the Client, triggered by the {meth}`~Client.spin` call. If you are doing work in the Client, and not waiting/spinning, then `received` might be artificially high. ``` An often interesting metric is the time it cost to do the work in parallel relative to the serial computation, and this can be given with ```python speedup = ar.serial_time / ar.wall_time ``` ## Map results are iterable! When an AsyncResult object has multiple results (e.g. the {class}`~AsyncMapResult` object), you can iterate through results themselves, and act on them as they arrive: ```{literalinclude} ../examples/itermapresult.py :language: python :lines: 20-67 ``` That is to say, if you treat an AsyncMapResult as if it were a list of your actual results, it should behave as you would expect, with the only difference being that you can start iterating through the results before they have even been computed. This lets you do a simple version of map/reduce with the builtin Python functions, and the only difference between doing this locally and doing it remotely in parallel is using the asynchronous `view.map` instead of the builtin `map`. Here is a simple one-line RMS (root-mean-square) implemented with Python's builtin map/reduce. ```ipython In [38]: X = np.linspace(0,100) In [39]: from math import sqrt In [40]: add = lambda a,b: a+b In [41]: sq = lambda x: x*x In [42]: sqrt(reduce(add, map(sq, X)) / len(X)) Out[42]: 58.028845747399714 In [43]: sqrt(reduce(add, view.map(sq, X)) / len(X)) Out[43]: 58.028845747399714 ``` To break that down: 1. `map(sq, X)` Compute the square of each element in the list (locally, or in parallel) 2. `reduce(add, sqX) / len(X)` compute the mean by summing over the list (or AsyncMapResult) and dividing by the size 3. take the square root of the resulting number ```{seealso} When AsyncResult or the AsyncMapResult don't provide what you need (for instance, handling individual results as they arrive, but with metadata), you can always split the original result's `msg_ids` attribute, and handle them as you like. For an example of this, see {file}`examples/customresult.py` ``` ipyparallel-7.1.0/docs/source/tutorial/demos.md000066400000000000000000000166571412533144600216030ustar00rootroot00000000000000(parallel-examples)= # Parallel examples In this section we describe two more involved examples of using an IPython cluster to perform a parallel computation. We will be doing some plotting, so we start IPython with matplotlib integration by typing: ``` ipython --matplotlib ``` at the system command line. Or you can enable matplotlib integration at any point with: ```ipython In [1]: %matplotlib ``` ## 150 million digits of pi In this example we would like to study the distribution of digits in the number pi (in base 10). While it is not known if pi is a normal number (a number is normal in base 10 if 0-9 occur with equal likelihood) numerical investigations suggest that it is. We will begin with a serial calculation on 10,000 digits of pi and then perform a parallel calculation involving 150 million digits. In both the serial and parallel calculation we will be using functions defined in the {file}`pidigits.py` file, which is available in the {file}`examples/parallel` directory of the IPython source distribution. These functions provide basic facilities for working with the digits of pi and can be loaded into IPython by putting {file}`pidigits.py` in your current working directory and then doing: ```ipython In [1]: run pidigits.py ``` ### Serial calculation For the serial calculation, we will use [SymPy](https://www.sympy.org) to calculate 10,000 digits of pi and then look at the frequencies of the digits 0-9. Out of 10,000 digits, we expect each digit to occur 1,000 times. While SymPy is capable of calculating many more digits of pi, our purpose here is to set the stage for the much larger parallel calculation. In this example, we use two functions from {file}`pidigits.py`: {func}`one_digit_freqs` (which calculates how many times each digit occurs) and {func}`plot_one_digit_freqs` (which uses Matplotlib to plot the result). Here is an interactive IPython session that uses these functions with SymPy: ```ipython In [7]: import sympy In [8]: pi = sympy.pi.evalf(40) In [9]: pi Out[9]: 3.141592653589793238462643383279502884197 In [10]: pi = sympy.pi.evalf(10000) In [11]: digits = (d for d in str(pi)[2:]) # create a sequence of digits In [13]: freqs = one_digit_freqs(digits) In [14]: plot_one_digit_freqs(freqs) Out[14]: [] ``` The resulting plot of the single digit counts shows that each digit occurs approximately 1,000 times, but that with only 10,000 digits the statistical fluctuations are still rather large: ```{image} figs/single_digits.* ``` It is clear that to reduce the relative fluctuations in the counts, we need to look at many more digits of pi. That brings us to the parallel calculation. ### Parallel calculation Calculating many digits of pi is a challenging computational problem in itself. Because we want to focus on the distribution of digits in this example, we will use pre-computed digit of pi from the website of Professor Yasumasa Kanada at the University of Tokyo (). These digits come in a set of text files () that each have 10 million digits of pi. For the parallel calculation, we have copied these files to the local hard drives of the compute nodes. A total of 15 of these files will be used, for a total of 150 million digits of pi. To make things a little more interesting we will calculate the frequencies of all 2 digits sequences (00-99) and then plot the result using a 2D matrix in Matplotlib. The overall idea of the calculation is simple: each IPython engine will compute the two digit counts for the digits in a single file. Then in a final step the counts from each engine will be added up. To perform this calculation, we will need two top-level functions from {file}`pidigits.py`, {func}`compute_two_digit_freqs` and {func}`reduce_freqs`: ```{literalinclude} ../examples/pi/pidigits.py :language: python :lines: 52-67 ``` We will also use the {func}`plot_two_digit_freqs` function to plot the results. The code to run this calculation in parallel is contained in {file}`examples/parallel/parallelpi.py`. This code can be run in parallel using IPython by following these steps: 1. Use {command}`ipcluster` to start 15 engines. We used 16 cores of an SGE linux cluster (1 controller + 15 engines). 2. With the file {file}`parallelpi.py` in your current working directory, open up IPython, enable matplotlib, and type `run parallelpi.py`. This will download the pi files via ftp the first time you run it, if they are not present in the Engines' working directory. When run on our 16 cores, we observe a speedup of 14.2x. This is slightly less than linear scaling (16x) because the controller is also running on one of the cores. To emphasize the interactive nature of IPython, we now show how the calculation can also be run by typing the commands from {file}`parallelpi.py` interactively into IPython: ```ipython In [1]: import ipyparallel as ipp # The Client allows us to use the engines interactively. # We pass Client the name of the cluster profile we # are using. In [2]: c = ipp.Client(profile='mycluster') In [3]: v = c[:] In [3]: c.ids Out[3]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] In [4]: run pidigits.py In [5]: filestring = 'pi200m.ascii.%(i)02dof20' # Create the list of files to process. In [6]: files = [filestring % {'i':i} for i in range(1,16)] In [7]: files Out[7]: ['pi200m.ascii.01of20', 'pi200m.ascii.02of20', 'pi200m.ascii.03of20', 'pi200m.ascii.04of20', 'pi200m.ascii.05of20', 'pi200m.ascii.06of20', 'pi200m.ascii.07of20', 'pi200m.ascii.08of20', 'pi200m.ascii.09of20', 'pi200m.ascii.10of20', 'pi200m.ascii.11of20', 'pi200m.ascii.12of20', 'pi200m.ascii.13of20', 'pi200m.ascii.14of20', 'pi200m.ascii.15of20'] # download the data files if they don't already exist: In [8]: v.map(fetch_pi_file, files) # This is the parallel calculation using the Client.map method # which applies compute_two_digit_freqs to each file in files in parallel. In [9]: freqs_all = v.map(compute_two_digit_freqs, files) # Add up the frequencies from each engine. In [10]: freqs = reduce_freqs(freqs_all) In [11]: plot_two_digit_freqs(freqs) Out[11]: In [12]: plt.title('2 digit counts of 150m digits of pi') Out[12]: ``` The resulting plot generated by Matplotlib is shown below. The colors indicate which two digit sequences are more (red) or less (blue) likely to occur in the first 150 million digits of pi. We clearly see that the sequence "41" is most likely and that "06" and "07" are least likely. Further analysis would show that the relative size of the statistical fluctuations have decreased compared to the 10,000 digit calculation. ```{image} figs/two_digit_counts.* ``` ## Conclusion To conclude these examples, we summarize the key features of IPython's parallel architecture that have been demonstrated: - Serial code can be parallelized often with only a few extra lines of code. We have used the {class}`DirectView` and {class}`LoadBalancedView` classes for this purpose. - The resulting parallel code can be run without ever leaving the IPython's interactive shell. - Any data computed in parallel can be explored interactively through visualization or further numerical calculations. - We have run these examples on a cluster running RHEL 5 and Sun GridEngine. IPython's built in support for SGE (and other batch systems) makes it easy to get started with IPython's parallel capabilities. ipyparallel-7.1.0/docs/source/tutorial/direct.md000066400000000000000000000565731412533144600217470ustar00rootroot00000000000000(parallel-direct)= # IPython's Direct interface The direct interface represents one possible way of working with a set of IPython engines. The basic idea behind the direct interface is that the capabilities of each engine are directly and explicitly exposed to the user. Thus, in the direct interface, each engine is given an id that is used to identify the engine and give it work to do. This interface is very intuitive and is designed with interactive usage in mind, and is the best place for new users of IPython to begin. ## Starting the IPython controller and engines In general, in this tutorial, each step will start with a fresh cluster. There is always a choice when starting an interactive session: Option 1. starting a new cluster ```python import ipyparallel as ipp cluster = ipp.Cluster(n=4) cluster.start_cluster_sync() ``` Option 2. connecting to an existing cluster, e.g. if it were started via {command}`ipcluster start` or another notebook, or a JupyterLab extension. ```python import ipyparallel as ipp cluster = ipp.Cluster.from_file() ``` No arguments are required for the default cluster (e.g. `ipcluster start` with no arguments), but `profile` and/or `cluster_id` would be typical arguments to specify a cluster. For more detailed information about starting the controller and engines, see our {ref}`introduction ` to using IPython for parallel computing. ## Creating a `DirectView` The first step is to connect a {class}`.Client` to your cluster: ```ipython In [2]: rc = cluster.connect_client_sync() ``` To make sure there are engines connected to the controller, users can get a list of engine ids: ```ipython In [3]: rc.wait_for_engines(4); rc.ids Out[3]: [0, 1, 2, 3] ``` Here we see that there are four engines ready to do work for us. For direct execution, we will make use of a {class}`DirectView` object, which can be constructed via list-access to the client: ```ipython In [4]: dview = rc[:] # use all engines ``` ```{seealso} For more information, see the in-depth explanation of {ref}`Views `. ``` ## Quick and easy parallelism In many cases, you want to call a Python function on a sequence of objects, but _in parallel_. IPython Parallel provides a simple way of accomplishing this: using the DirectView's {meth}`~DirectView.map` method. ### Parallel map Python's builtin {func}`map` functions allows a function to be applied to a sequence element-by-element. This type of code is typically trivial to parallelize. In fact, since IPython's interface is all about functions anyway, you can use the builtin {func}`map` with a {class}`RemoteFunction`, or a DirectView's {meth}`map` method: ```ipython In [62]: serial_result = list(map(lambda x:x**10, range(32))) In [63]: parallel_result = dview.map_sync(lambda x: x**10, range(32)) In [64]: serial_result == parallel_result Out[64]: True ``` ```{note} The {class}`DirectView`'s version of {meth}`map` does not do dynamic load balancing. For a load-balanced version, use a {class}`LoadBalancedView`. ``` ## Calling Python functions The most basic type of operation that can be performed on the engines is to execute Python code or call Python functions. Executing Python code can be done in blocking or non-blocking mode (non-blocking is default) using the {meth}`.View.execute` method, and calling functions can be done via the {meth}`.View.apply` method. ### apply The main method for doing remote execution (in fact, almost all methods that communicate with the engines are built on top of it), is {meth}`View.apply`. We strive to provide the cleanest interface we can, so `apply` has the following signature: ```python view.apply(f, *args, **kwargs) ``` There are some controls to influence the behavior of `apply`, called flags. Views store the default values for these flags as attributes. The `DirectView` has these flags: dv.block : whether to wait for the result, or return an {class}`AsyncResult` object immediately dv.track : whether to instruct pyzmq to track when zeromq is done sending the message. This is primarily useful for non-copying sends of numpy arrays that you plan to edit in-place. You need to know when it becomes safe to edit the buffer without corrupting the message. There is a performance cost to enabling tracking, so it is not recommended except for sending very large messages. dv.targets : The engines associated with this View. Creating a view is done as if the client is a Python 'container' of engines: index-access on a client creates a {class}`.DirectView`. ```ipython In [4]: view = rc[1:3] Out[4]: In [5]: view.apply view.apply view.apply_async view.apply_sync ``` For convenience, you can specify blocking behavior explicitly for a single call with the extra sync/async methods. ### Blocking execution In blocking mode, the {class}`.DirectView` object (called `dview` in these examples) submits the command to the controller, which places the command in the engines' queues for execution. The {meth}`apply` call then blocks until the engines are done executing the command: ```ipython In [2]: dview = rc[:] # A DirectView of all engines In [3]: dview.block=True In [4]: dview['a'] = 5 In [5]: dview['b'] = 10 In [6]: dview.apply(lambda x: a+b+x, 27) Out[6]: [42, 42, 42, 42] ``` You can also select blocking execution on a call-by-call basis with the {meth}`apply_sync` method: ```ipython In [7]: dview.block = False In [8]: dview.apply_sync(lambda x: a+b+x, 27) Out[8]: [42, 42, 42, 42] ``` Python commands can be executed as strings on specific engines by using a View's `execute` method: ```ipython In [6]: rc[::2].execute('c = a + b') In [7]: rc[1::2].execute('c = a - b') In [8]: dview['c'] # shorthand for dview.pull('c', block=True) Out[8]: [15, -5, 15, -5] ``` ### async execution In non-blocking (async) mode, {meth}`apply` submits the command to be executed and then returns a {class}`AsyncResult` object immediately. The {class}`AsyncResult` object gives you a way of getting a result at a later time through its {meth}`get` method. ```{seealso} Docs on the {ref}`AsyncResult ` object. ``` This allows you to quickly submit long-running commands without blocking your local IPython session: ```ipython # define our function In [6]: def wait(t): ....: import time ....: tic = time.time() ....: time.sleep(t) ....: return time.time()-tic # In non-blocking mode In [7]: ar = dview.apply_async(wait, 2) # Now block for the result In [8]: ar.get() Out[8]: [2.0006198883056641, 1.9997570514678955, 1.9996809959411621, 2.0003249645233154] # Again in non-blocking mode In [9]: ar = dview.apply_async(wait, 10) # Poll to see if the result is ready In [10]: ar.ready() Out[10]: False # ask for the result, but wait a maximum of 1 second: In [45]: ar.get(1) --------------------------------------------------------------------------- TimeoutError Traceback (most recent call last) /home/you/ in () ----> 1 ar.get(1) /path/to/site-packages/IPython/parallel/asyncresult.pyc in get(self, timeout) 62 raise self._exception 63 else: ---> 64 raise error.TimeoutError("Result not ready.") 65 66 def ready(self): TimeoutError: Result not ready. ``` ```{Note} Note the import inside the function. This is a common model, to ensure that the appropriate modules are imported where the task is run. You can also manually import modules into the engine(s) namespace(s) via `view.execute('import numpy')`. ``` Often, it is desirable to wait until a set of {class}`AsyncResult` objects are done. For this, there is a the method {meth}`wait`. This method takes a collection of {class}`AsyncResult` objects (or `msg_ids` or integer indices to the client's history), and blocks until all of the associated results are ready: ```ipython In [72]: dview.block = False # A trivial list of AsyncResults objects In [73]: ar_list = [dview.apply_async(wait, 3) for i in range(10)] # Wait until all of them are done In [74]: dview.wait(ar_list) # Then, their results are ready using get() In [75]: ar_list[0].get() Out[75]: [2.9982571601867676, 2.9982588291168213, 2.9987530708312988, 2.9990990161895752] ``` ### The `block` and `targets` keyword arguments and attributes Most DirectView methods (excluding {meth}`apply`) accept `block` and `targets` as keyword arguments. As we have seen above, these keyword arguments control the blocking mode and which engines the command is applied to. The {class}`View` class also has {attr}`block` and {attr}`targets` attributes that control the default behavior when the keyword arguments are not provided. Thus the following logic is used for {attr}`block` and {attr}`targets`: - If no keyword argument is provided, the instance attributes are used. - The keyword arguments, if provided overrides the instance attributes for the duration of a single call. The following examples demonstrate how to use the instance attributes: ```ipython In [16]: dview.targets = [0, 2] In [17]: dview.block = False In [18]: ar = dview.apply(lambda : 10) In [19]: ar.get() Out[19]: [10, 10] In [20]: dview.targets = rc.ids # all engines (4) In [21]: dview.block = True In [22]: dview.apply(lambda : 42) Out[22]: [42, 42, 42, 42] ``` The {attr}`block` and {attr}`targets` instance attributes of the {class}`.DirectView` also determine the behavior of the parallel magic commands. ```{seealso} See the documentation of the {ref}`Parallel Magics `. ``` ## Moving Python objects around In addition to calling functions and executing code on engines, you can transfer Python objects between your IPython session and the engines. In IPython, these operations are called {meth}`push` (sending an object to the engines) and {meth}`pull` (getting an object from the engines). ### Basic push and pull Here are some examples of how you use {meth}`push` and {meth}`pull`: ```ipython In [38]: dview.push(dict(a=1.03234, b=3453)) Out[38]: [None, None, None, None] In [39]: dview.pull('a') Out[39]: [ 1.03234, 1.03234, 1.03234, 1.03234] In [40]: dview.pull('b', targets=0) Out[40]: 3453 In [41]: dview.pull(('a', 'b')) Out[41]: [ [1.03234, 3453], [1.03234, 3453], [1.03234, 3453], [1.03234, 3453] ] In [42]: dview.push(dict(c='speed')) Out[42]: [None, None, None, None] ``` In non-blocking mode {meth}`push` and {meth}`pull` also return {class}`AsyncResult` objects: ```ipython In [48]: ar = dview.pull('a', block=False) In [49]: ar.get() Out[49]: [1.03234, 1.03234, 1.03234, 1.03234] ``` ### Dictionary interface Since a Python namespace is a {class}`dict`, {class}`DirectView` objects provide dictionary-style access by key and methods such as {meth}`get` and {meth}`update` for convenience. This make the remote namespaces of the engines appear as a local dictionary. Underneath, these methods call {meth}`apply`: ```ipython In [51]: dview['a'] = ['foo', 'bar'] In [52]: dview['a'] Out[52]: [ ['foo', 'bar'], ['foo', 'bar'], ['foo', 'bar'], ['foo', 'bar'] ] ``` ### Scatter and gather Sometimes it is useful to partition a sequence and push the partitions to different engines. In MPI language, this is know as scatter/gather and we follow that terminology. However, it is important to remember that in IPython's {class}`Client` class, {meth}`scatter` is from the interactive IPython session to the engines and {meth}`gather` is from the engines back to the interactive IPython session. For scatter/gather operations between engines, MPI, pyzmq, or some other direct interconnect should be used. ```ipython In [58]: dview.scatter('a',range(16)) Out[58]: [None,None,None,None] In [59]: dview['a'] Out[59]: [ [0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15] ] In [60]: dview.gather('a') # This will show you the status of gather. Out[60]: In [61]: dview.gather('a').get() # This will give you the result. Out[61]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] In [62]: dview.gather('a')[3] # You can also direct call the result. Out[62]: [2] ``` ## Other things to look at ### Signaling engines New in IPython Parallel 7.0 is the {meth}`Client.send_signal` method. This lets you directly interrupt engines, which might be running a blocking task that you want to cancel. This is also available via the Cluster API. Unlike the Cluster API, though, which only allows interrupting whole engine 'sets' (usally all engines in the cluster), the client API allows interrupting individual engines. ```ipython In [9]: ar = rc[:].apply_async(time.sleep, 5) In [10]: rc.send_signal(signal.SIGINT) Out[10]: In [11]: ar.get() [12:apply]: --------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) in KeyboardInterrupt: [13:apply]: --------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) in KeyboardInterrupt: [14:apply]: --------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) in KeyboardInterrupt: [15:apply]: --------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) in KeyboardInterrupt: ``` ### Remote function decorators Remote functions are like normal functions, but when they are called they execute on one or more engines rather than locally. IPython provides two decorators for producing parallel functions. The first is `@remote`, which calls the function on every engine of a view. ```ipython In [10]: @dview.remote(block=True) ....: def getpid(): ....: import os ....: return os.getpid() ....: In [11]: getpid() Out[11]: [12345, 12346, 12347, 12348] ``` The `@parallel` decorator creates parallel functions, that break up an element-wise operations and distribute them, reconstructing the result. ```ipython In [12]: import numpy as np In [13]: A = np.random.random((64,48)) In [14]: @dview.parallel(block=True) ....: def pmul(A,B): ....: return A*B In [15]: C_local = A*A In [16]: C_remote = pmul(A,A) In [17]: (C_local == C_remote).all() Out[17]: True ``` Calling a `@parallel` function _does not_ correspond to map. It is used for splitting element-wise operations that operate on a sequence or array. For `map` behavior, parallel functions have a map _method_. | call | pfunc(seq) | pfunc.map(seq) | | ------------------ | --------------------------- | --------------------------- | | # of tasks | # of engines (1 per engine) | # of engines (1 per engine) | | # of remote calls | # of engines (1 per engine) | `len(seq)` | | argument to remote | `seq[i:j]` (sub-sequence) | `seq[i]` (single element) | A quick example to illustrate the difference in arguments for the two modes: ```ipython In [16]: @dview.parallel(block=True) ....: def echo(x): ....: return str(x) In [17]: echo(range(5)) Out[17]: ['[0, 1]', '[2]', '[3]', '[4]'] In [18]: echo.map(range(5)) Out[18]: ['0', '1', '2', '3', '4'] ``` ```{seealso} See the {func}`~.remotefunction.parallel` and {func}`~.remotefunction.remote` decorators for options. ``` ### How to do parallel list comprehensions In many cases list comprehensions are nicer than using the map function. While we don't have fully parallel list comprehensions, it is simple to get the basic effect using {meth}`scatter` and {meth}`gather`: ```ipython In [66]: dview.scatter('x',range(64)) In [67]: %px y = [i**10 for i in x] Parallel execution on engines: [0, 1, 2, 3] In [68]: y = dview.gather('y') In [69]: print y [0, 1, 1024, 59049, 1048576, 9765625, 60466176, 282475249, 1073741824,...] ``` ### Remote imports Sometimes you may want to import packages both in your interactive session and on your remote engines. This can be done with the context manager created by a DirectView's {meth}`sync_imports` method: ```ipython In [69]: with dview.sync_imports(): ....: import numpy importing numpy on engine(s) ``` Any imports made inside the block will also be performed on the view's engines. sync_imports also takes a `local` boolean flag that defaults to True, which specifies whether the local imports should also be performed. However, support for `local=False` has not been implemented, so only packages that can be imported locally will work this way. Note that the usual renaming of the import handle in the same line like in `import matplotlib.pyplot as plt` does not work on the remote engine, the `as plt` is ignored remotely, while it executes locally. One could rename the remote handle with `%px plt = pyplot` though after the import. You can also specify imports via the `@ipp.require` decorator. This is a decorator designed for use in dependencies, but can be used to handle remote imports as well. Modules or module names passed to `@ipp.require` will be imported before the decorated function is called. If they cannot be imported, the decorated function will never execute and will fail with an UnmetDependencyError. Failures of single Engines will be collected and raise a CompositeError, as demonstrated in the next section. ```ipython In [70]: @ipp.require('re') ....: def findall(pat, x): ....: # re is guaranteed to be available ....: return re.findall(pat, x) # you can also pass modules themselves, that you already have locally: In [71]: @ipp.require(time) ....: def wait(t): ....: time.sleep(t) ....: return t ``` ```{note} {func}`sync_imports` does not allow `import foo as bar` syntax, because the assignment represented by the `as bar` part is not available to the import hook. ``` (parallel-exceptions)= ### Parallel exceptions Parallel commands can raise Python exceptions, just like serial commands. This is complicated by the fact that a single parallel command can raise multiple exceptions (one for each engine the command was run on). To express this idea, we have a {exc}`CompositeError` exception class that will be raised when there are mulitple errors. The {exc}`CompositeError` class is a special type of exception that wraps one or more other exceptions. Here is how it works: ```ipython In [78]: dview.block = True In [79]: dview.execute("1/0") [0:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero [1:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero [2:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero [3:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero ``` Notice how the error message printed when {exc}`CompositeError` is raised has information about the individual exceptions that were raised on each engine. If you want, you can even raise one of these original exceptions: ```ipython In [80]: try: ....: dview.execute('1/0', block=True) ....: except ipp.CompositeError as e: ....: e.raise_exception() ....: ....: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero ``` If you are working in IPython, you can type `%debug` after one of these {exc}`CompositeError` exceptions is raised and inspect the exception: ```ipython In [81]: dview.execute('1/0') [0:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero [1:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero [2:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero [3:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero In [82]: %debug > /.../site-packages/IPython/parallel/client/asyncresult.py(125)get() 124 else: --> 125 raise self._exception 126 else: # Here, self._exception is the CompositeError instance: ipdb> e = self._exception ipdb> e CompositeError(4) # we can tab-complete on e to see available methods: ipdb> e. e.args e.message e.traceback e.elist e.msg e.ename e.print_traceback e.engine_info e.raise_exception e.evalue e.render_traceback # We can then display the individual tracebacks, if we want: ipdb> e.print_traceback(1) [1:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero ``` If you have 100 engines, you probably don't want to see 100 identical tracebacks for a NameError because of a small typo. For this reason, CompositeError truncates the list of exceptions it will print to {attr}`CompositeError.tb_limit` (default is five). You can change this limit to suit your needs with: ```ipython In [21]: ipp.CompositeError.tb_limit = 1 In [22]: %px x=z [0:execute]: --------------------------------------------------------------------------- NameError Traceback (most recent call last) ----> 1 x=z NameError: name 'z' is not defined ... 3 more exceptions ... ``` All of this same error handling magic works the same in non-blocking mode: ```ipython In [83]: dview.block=False In [84]: ar = dview.execute('1/0') In [85]: ar.get() [0:execute]: --------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) ----> 1 1/0 ZeroDivisionError: integer division or modulo by zero ... 3 more exceptions ... ``` Sometimes you still want to get the successful subset, even when there was an error. Like {py:func}`asyncio.gather`, {meth}`.AsyncResult.get` and map functions accept a `return_exception` argument (new in IPython Parallel 7.0), to return the Exception objects among results instead of raising the first error encountered. ```ipython In [89]: ar = dview.apply_async(lambda: 1/0) In [90]: ar.get(return_exceptions=True) Out[90]: [, , , ] ``` ```{versionadded} 7.0 The `return_exceptions` feature ``` ``` ipyparallel-7.1.0/docs/source/tutorial/figs/000077500000000000000000000000001412533144600210635ustar00rootroot00000000000000ipyparallel-7.1.0/docs/source/tutorial/figs/asian_call.pdf000066400000000000000000000311001412533144600236370ustar00rootroot00000000000000%PDF-1.4 % 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 8 0 obj << /XObject 7 0 R /Pattern 5 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /ExtGState 4 0 R /Shading 6 0 R /Font 3 0 R >> endobj 10 0 obj << /Contents 9 0 R /Type /Page /Resources 8 0 R /Parent 2 0 R /MediaBox [ 0 0 576 432 ] >> endobj 9 0 obj << /Filter /FlateDecode /Length 11 0 R >> stream xXɮ5W$"exݠHmvvp\njW?r]vvϧ41{^nNl T?1y/<W K7{HGd)g-MJV >a-qӮzZսXJh62|V:)훊yL3~ܟaЬQYGC4c/(앵5`-[b 9mM yjOpR^9 j6Vhe={L3n~垉7ޟE;tlTltN  glO /H;bOޘ٠Lrfr˵3Y01da 6i-L6F "O2zuV 1زbۉ TT tap7օ`!ǘ])Nbov$knnآZp,\[8DJaPR\PzQ%A$%BGA>foo8MI<˸reXurT5a_'{ʬ07<~驪aǵDTU+\?*/x)+8&Z-luvyXo1±a)#A~:VrY[F8LzErX렭4Payܾ%;e>KӭNܼv]Z<@)nzpi'.`6 JJ2jx8uV0D\q^x0ԏklt9]S)cU Pwj~>rl8&G:MXљSRx f wm֢{1 w`KfH;M_Ij$ {%BOG3wVc;y~-WƦeEw^] endstream endobj 11 0 obj 1705 endobj 16 0 obj << /Filter /FlateDecode /Length 49 >> stream x36P0P040F@B!H Y@8&+ & endstream endobj 17 0 obj << /Filter /FlateDecode /Length 80 >> stream xE 0D{`~&f( JpO{:2Sa ,S`5FR죰n_uzS*Ovvq= endstream endobj 18 0 obj << /Filter /FlateDecode /Length 90 >> stream xMA "OPDtz_NE5jK02kP)U0\ 2IL{qIqzz"X endstream endobj 19 0 obj << /Filter /FlateDecode /Length 210 >> stream x5P C1g dVukm;aBXȔy)K>:L." u%ʚ +`p&^7`i5tႦ.B%|u{OxjrvC` jMX> stream x36P0C. endstream endobj 21 0 obj << /Filter /FlateDecode /Length 317 >> stream x5RKrC1ۿSpΘ}tj'+-@B./YK~%ۥW%B>R-G- Q=2'":xa>N)x_xN;2$KMH=I+4t~&+s{rj X+)$=Hr7VސWg%&&MܕBXtLX㰄*aՃM5fcdxLP} #GMv²[6!D3,($Nc$ Ұ9 9e, mh%zМaמE[{ endstream endobj 22 0 obj << /Filter /FlateDecode /Length 338 >> stream x5R9@ } ] v͜~߆_ CVie!U-.Im W%ڥ Pt,6˯JH+kLwIi"Eo7o}=@.^ AS(i|Ъc(ew 4<3}(~_K&(? _osџa`Ś}@*z`yT endstream endobj 23 0 obj << /Filter /FlateDecode /Length 88 >> stream x5 0D{8R[.{9>G-dCxI9>Q4Zo:Hsd33}d4I!rY)z>~ endstream endobj 24 0 obj << /Filter /FlateDecode /Length 232 >> stream x5Q;r1} ] 3og3JFfa =؈ėoYf~'Y)QTEX!Yjs#Sr&>'b8Ύf01h9f=!#n4U i[ZSEȺ)Z[=- c _ĜE'~3尒4#15όO}>hw/̈́LHœƘ1T$?г>0TG endstream endobj 25 0 obj << /Filter /FlateDecode /Length 165 >> stream xE;! C{N#y6;I,%?+:cck'R: g]a2So\\`nN|愯U—L 0;Q|$ ꔍ0G@LM/yjAPkGkS4g*[ :L?= endstream endobj 26 0 obj << /Filter /FlateDecode /Length 338 >> stream x5RK[AۿSjy쬠8K Qry:i>ޅԂ"iǚ5Q4b)>(!SHfd2r >֛L9is(Iz-v|YAғu-Mk"925Ǟ2#A, IOH3;g-yx/~2U2 `; ("$h(fƚۅ"=D^ ,z2'; Jjaq6JS]g&a氳R2 qp/${1aļ]l\̹&ӏ(H\M(?7} endstream endobj 27 0 obj << /Filter /FlateDecode /Length 247 >> stream xMQmD1 \ky R]oC /)%K [UC?13,=?TPbht/"+ߏe s`&4`oI&ռ3d‰ATwM,3V7: lx%D`r Z`Q+ tĺv7C/਺x} K{,|BL;wI#fR:=b}@e+ (\* endstream endobj 28 0 obj << /Filter /FlateDecode /Length 248 >> stream x-Q9AzBsˑ C :-qPO+Uwu9HTM]vf5,?c 7zqxLu5{kOfP2+qSușO \ ȹeƌ#M!RH&3AQ~#aU#j \Ks4;<9GW +ET<pC7ҹ^s0XM7/=[ endstream endobj 29 0 obj << /Filter /FlateDecode /Length 70 >> stream x30Q0Pb3s3s#KC.# 3@.X 261R044L͍b0@YKA9P9\iOT/ endstream endobj 30 0 obj << /Filter /FlateDecode /Length 304 >> stream x=;0 C{Ȍd'>2VI(/u< i& b;w؞D/)ϡ+E:Ū0[M*K õ}74uK hY pu;Gw5<TQ!OJ|<(!\{0FS@\^BAjI'> stream x5QIn0 .AO ``hKxA[֌]< 'Q^{ .o8|^Z9O2 D`@ai'ώH5YN_KK(O~ J.kO8'O [WLcD.A|!]'@;綟Wuu)O645I"%Ҹ[{TS endstream endobj 32 0 obj << /Filter /FlateDecode /Length 245 >> stream xEPC1 =`,{wHۿ=JFp!Z?ZK oGFA= 3A΄@xFnvpμ39Zpә\'mBITqTqLύׁlӑ!KI%&~S*)[*EH䁓M4,?Cb̠Q0qGuٜ9-L|X&Q)2>'\N}䢥Uޑ"ۡW%Qէ<Y> endstream endobj 33 0 obj << /Filter /FlateDecode /Length 68 >> stream x32P0P4& f )\@B.H  %[B4AXf&fI8"ɴ endstream endobj 34 0 obj << /Filter /FlateDecode /Length 81 >> stream x=̻0>SB|[D|! nKw}zȀSj\Ni}}jKՉ?k endstream endobj 35 0 obj << /Filter /FlateDecode /Length 45 >> stream x32P0P4& f )\V.L,іp "} endstream endobj 36 0 obj << /Filter /FlateDecode /Length 214 >> stream x=PC1= |7˥m$B6BLɔ:ʒ)O>Kbnd6%*E/% }ՖC4h9~ 3*K6p*3 mtV[ Ф`׶ r " JMrR=ot-N=Dkq: DpFjtaŲC5=kz7hGt4CָR endstream endobj 37 0 obj << /Filter /FlateDecode /Length 161 >> stream xEK CBGG|tJ■!M@w'/mK >[ x6n5uVhR}ith6s+ fz:rGp_Gdf)|Q]dcnk]3s: endstream endobj 38 0 obj << /Filter /FlateDecode /Length 332 >> stream x-R9$1 ~`LtIUls#h/#xE=f۴[iGiK,W ;BjW0wy.2meDkag؏]e8*Jl !2J'Qw\I2[E™w2;yNE{ kF9+%|6vzrYɩHHӺ NKؖߗ3| endstream endobj 39 0 obj << /Filter /FlateDecode /Length 157 >> stream xEC1DsUA wJo-%S'"h0yM%V,&rAJ1xN1븨ufihW3=5'M<[ }@8IP1}bv">G)#qbn fW7y endstream endobj 40 0 obj << /Filter /FlateDecode /Length 320 >> stream x5Qq0 54sۿ @;a@dJ\UGM>`!S֖{&UF!}W2j]* UYFp&I8d Rӿccz endstream endobj 41 0 obj << /Filter /FlateDecode /Length 131 >> stream xE ! CT>՞0ABA";06Ѣ76իc,zRV鐇Pi0QąYLCaΘȖ2MlTv<e~ma, U^ ?KwUBS0 endstream endobj 42 0 obj << /Filter /FlateDecode /Length 138 >> stream x=A1y?)vBX޳UO_K^1BCoj NjK)Jș`gzb8V}F%hGSiܖq5)\W4ݴk8߽U__. endstream endobj 43 0 obj << /Filter /FlateDecode /Length 392 >> stream x=RKn1)@Mr[T /1 %?ꒈ3L~r]Qljg!.6Xr_rњbO/ȴTXVݣC(-װr{d`Jn@CHYAaPl( WԬtb ) ٠[]aP[[xfޑ3qYk?=Q2QMg|2RCgB'`$Ip#A 1qOl)V;ޒ{,\L'ib?lK\+E(~Aq|XdDw#h% 0xyDhDԎ=(ͱ&{ǫvzcw. endstream endobj 14 0 obj << /FontDescriptor 13 0 R /Name /BitstreamVeraSans-Roman /FontMatrix [ 0.001 0 0 0.001 0 0 ] /BaseFont /BitstreamVeraSans-Roman /Widths 12 0 R /Subtype /Type3 /CharProcs 15 0 R /Type /Font /FirstChar 0 /FontBBox [ -184 -236 1288 929 ] /Encoding << /Differences [ 32 /space 46 /period 48 /zero /one /two /three /four /five /six 56 /eight /nine 65 /A 67 /C 80 /P 83 /S 86 /V 97 /a 99 /c 101 /e 105 /i 107 /k /l 110 /n /o 114 /r /s /t 121 /y ] /Type /Encoding >> /LastChar 255 >> endobj 13 0 obj << /Descent -236 /FontBBox [ -184 -236 1288 929 ] /StemV 0 /Flags 32 /XHeight 547 /Type /FontDescriptor /FontName /BitstreamVeraSans-Roman /MaxWidth 1342 /CapHeight 730 /ItalicAngle 0 /Ascent 929 >> endobj 12 0 obj [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 318 401 460 838 636 950 780 275 390 390 500 838 318 361 318 337 636 636 636 636 636 636 636 636 636 636 337 337 838 838 838 531 1000 684 686 698 770 632 575 775 752 295 295 656 557 863 748 787 603 787 695 635 611 732 684 989 685 611 685 390 337 390 838 500 500 613 635 550 635 615 352 635 634 278 278 579 278 974 634 612 635 635 411 521 392 634 592 818 592 592 525 636 337 636 838 600 636 600 318 636 518 1000 500 500 500 1342 635 400 1070 600 685 600 600 318 318 518 518 590 500 1000 500 1000 521 400 1023 600 525 611 636 401 636 636 636 636 337 500 500 1000 471 612 838 361 1000 500 500 838 401 401 500 636 636 318 500 401 471 612 969 969 969 531 684 684 684 684 684 684 974 698 632 632 632 632 295 295 295 295 775 748 787 787 787 787 787 838 787 732 732 732 732 611 605 630 613 613 613 613 613 613 982 550 615 615 615 615 278 278 278 278 612 634 612 612 612 612 612 838 612 634 634 634 634 592 635 592 ] endobj 15 0 obj << /period 16 0 R /one 17 0 R /four 18 0 R /zero 19 0 R /space 20 0 R /six 21 0 R /three 22 0 R /A 23 0 R /C 24 0 R /P 25 0 R /S 26 0 R /five 27 0 R /two 28 0 R /V 29 0 R /a 30 0 R /c 31 0 R /e 32 0 R /i 33 0 R /k 34 0 R /l 35 0 R /o 36 0 R /n 37 0 R /s 38 0 R /r 39 0 R /nine 40 0 R /t 41 0 R /y 42 0 R /eight 43 0 R >> endobj 3 0 obj << /F1 14 0 R >> endobj 4 0 obj << >> endobj 5 0 obj << >> endobj 6 0 obj << >> endobj 7 0 obj << >> endobj 2 0 obj << /Count 1 /Kids [ 10 0 R ] /Type /Pages >> endobj 44 0 obj << /CreationDate (D:20111127165114-07'00') /Producer (matplotlib pdf backend) /Creator (matplotlib 1.2.x, https://matplotlib.sf.net) >> endobj xref 0 45 0000000000 65535 f 0000000016 00000 n 0000011672 00000 n 0000011556 00000 n 0000011588 00000 n 0000011609 00000 n 0000011630 00000 n 0000011651 00000 n 0000000065 00000 n 0000000315 00000 n 0000000208 00000 n 0000002095 00000 n 0000010166 00000 n 0000009951 00000 n 0000009458 00000 n 0000011219 00000 n 0000002116 00000 n 0000002237 00000 n 0000002389 00000 n 0000002551 00000 n 0000002834 00000 n 0000002923 00000 n 0000003313 00000 n 0000003724 00000 n 0000003884 00000 n 0000004189 00000 n 0000004427 00000 n 0000004838 00000 n 0000005158 00000 n 0000005479 00000 n 0000005621 00000 n 0000005998 00000 n 0000006301 00000 n 0000006619 00000 n 0000006759 00000 n 0000006912 00000 n 0000007029 00000 n 0000007316 00000 n 0000007550 00000 n 0000007955 00000 n 0000008185 00000 n 0000008578 00000 n 0000008782 00000 n 0000008993 00000 n 0000011732 00000 n trailer << /Info 44 0 R /Root 1 0 R /Size 45 >> startxref 11883 %%EOF ipyparallel-7.1.0/docs/source/tutorial/figs/asian_call.png000066400000000000000000000562011412533144600236630ustar00rootroot00000000000000PNG  IHDR XvpsBIT|d pHYsaa?i IDATxytTg"% XdQT@YT@ X)"T] %UE\wl-"-!AD` [D$%&,s~_3sgIi;K(???_(Dg!@xg!@xg!@xg!@xg!@xg!@xg!@xg!@xg!@x~(=#wﮨZXzlh hֺ(mڴ)l v_c%%%wjҤj֬6mhZx+';اP3gNs#\Æ Ӿ}?Imڴ޽{}/R]vz-Szwtk۶mYSRRg 8P:u**U~ܸqڶmwǫEշo_=裥z|`ܹsQFIڶmviʕTӦM ݲeV\:?/r_٧|#GjҤIE3h{ckx@'N駟.I>}O<'|򔏽aIRΝOylq=w!I999jݺ5rHիW @EpURJ9rdGV~~~S^*Ub/&&攏WkKnF5h@V*vњ4iRA|HҐ!C$I?O[ԼyR_Ƈt1yd>+}PQ\R[lQϞ=ՠAˇO?]))):zhW_+Vm۶fq.]Z VTjUرCަE¥e˖S2=~yرCwuڵk:ue*!Fy\wu.]?ZlYRSS5k,jڴiܹ֭;S?)s8pK.Ѹq?QI&ڿk֬YW^]G)͏VZI6o\۱c:vGyDիWo~u]rwo߾2'k@Xw:tZq/X}5vX;V_VZg}V<ԩɓ'1srr4}tU^]_|E.O<xmڴ$}'e̙3wn+Zz(l(+5͛#G /Tv=fzm6}Eo޼7oqzJ ͛7+//OC )oh߾}~JQkOK|'Cjժ7Jn"ǥ3R)X1{lB!͚5K/B_cƌQ^^^geeԉnݪ#G(??lӦ>lXӦ+~HRlll 2D˗//^zIݻw/煜t~I/֯_`N`իWkjӦ.“w 7H?+//O_|ڴi;jԨQ2e>}( ;(qt7jǎjҤzCZRʕաCSFLE9R<߯D]p1b 5J+atgj„ ֭ƎFoָqNXn, 0gB!35k=zձcGu]RVXg}V999'$)))I:uŋ맟~Ҕ)S|rլYO*S~kƍ4iԩ>@>222ԡC-\P}QիWg}SjΝz뭷Խ{w-_\Oͧ I|spDҁ4eKRrrr>#3F\pT(}7'K.VǎK~Vڿ={.rծ][jR.]駟z~CHڽ{fϞ#GhРAʕ+{)>>^]t) 3fu] /P ,Pbb4i$~&n6uUzuW+'' ?bcc8MV=c2eԸqBjԨ맔?֭['mJ)55UÇʕ+ K_(Je˖iϞ= ]>zh_F?(duܙ@ l"I: ]5j(33,OIj*i QiРAA|HRTTﯔzaZ  .h޼$iݺu֭[YYY:x]ժUxI-jcecD.K)15b.+,>]J*߷n߿ǽDҹuf͚%S7A"tWk.N;ӳ@&B!=Z}>쳂sssdnݚ׻F0΀l:x`i 6(==]jժiZz$i7|SuQݺuյkWI ȴi4aEGG몫Қ5kt}i„ _? P~ >Q&N4 < $1=A@|v )czE_8 n< :~@0p2~@p @P ؎_8v<41=#>`"@ !>pL  czlG|G#>DpL r czlG|ʁ_8f8؆_8f&1=6#>F >l@|/`3DG|/`3qg1=6#>_8f_s@!<'88f!@ >'$< ~[J3 i1=">D|O`+@Y )1=">@|O`+@y ׈czlE|(/ "K8F"@؈~…F|O`#@8U6=4~-1=!>@|O` ܈czlC|p?qLm^ @p!>' +1=!>x qLmՁ4eKRrrrW׮]{~EczlC|~(xٳGV҃>x㳲4uT=3 czlC| \Oy̍7ި^zi, *X_8&6g}ڸq)(+~5yd=cW9( ~8w$T>ǎk̘1' pLMЮԡvoEEe Vӵb }G.tO?{3P)x:P1=6!>`ʆ K.Dk.ٳgVZz7 )d'p*1=6!>`ҨQԣGBG4hn6wy4( pLMm:x/tIRbb4i&M{ۆ k׮m  ؄ƍ:iiiJKKS(Rff7nlx!?sLMx%33\ ؈1=6!>؂~8& czlB| #>V$[lF1= >؎Ad#> D.~[Ad"> HD~[Ad!> czlA|*01= >@|4؂tM\G|4؀)8`#>`czl@|$i  &9HD x"`!>`czl@|di  9 9C@E8q8F^LqL (OprC|؀ S`N=P:Gx؂#@oLqL (;">`czl@|@pD|؂   `ƒ?01=6 > |G|؂5 0 G&8` ^sL-pg@=^sL-pg@&8x7x1=6!>;G|k5 8)SW^UTT=6##C R\\6m#FhE*Gq^sL-|JwS^^q]y啊YgÇ+--M4~AHڽ{fϞ#GhРAP(T丯Z:uw}3gjZf:u"׿֚5k }17xR#>+M999{ԨQ#=vںuWȑ#&'x xٳG?hΜ9ѣZbbbb$I:uR˖-5{l%%%:^z袋Wkիk۶mSNe{VÆ 5uT0`g/b/5`͛K/ռyJ}_G|KE X=ozQ'['**P|w<:233]#@JWNN?"׵iF[l)r/իvҥ-ZTsz\L|;7Dz|| I=A%ؚաC dذa5kVXɓ'+''G\sq6{`6V吕;S=z_nz 5 ._Rϗ]vNs=WsnFy%)I5~qY|`:})tl_P׉ՀTzuJH)5lP}]vZj^RwJJJեK ezW`n祡 uhBgݧ1g$*͇u.WW^yvءիWI&SX)uFZn]֯_SjV">؄xUٳG={7|{O[6= >@RTTFW^yEg;222G.zUj`W`Fx_vvz쩬,j۶I g˗/~I҆ .IJLLTj4qDwJJJҏ?ӧ+!!Acƌ){L_} խ[W,X-[hƌQ㗯868S$܏1c~'Y󿧩խ[TA0 ?7nn=P(4) )33S7Vf駟j?~T]'Tttt}lRK.ҥKXwyzիO_H^7d)//O t뭷Qx1z#@~VIHH…%o߾۷o8f8&@|Zin` lDԈx1=6!>n %#>؆@w$t/86Ex+ 7F|  p#<sx `F|f 8`F|,pczlCx H#>6؆@ `F|CD"nsLi1=!</$pczlC| HA|Mp#>;9 `#F| HpczlD| ܈.HPpczlDx `$1=6">n`/^47$7O nqL nqL b3nrLb+nqLb#nqL 8F 78V"b npLD΀8F@|3npLD&W[7\]czlE| ܈ , `3F| @ sL8czlE| ܈' 4p#>g@ qL͈81="<np؊@N3 `3F|( ΀rL͈81="<n"@8fP< czlF| ܈E6pL͈"4VЁ4eKRrrrfddhРASӦM5b޽+جOxc IDATŠqwٳu 4H _SN4sLM>]k֬QNl/B1=6#<nN={H~͙3q=zT+VPLL$SNjٲfϞ$6_8#B| ܈$??4| 0 >$yK5o<&‡8czlG| TEkӦ^|Ep#>PQ۷o$թSu:t萲 A @`;F|DR[/ۻ7^ﷸD߶=ܷ?4—eݧ.XSkذ$vRj8؎@8uFZn]֯_5،@pCTTFW^yk=222G6& `3F|/_jc/ٰa%IV&Nt[III5}t%$$h̘1&0czlG| ܈TĸquVI>=--Miii BTƍլY3}駚n @78f'< 7،@83 @89f‰W9_nݪQFAھ}jժիsL vf͚G* wjРƎk'"`3FxOzꩧs{=\קOX:#֧BaG|g@RRR4x`͜9SWFFe$؎@lc}ddd(99rssO?ypFHƍj* 0u7on`1=#<={*55U#GT۶m .߼y-[aÆ\k9vBx/B>}8 ]xᅺK%I7tZlʕ+kԩ*T3 gy 9~[ժUSNNnw}:3LO BxD:!O<czAc}>|XV5\w~: ,9 (Tȍ7ިÇ+55u7tV_|2czAf}^Z>`OwyNjK n#>k.uY^;wz !>&@>@>lkݻw-Z X$F|$HΝꫯV&M .oꫯ 0 "Dx+ )!!A;w]wݥN:iӦ B'}}">6@ H\\֮][oUVҚ5kTZ5%&&ꩧR\\pcz׮]P(dz!< DžB!խ[ 1=ACx+ceKJLLYgylȑ#=Z8 x¬ QFi͚5:묳4zSOX1=ADx+eelٲ[l1pL@ 'geK>M6s- cz85+J*z7 pL@ c}iF?tWrL@ ec'_wuzuaSPVy3 O?) N:+ԹsgUT1'N4rL@P g}$%%%Kdɒ" >"<*mx- #>9!/~־=55U:tЙg֭[W~~Yxq9\էB >VYx.IjѢ6mڤ?:t^"czWpg@x jJ7nƍߪ_~9s&gALpD|U7<W_)))I-Z$կ__~-[۷QFF/-+d׮] ]֥K9 x@E_n2"IPQQǞMS\H@| ¡MOdmL>^{$3gN=:zVXQN:e˖={$I?O#Gjƌ޽{+??_>&O x𓝂cz" 'uA͚5Kݻw$OZ`^|EUZHg@~do㛗k}yK5o޼˖-[={/tGVnn^~ewƗ#@| \8qjח$<66VguN?t`{/r]6me˖/W5ؓqDx3ӭNG\s5jҤ&N?P۷o׋/4r- O*۷KԩSX:tHيURBgJN<}yوx@8e 6G֭[su]gplٷoKmݺU{VڵMO*xPD|‰(L )q~B!oSڵu嗫gϞ뮻* 6brw׮]Zj ѣgD~y:ʿ8{Ro豯pJw.@x ˥Է _?L* ](Z wjGG.;7c{9_^YYYjԨ$ꫯVLL|AW\\\ _~Y'OVbb\…  uU5nݺ"׭_^͚5+y撤u:5*!qSRf@ypY}}h J*nezYݢO.H*rovq?~[Frs.,gf̘>}hҥk ]׼ysmڴ),#Fhҥ>###C}QWkVJJJHIIiÇeS!xx9S*I&/7|c^8M̂E?qFy^Wre8pT|r#!!Acƌ)Ujڴi0auUWi͚54a„ '|w/7#|&M7xCku_+/A>@ׯkDWVӦMKu?ƍ֭[%4) )33S7Vf駟j?~T]'Tttt[UV-=s;wZhG}T~{hx˴n:-]Tx SsQbb*Udz"|K/k"w޽zգGROi?#!!㩌1B#F(ձ >6n <ӺuknBJb}<:ԤI 8P+WjժrpqL@$"<6n!>>@ׯ0ax Iʕ+ug)HDx @xg}qOڵkԩP($[\XND@x-`䷿,YRm߾]W\q222L+L@$">6n!<X |Ə~vҕW^*UZ؁c},YDW^y5ydIRvvzÇ?4'^ >ҥK͛7OÇW||*]uUڽ{>Cřp!C(33S#Gy睧oF~?FxM I;e-X@WV˖-MO|YB . BÇueI o>SS x k{^ p@0Y )))'ExmFxez!>6n#> |۫f͚Ny|׮]=XCxmFx޽֬Y.Hݻw/P(Gz3 D+dʕjժU?x"rѣGXժU(#F|  _JK,Q>}L\ExmB|9ԪU+m۶5Fx+)o;p@͚5 4V 8πHR6m-Z(11Q;wVJ 3x`C!<6^!<2dȐ1cf̘Qzކ6!<6^">mxDxKX Bb}$$$hѢEj۶m֯_h˖-%#<6^"<~rrruV%#<6^#>Rkꬳ2=Dx}Fx(+d޼yJII)~ܸq:3 G_}:0^ >5@yY Ԯ] ֡C w6mIAxk2@ƍqI㕞v^Cx L >r,IAx@8Y 'Ւ%K oVBBY8^!>` ܬ ɓ'ko ]޾}{mذ^zI}#:%&ez@y|'o~S買zK6lȑ#9sHqP}.$>z7Yyd˖-0aB?ZjzꩧM/;ch%耗Bxg@8:u|aӧbbb .ڵ[׈B|g@.b}%I~9K.qժUS(21#:5fe\uUZhƌڵkk޽{:.##C 401"<5&L2@S*!!A͛uWM6[h:vhh%lBxkL#>bejJyݻW'N m6޽Ӂ0IӬ I֭uv>l_ESDx k( &0GiDL"< :L!:؂ՈS!@`~At~_*99Yk׮Վ;ԨQ#9Rsi9V :'L!< .Ԉ#ԳgO_222c`Eto@toۧnIRpyݍm] !:` 5k~jr#@`"<` *UhݺvZիWOԽޫjժ#@9~Fx6P(R>}ԯ_?MGt h >ԹJEw2=+~@xyWvD!@  <FxO7@@(~Cx]BxoD :G?"< X|5RLB_æ"@CxX*""<{DV`D+ _#: @3Fx!@;Fx#@K;EtF e"<_ @ _":!@#<`DxE>Dx|@>Bx|B_!:k DlBxWz6"<D("<H&lGx7 _ :ADp hD8 AEx8h3AGx8D0QD_2=6տ)..N п"Ǎ5JQQQEZnm`5cqt׽M|E|D9s(**J5k4= HlܸQ:tPƍ5uT駟VΝrJuСժUӪU\od 8"5ydi߾}Hy:z>3H~ߪaÆ>}/^\J*颋.215"C6vXC111JOO7= @:w\T~}uQvڥ؂M̌?ȡw{L=Xkժj…nr$}WzWN;̙3G :(?!;wOWBB?oWJ EtAx\ N4s@"@sѪUNyɧ IDAT\LL^{5y‹xdܹ;w;S !8wpH#8op"H!8pRH`9$` ":b90DX#<:@ >Gp@tuDA55әg'_ <:@#@@DBt +L/BT ; G D$CtGaDd?#<0>PP DxG% 4R <0DG{=KOco^մiS`ʉ@si۶mպuk}JIIw߭?P;v4=>G b@fΜ |߫W/ 2D zh"`Ch.!< G5jH۷WffE +G={h?S`Fh}_${16 @ =ܣ%KgU-Lρx|@@Dlx|^ґ ?)o8x=馛*$Ž,Bx "6<¦_'(iIoӧ+99Ywq4ʍ,Et s>%''kڴi`z,CDD 0'дiԧOGk֬)t%\bhlA@ :cٲe BZ|/_^P(GZ[ " d%O{PK_-6G]bkɒ%߿裏*77B z/5h u]K,Qyy֮]x}4hcs)>>^zumkذa*((P.]}Õݻw;/~ :6lkѢE93Xĉڳg6lؠ9sHƎJK.% '(>>Heee$}7:t$ʕ+2d^*I P@$I_\hƒM^@GkO壎C-/.oIg-_QuO۷o[>[USM^t9zU}{߫6x -Z)ShƌzWT^^UV#Ii\9/\<]sCC-' }.^_i9lh 0i$;Zx#ͦ\?O[o)**J.I.o[\\: ֘1ceѣiM<… 3fsO8QԧO}իXxxj;jC4ŋ T@@Ν@SԵkWuINӧO+22CBB4k֬&]?pxTK猊ѣG.I#Ɵs*|aC:{o߮W^yEm۶$v͞=[{UII5EEE:zu-MRRnݺ>1fQVVbbb4x`-T+;;[*++ƍ۷O۷wl[TTxC-r|fSAA˻i'OV^^|M[cea@M8RڵÇs窤Do8۫\K,Q5dS>>>ztR 6LǏ>\ *!!A]tnWVVV8yƎPEGG^ȝ7wkG}Tn}j9j(jqƹpP7n3qDg֬Ycof̘a|||Ν;k}Mhhy'̙3f3YYY.=v3f޽w5&..Δ7ayLHH0QQQĉNg6!yDCk{1n:sn"##MPPpӶjzҘ>0/ٱc9tկ~en7vrږt_-[C_6g___W{$P776lܸi|ԨQ_~ɓ'k911DGGo1fvhFeBBoޢ1u~zǏf.\4NOޓ4ܸqCwї;x3p@pB3j(j = `IVV4m4$ɓ5f96%4sLk1>zh=C^ՒXQlR5>`GQhhj$zҝ=)5s rїeoKwqժU˲]ժ$PW79}~Gu~Ш .T{.&&FϟóRTT`iZzܹ{xY;=ZV枔SJݹsGZpsѶ9TJJN:;w*88ί'ݯdݬ]V׿={hԨQڰa&LPӗ./kf)''Q'p xXxx[搤7os4VԲ&iiiTk#wԱRںu6mڤ'|=ZězRrO-_|Qyyy믿4D_5lh/_e˖g?|||TRRB*++sڇ$PW7RQQ?*O$EFF6zl6:usOv́I՛Q1FSVV2335cƌjГem'%?~>S}7Kwֲ&ԗ UVV (((8v옾 jɒ%ZOOu;oyO>l6i&XeeeIAAA7nӧm|q!3p@i>lΝ;ؾ5$PW74iرYfٷo9si۶Ӈ:%''sefggl[ofϟǏ;Dg{P3tPSQQaqZky3vXi8`222ѣM6m̔)S,;Φ:Ο?l6bO?iz=l-=iLkSOe˖?˗8cͶmۜ/SҗsSm$P7*//7K,1111K.G?ufΜ9nK.9l6n;}}hM^ /`n߾ݤfs̓O>iuf|}}Mpp:tOmh{T>"""CO՘Z4\z3AAA}&""L89r<]ek|ϹWmgNZCOua3Ƌ?]@›X2! e ,C&O,???ָͬYԮ];w/^nWVVVr5-_\Ne;Wom۶rJ}$''+$$D~}@xdljgA UPP$]zU?4c Tii~iڵk ʕ+o>͜9SwV^t5IqFIҥK_r^UrJI;㕖&jk믵}v}u@STBBl٢իWy6lfSrr-[R9sF}$%%%)66V/֬YԩS'9rF#4m4EGGkݺuZbկ_?IRϞ=]n%2EEEI[mӧOl2mVVk.܍3 ZJJn޼{J˵e1B={ԟ'EFF:G &[U***okҥ|||7oٳMz\Y`nܸlIRee2224~xuc@"rSLQ@@2ƍJII$]rEX7oެÇk͚5:{v# hԩ_k*::Z=G@JJ***\+8]2@Ը_;ڵKNc;w$c59i$=z?~ ΀z ?8SQu$kΝ6lԻwoYFgΜQFF:vX~SRRꫯ^SllnݪÇ{No,޽}vEGG+<<\_}M6Z~:wEFF*((HԦM=Zx:u9sԥT49΀hBGLLbcc_'xB.\(޽[=\UV)77Wiii*++tk]___wSee}Yi OBWnF5yd :T999NL>]4{l׺?b3|.xիWkէOO/I\^>ә3grJM2hV8^&""B;v~_;@s@`ބ2! e ,C`@X2! e ,C`@X2! e ,C`@X2! e ,C`@X2!g(:IENDB`ipyparallel-7.1.0/docs/source/tutorial/figs/asian_put.pdf000066400000000000000000000307321412533144600235460ustar00rootroot00000000000000%PDF-1.4 % 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj 8 0 obj << /XObject 7 0 R /Pattern 5 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /ExtGState 4 0 R /Shading 6 0 R /Font 3 0 R >> endobj 10 0 obj << /Contents 9 0 R /Type /Page /Resources 8 0 R /Parent 2 0 R /MediaBox [ 0 0 576 432 ] >> endobj 9 0 obj << /Filter /FlateDecode /Length 11 0 R >> stream xXˎ\5+UH)@F`2 {l_ߗKSSQ~AE;Bnk^;^0(ǥM)ݸ6eg]e9z׿O|-$^?P+0*zk|m Cp.w9D(:B!&$^$ Z̓jZ hfPL1WS}3j澙'6L):dۜl8.jQqzDqWbp@ӭĞ31(ݶU%.N66( b)&9OjQ & |hۤ}tjFfyFIv-`J7F|G}w$6l ]l&7l!Y_v#ݰD,7jiՈs%J21XV-o'P#gP\mdC5bS 5w\o~g29q< IO~X)K.̐s9 8 ^B 1:g#KiqR'h3YZY9#Zɰy;Ke90,|6-h^M\5u nZKٓ_u:O% v5ᥖqzu8Agx>0q7S~>TT@`R,v=޽ * ~ykk/k0_/+}#%0s*06魷u SȦޚ t:t]E.z@^A]0mb*:?ƻޮ뽷l oj;~g{P*m/oiX:lSw-X\:?CPBa=p!86]ks5u~fF3Dc7`JS+rmni:?CvX_-5"$t޺-2ho@>`XJ갻|Z(wOO/ǡ~Ua'w['$6q]U]|֯;񝛊~"gJJ瘕{{O*B,{| Z Syqr\wAڌ79;q uOR ~觃+\9?Vib8>%3@{RVnKqIuihs-6_W7r.3qt[8Lq=ѧ}0YAL> dƼ0'ZJ8x6O$% B :i$>?2C|s//%v&? h)$:{ܯkP,vǛe@0^ L,L7<7L/` !y Q fb(,T0np^dRsʔ嵤b suP"~@"Keyׁ rBHT-?[oB endstream endobj 11 0 obj 1675 endobj 16 0 obj << /Filter /FlateDecode /Length 49 >> stream x36P0P040F@B!H Y@8&+ & endstream endobj 17 0 obj << /Filter /FlateDecode /Length 80 >> stream xE 0D{`~&f( JpO{:2Sa ,S`5FR죰n_uzS*Ovvq= endstream endobj 18 0 obj << /Filter /FlateDecode /Length 90 >> stream xMA "OPDtz_NE5jK02kP)U0\ 2IL{qIqzz"X endstream endobj 19 0 obj << /Filter /FlateDecode /Length 210 >> stream x5P C1g dVukm;aBXȔy)K>:L." u%ʚ +`p&^7`i5tႦ.B%|u{OxjrvC` jMX> stream x36P0C. endstream endobj 21 0 obj << /Filter /FlateDecode /Length 317 >> stream x5RKrC1ۿSpΘ}tj'+-@B./YK~%ۥW%B>R-G- Q=2'":xa>N)x_xN;2$KMH=I+4t~&+s{rj X+)$=Hr7VސWg%&&MܕBXtLX㰄*aՃM5fcdxLP} #GMv²[6!D3,($Nc$ Ұ9 9e, mh%zМaמE[{ endstream endobj 22 0 obj << /Filter /FlateDecode /Length 338 >> stream x5R9@ } ] v͜~߆_ CVie!U-.Im W%ڥ Pt,6˯JH+kLwIi"Eo7o}=@.^ AS(i|Ъc(ew 4<3}(~_K&(? _osџa`Ś}@*z`yT endstream endobj 23 0 obj << /Filter /FlateDecode /Length 163 >> stream xM;0 Cw yRtjVrʐ(usf!-eǷO'庉}*ƀF;t{MXp@'<,VQ{"Iڣ g O㵦_)3HBdZqg{?83 endstream endobj 24 0 obj << /Filter /FlateDecode /Length 88 >> stream x5 0D{8R[.{9>G-dCxI9>Q4Zo:Hsd33}d4I!rY)z>~ endstream endobj 25 0 obj << /Filter /FlateDecode /Length 165 >> stream xE;! C{N#y6;I,%?+:cck'R: g]a2So\\`nN|愯U—L 0;Q|$ ꔍ0G@LM/yjAPkGkS4g*[ :L?= endstream endobj 26 0 obj << /Filter /FlateDecode /Length 338 >> stream x5RK[AۿSjy쬠8K Qry:i>ޅԂ"iǚ5Q4b)>(!SHfd2r >֛L9is(Iz-v|YAғu-Mk"925Ǟ2#A, IOH3;g-yx/~2U2 `; ("$h(fƚۅ"=D^ ,z2'; Jjaq6JS]g&a氳R2 qp/${1aļ]l\̹&ӏ(H\M(?7} endstream endobj 27 0 obj << /Filter /FlateDecode /Length 247 >> stream xMQmD1 \ky R]oC /)%K [UC?13,=?TPbht/"+ߏe s`&4`oI&ռ3d‰ATwM,3V7: lx%D`r Z`Q+ tĺv7C/਺x} K{,|BL;wI#fR:=b}@e+ (\* endstream endobj 28 0 obj << /Filter /FlateDecode /Length 248 >> stream x-Q9AzBsˑ C :-qPO+Uwu9HTM]vf5,?c 7zqxLu5{kOfP2+qSușO \ ȹeƌ#M!RH&3AQ~#aU#j \Ks4;<9GW +ET<pC7ҹ^s0XM7/=[ endstream endobj 29 0 obj << /Filter /FlateDecode /Length 70 >> stream x30Q0Pb3s3s#KC.# 3@.X 261R044L͍b0@YKA9P9\iOT/ endstream endobj 30 0 obj << /Filter /FlateDecode /Length 304 >> stream x=;0 C{Ȍd'>2VI(/u< i& b;w؞D/)ϡ+E:Ū0[M*K õ}74uK hY pu;Gw5<TQ!OJ|<(!\{0FS@\^BAjI'> stream x5QIn0 .AO ``hKxA[֌]< 'Q^{ .o8|^Z9O2 D`@ai'ώH5YN_KK(O~ J.kO8'O [WLcD.A|!]'@;綟Wuu)O645I"%Ҹ[{TS endstream endobj 32 0 obj << /Filter /FlateDecode /Length 245 >> stream xEPC1 =`,{wHۿ=JFp!Z?ZK oGFA= 3A΄@xFnvpμ39Zpә\'mBITqTqLύׁlӑ!KI%&~S*)[*EH䁓M4,?Cb̠Q0qGuٜ9-L|X&Q)2>'\N}䢥Uޑ"ۡW%Qէ<Y> endstream endobj 33 0 obj << /Filter /FlateDecode /Length 68 >> stream x32P0P4& f )\@B.H  %[B4AXf&fI8"ɴ endstream endobj 34 0 obj << /Filter /FlateDecode /Length 81 >> stream x=̻0>SB|[D|! nKw}zȀSj\Ni}}jKՉ?k endstream endobj 35 0 obj << /Filter /FlateDecode /Length 45 >> stream x32P0P4& f )\V.L,іp "} endstream endobj 36 0 obj << /Filter /FlateDecode /Length 214 >> stream x=PC1= |7˥m$B6BLɔ:ʒ)O>Kbnd6%*E/% }ՖC4h9~ 3*K6p*3 mtV[ Ф`׶ r " JMrR=ot-N=Dkq: DpFjtaŲC5=kz7hGt4CָR endstream endobj 37 0 obj << /Filter /FlateDecode /Length 161 >> stream xEK CBGG|tJ■!M@w'/mK >[ x6n5uVhR}ith6s+ fz:rGp_Gdf)|Q]dcnk]3s: endstream endobj 38 0 obj << /Filter /FlateDecode /Length 332 >> stream x-R9$1 ~`LtIUls#h/#xE=f۴[iGiK,W ;BjW0wy.2meDkag؏]e8*Jl !2J'Qw\I2[E™w2;yNE{ kF9+%|6vzrYɩHHӺ NKؖߗ3| endstream endobj 39 0 obj << /Filter /FlateDecode /Length 157 >> stream xEC1DsUA wJo-%S'"h0yM%V,&rAJ1xN1븨ufihW3=5'M<[ }@8IP1}bv">G)#qbn fW7y endstream endobj 40 0 obj << /Filter /FlateDecode /Length 320 >> stream x5Qq0 54sۿ @;a@dJ\UGM>`!S֖{&UF!}W2j]* UYFp&I8d Rӿccz endstream endobj 41 0 obj << /Filter /FlateDecode /Length 131 >> stream xE ! CT>՞0ABA";06Ѣ76իc,zRV鐇Pi0QąYLCaΘȖ2MlTv<e~ma, U^ ?KwUBS0 endstream endobj 42 0 obj << /Filter /FlateDecode /Length 138 >> stream x=A1y?)vBX޳UO_K^1BCoj NjK)Jș`gzb8V}F%hGSiܖq5)\W4ݴk8߽U__. endstream endobj 43 0 obj << /Filter /FlateDecode /Length 392 >> stream x=RKn1)@Mr[T /1 %?ꒈ3L~r]Qljg!.6Xr_rњbO/ȴTXVݣC(-װr{d`Jn@CHYAaPl( WԬtb ) ٠[]aP[[xfޑ3qYk?=Q2QMg|2RCgB'`$Ip#A 1qOl)V;ޒ{,\L'ib?lK\+E(~Aq|XdDw#h% 0xyDhDԎ=(ͱ&{ǫvzcw. endstream endobj 14 0 obj << /FontDescriptor 13 0 R /Name /BitstreamVeraSans-Roman /FontMatrix [ 0.001 0 0 0.001 0 0 ] /BaseFont /BitstreamVeraSans-Roman /Widths 12 0 R /Subtype /Type3 /CharProcs 15 0 R /Type /Font /FirstChar 0 /FontBBox [ -184 -236 1288 929 ] /Encoding << /Differences [ 32 /space 46 /period 48 /zero /one /two /three /four /five /six 56 /eight /nine 65 /A 80 /P 83 /S 86 /V 97 /a 99 /c 101 /e 105 /i 107 /k /l 110 /n /o 114 /r /s /t /u 121 /y ] /Type /Encoding >> /LastChar 255 >> endobj 13 0 obj << /Descent -236 /FontBBox [ -184 -236 1288 929 ] /StemV 0 /Flags 32 /XHeight 547 /Type /FontDescriptor /FontName /BitstreamVeraSans-Roman /MaxWidth 1342 /CapHeight 730 /ItalicAngle 0 /Ascent 929 >> endobj 12 0 obj [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 318 401 460 838 636 950 780 275 390 390 500 838 318 361 318 337 636 636 636 636 636 636 636 636 636 636 337 337 838 838 838 531 1000 684 686 698 770 632 575 775 752 295 295 656 557 863 748 787 603 787 695 635 611 732 684 989 685 611 685 390 337 390 838 500 500 613 635 550 635 615 352 635 634 278 278 579 278 974 634 612 635 635 411 521 392 634 592 818 592 592 525 636 337 636 838 600 636 600 318 636 518 1000 500 500 500 1342 635 400 1070 600 685 600 600 318 318 518 518 590 500 1000 500 1000 521 400 1023 600 525 611 636 401 636 636 636 636 337 500 500 1000 471 612 838 361 1000 500 500 838 401 401 500 636 636 318 500 401 471 612 969 969 969 531 684 684 684 684 684 684 974 698 632 632 632 632 295 295 295 295 775 748 787 787 787 787 787 838 787 732 732 732 732 611 605 630 613 613 613 613 613 613 982 550 615 615 615 615 278 278 278 278 612 634 612 612 612 612 612 838 612 634 634 634 634 592 635 592 ] endobj 15 0 obj << /period 16 0 R /one 17 0 R /four 18 0 R /zero 19 0 R /space 20 0 R /six 21 0 R /three 22 0 R /nine 40 0 R /A 24 0 R /P 25 0 R /S 26 0 R /five 27 0 R /two 28 0 R /V 29 0 R /a 30 0 R /c 31 0 R /e 32 0 R /i 33 0 R /k 34 0 R /l 35 0 R /o 36 0 R /n 37 0 R /s 38 0 R /r 39 0 R /u 23 0 R /t 41 0 R /y 42 0 R /eight 43 0 R >> endobj 3 0 obj << /F1 14 0 R >> endobj 4 0 obj << >> endobj 5 0 obj << >> endobj 6 0 obj << >> endobj 7 0 obj << >> endobj 2 0 obj << /Count 1 /Kids [ 10 0 R ] /Type /Pages >> endobj 44 0 obj << /CreationDate (D:20111127165057-07'00') /Producer (matplotlib pdf backend) /Creator (matplotlib 1.2.x, https://matplotlib.sf.net) >> endobj xref 0 45 0000000000 65535 f 0000000016 00000 n 0000011570 00000 n 0000011454 00000 n 0000011486 00000 n 0000011507 00000 n 0000011528 00000 n 0000011549 00000 n 0000000065 00000 n 0000000315 00000 n 0000000208 00000 n 0000002065 00000 n 0000010064 00000 n 0000009849 00000 n 0000009359 00000 n 0000011117 00000 n 0000002086 00000 n 0000002207 00000 n 0000002359 00000 n 0000002521 00000 n 0000002804 00000 n 0000002893 00000 n 0000003283 00000 n 0000003694 00000 n 0000003930 00000 n 0000004090 00000 n 0000004328 00000 n 0000004739 00000 n 0000005059 00000 n 0000005380 00000 n 0000005522 00000 n 0000005899 00000 n 0000006202 00000 n 0000006520 00000 n 0000006660 00000 n 0000006813 00000 n 0000006930 00000 n 0000007217 00000 n 0000007451 00000 n 0000007856 00000 n 0000008086 00000 n 0000008479 00000 n 0000008683 00000 n 0000008894 00000 n 0000011630 00000 n trailer << /Info 44 0 R /Root 1 0 R /Size 45 >> startxref 11781 %%EOF ipyparallel-7.1.0/docs/source/tutorial/figs/asian_put.png000066400000000000000000000551041412533144600235610ustar00rootroot00000000000000PNG  IHDR XvpsBIT|d pHYsaa?i IDATx{uu ͈ƐMCN )0lZar[+ȲlbժfV m[)]ʘԖ84pu5{=G7|ǽ;>Xox2 ,C 2 ,C 2 ,C 2 ,C 2 ,C 2 ,C 2 ,C 2 ,C 2 ,C ȐСC^ !zO>~~K>YF( ~UPAZԩ~0W9e'%%g)g^s5ڽ{˺<|>=,;zwj֬Y#1kj/$=4h:իW{'tR 2DSժUՠA_[n-bg@KMO/ի'В%K~M>𚘘2  \W^QNN%I7pnFmذAjРAӆ ԲeK}E>ѣ~lz5zhwӜ9s|݊+4k,7N˗$͘1CӧOs=y._ÇU\9x㍅tmxxx mܸQдiʴ.-X\yXX\СC_5@ʕb?/""3::س%ÇWڵqb.<<\Ǐ/I߿$iWbbO5o\CUfR}V#@҆ o>uUk.|}РA*_,YpVZ4sLmٲEyyyk֬w߭XUTI~_+Vw}bO>E¥I&7|S/I1c,X'O{z5eʔRV#@ҋ/(I2dH5jP^Gk׮ xoZprrr4m4mV5kɓu~3f{g}֭[kȑz'4}tկ__'N(6lX+WٳgK|S^^o>[N=z(`΀pt5` 0^|Es=\F#Fh޽ڸq^x=3TBBES4c U\Y_|E%;;[=A?1ʕ;O:U佽{Z  \gҥ:{n喀C[z{=}eQFjԨz_d|WSCy?~Qk$I|4i믿^Q8xb|>-\PrKDFF駟VRR'On:{eռysխ[W:{lᙎ={8n!o+??_>b&::ZںuZnmri&}Wj޼EC~_I^~e/PժU+kĉիz!ϧI&]~ >\}ׯaÆsjڴʕ+-[Vݻn[~a5nX={ËnݺI:wQFi̙zW\6$ϧz5lP;wַ~u֩UVzUB^ЩS4vX}Ǘ}Z3f5|IRݕzJ En %{S M2E˖-+ x \HLI}ڵ:r䈆 СC^{Lkp%>|u릾}Z"[hzLLTt;`$}ڽ{K &8xQ佨(OWvܩ~ȑ#%I:u҆  {GUzu-ZH7ng}Vcǎs9b$Og_!Ej%{p1JlN:)//ğ, )m-?Gg8SiCyKA f{BW*I0s4NqHpK+i @ΏZp'Bf`k'"@r877+<.b!D8 F͘h' @܊v"@I܈x!; _5NԩSu5h֬Y_5jhҥΞ=klBqVn,؁3"%111:r$JJJ*rMʕ_+22ݻN:2e׿o߾B0phv Dj}G}jʔ)JOO7{ip0`lς= Vck?$hNL@`*"p"噆32224yduYwqˁXpVVa[`ϿΗe۷*W_~O B,}Pjqu_B҅~/jYVuΏ?;S}6mڤCjlTn,XA(9r䈺vWf^c0[0ѣGյkWeddh^0[08ѺutI8qBk.Hz)s?>s͟?_?lR5kTll # p4"p "0ŵ9Rt)JNNSzz'G-F0TnALLC%y`^^+ p"pgL7#@ZLELE`B^`?N %LF]0F阀 0= fa" ,bn,;fp2`آc{ƶ,NTNDFc[!@YN9<Dg@pVN9!(S8i; epVN9`%(!"p2"0! Y8D`!LL@2`*b{D Y(! ,lт= e,FaʂmYʊ,|4AY"Ec*'b*`"J3 8+%E@iJ!!R8 Pp0"NB LC\iiE(@.AIi\!x!``0 \S!!x!p !xBNA LCo @D)B0BN4`.ārfW{ 1A0  =GU{1D4`iA0 4 4qDuMQQQJLL,ڴ4OjР┙Y:_gy&5!0!' B "prv/ 233xbxׯ\w^mV 4Ђ ti͘1Cmڴ'|~?>ൺu΂)ApTc+WDHBW7)jm^ "@$ȑ#Ç+))Ν\F6mԤI-^X&Lꫯ֭jz %!(!˰že˖o߾FoҥKKYF|,؉!gCw!@J~ЩSԢE"5o\+kʕ+Fj׮VZeڈ܁)J"##3gѣ 8P .Tjjt)w};w)c30 ݈8bW_}۷o)SԩS5|pUvX:F*x]u8;qH0~O~  %TNI*RJB?&L;v]v-˴^Bf D`'#FRܯm^P[Jf͚RmV;v(66Qp([Y0[`Ά l)!߯8Y&GZZ6oެK~}~~?bŊd73! !`!3g֭ɓ'u IҮ]"Iٳ*Uq)%%Eݻwׄ  Dz̙={K.Y7Ծ}4|URŖmY0۲`'ex!`?g#G%"d|>^zjذ(!!AGV ԡC͛7OᅟդIYpZ믿^sUn "0!pH}X\Kz2bY"D`Bvab؃3 :LAu؁!k (D @ D` ,D@#@P,Bf`;0 A0ĉխ[7EEE+11kԯ_?EGGA+D @/33S/ٳgկ_?I?dy޽jӦ[-X@3fЖ-[ԦMg{ J${T5q,X["ܲ7PLL9"I:|nܹUjj"""$ImڴQ&MxbM05YĘ,LD`5e/??l2۷0>$QF۵tR"@PjBjDJ)~ANR-׼ys۷φU)jLC "<(I,^TTΜ99#@Pf@jDJi0T8+qHzjk82zR^ x1I*ԩSGCTR!*"0!+"_<_<[ªf͚RmV;v(666Eh` L,Y۲~i͚5g=Ҵyf۷8؎U8n:hڵ]v)%%E))):}$iܸq*Wwﮔ-[LwubccCٹ|،-XC`eJsؒlȑڿsOAONNVrr|>U^=5lP?ѣGB С͛p؉e8" gCsKt]llV\ij6lؖp>Va[mU(=#D`BV`C1(4fa+0 #@HB \G#D`BV B(@,4 p"f!B`6"!@:LC`!0 @bBl/#@zBlD/"@2!iDL @AH#D`!0'"0 !3"BO!B`Bf"B4f"D`!B"D`&Bf!D#D`&"f!BY,LC\Y&P !030 p 4f!D8!!0 i4 @)1 Y$$Bf!B`" @"0i Ab IDATYv!@!0`50YјL+ f"@ !00 `iD0؄шi# iD08!#!0,ai4@HD(-p0BF"B`&"@I "0 i @!B`"f"D\  iF(!!0*gw^ڵk^*WlDH?y%pTc+A*96BbciӦz5uT}w#F駟yu5LC`6!\ h"׿￯zTWXmY0gC`6eYd^-X@۷x/&&Fiii6 Ro{p1"F B`6"&HZZxbO?d D i4Hzqb[~5jd F ؒ!0!xk׮Z|n*WW_}kqu!BPFD4V B1cJr-%I<4iriʔ)6 ˷"(!0 1 q]v)11QmڴW_jٲe:sK >@U/Bcƌщ'TR%:uJF֭[uWڽDc!(#"F`+!δgnZoﯤ$EDDhȐ!կ~e! jժ瞳{Y]Uxx! a6`<))):y.\N:Iztz饗TbE{ Gs$;;[YYYžlWd&!0iƶ,U$)***(]uU*_˂>@Ç#<_B 8- V Bw}~7n># [yyyzlZO)66Vm۶?6mڨA|zꩧ^=0+!_ 8p +WJ:;Httnݪ=zh۶m={o߮={>Ai4\ǏSϞ=5{l^Z=-Zn Ÿ>@$)66VoGYYYZfbcc^ؒ0 X1Gm6 4Hoy9sFIIIzfp?~>ϧ5kڽ g: u;e R7GfԬY3M<\ ճgO]uUZt|>%O111p: u;e*R)>}h޽v/Ž;eHD-{>@7o?!0 X!CO^XC駟.]m۶E3n8VB% :ĝ`r}L0z-[E!@J]/ĝ`n mxMB`LC`$! xnk"0 *`B_|ZljժYfz'oBa0n #q,XC@ٹ2@V^A/PttKմi^Z"B`"F"B`n +SӦM{n޽[|z 01 HLC`%"+dϞ=:t7n,IUƎ'N6XeNX {2@:֮]`"&`#1 greH Ga pކ7!!A˓$;V׮Yҵy I]/sC9\ _y}]8)Ixp!L a4sedddؽIHLC^=b,M8QݺuSTT~6--MStt4h8eff{eԮ];UV7o?3p.&:")33S/ٳgկ_?IoڻwڴioV ,Ќ3eiFG v2dnٳ&L[3يDDĝz܂e9rDta%%%{ܹsmڴQ&MxbM0Ati͘1C%IݻwW~~z)%$$vd6cKL!`& m|l27[5߮KvZ9rDÆ C*''G9w"&!0iX)~ANRE~͵o߾ĨJ*JOO7wNCD ш0RB$EFFy/**JgΜ)<rALJο5kT*Um۶"رC 6,}F$ȵ:ybcc/&JZplɂɘhLC I3zr\2|Ck֬у>^F_}~f͚g~i͊/|W^Q,YK,W\A@Dlɂ8e-Xwɓ}\r*[N'Oԉ'$IvRJJ$gϞTƍu]&L(|GllzϪXM1c(<<\wul٢3gj̘1UVÖ,-Y0 >@jժ[{-ަMԠA}ȑ#~I瞂d|>^zjذ(!!AGV ԡC͛7O裏zZh^y5nX>Ǝ[:!0/8q}~z4z[;vLo:w\)9bccr]] w;!1 ј@ ȬYtqկ_lEbb>} Dq.&l(9HZ;#Iڰa}>8.@DF~ ֩ST^mQRdd|>sqB`Άh KsxGTTTa|@ڵkKWJJt]w)33SׯWttKXј9?.IWzz믿^|Pex^,uC&MR\\ݫ{OM4{I0Xi4WrRjU|_|>);;[۷$qeVi/rew}%alɂGU#B`?N"Bx+dɒ%v/N4a30 !sĹX!0w\9uM7jժ/{},XI,4fa[Pԩl٢[oU:u>O, X!0۲"WȆ Դi.i,4fa 2@ VTTWn\D,4f` Tz^^G[l{)p:bPY8\ W\q6m o'D`Q;ep3W$s=Zpˀ!"Ҽys:uJ7رc+W B`1!0;ܹS6lʕ+qƚ9s˂ _6(PY8+\Rqqqڵ|IժUKiii^\mxQ& wފ]ڵyp2WҥK=9Rժ?ڳgc2؄!0CӒcCƠ>wEVxX֩SG;w=ޫk.ɕrI:tGՙ3g?iӦY<"6a31 Eky۾P\{*55 _B+π9R;wΝ;U^=^xAk׶{p;΅F 8` 6LjժWX!Ijժkrr `0 nF 8P/;OWVrro%\9XB<Ə}ٽ$&!셙e/Jcٲez'gk۶mJLLԟgpe$$$^zE^馛t7Լytm!0!0|_}g:qℶo߮I&w_-a1W?/ˀ}]ڵKVZZ$Isεcu ͘LLCɕo߾"?RJ hذa0`6l`* DlFLD32@Yl_^=zPDDD:t7|c%Dl4fbh nM~i?#={V[RJ܋ BDD0+䮻ի?J:w'I޽{uiii<\iF(+WH^'(66V]w?;T[jÁ831 P Mjj׮ /hʕ|T߾}mZ%<0 وp;v쨎;^ujǎONCdSa\9\II4@I و8[`6Ά( !B`6" U81 p1`%"–,Xp!!B`6"DؒevB–,X@v#B0D46p"C DMD-Y{I8+!w !p "V`x8bKBC!.p2"E lB8bKB܀! t [!p0"V!B#@7Yc! 8B`%"p'p+"F*D>fDUؒ cKD@!  T!p8"V"B"@PBؒ+1 B  B`%"pED\A % V#B @([+!؋ ![ W!pdjD`=".AJLCk !p "V#Bk !p djLC W!p"V#B !p"Vc!@U8;!q!p"VcK` !p"v B!@Z% v`P<".CDPz#B2D ҈ [G:IIIZK  J !<EGGٽ[s1B;wV׮]or!BRD`W_}U}y%F(=".E5f=ӊ{9p@p[c7Qf4b)gҀvJj*v/p)[c>%%EomfeS0 !DBDBE*RLIՇUEҨQ裏ꫯѣG%Iْcǎ\r+˰2`0[RlJ'33S?̙5jzuIU^]qqqv/qؒcLڵqƀg~駟|w}W6NG0#B˫P:vXW^yEaaaС  ؎c;ǓQ"s!p1΅+q&<y#@!`,[8H)V>}hW׿\/_WflX5DBemxKajٲի)S(''Gնm[mذA-[ RJڸqcܪʆ)ɓ'+77WI<:uhƌZzuaaa[X*\DB@$c V)T۶m CjժVZ֡CϷz;,Ζ,d@ p)y=<<\yyyxӪ]*Vf͚iʲh !DR ͚5u̙N8M6I@-Zh̙8qbpլYS>{/_^z'4vXS.A u8)kV7nuZb+BCg@8gBbxf#@8BA 0 Ew!B^Bp"!i @Eu"Bew#Bˆ~DB  BƖ,:8"@( @"!i#@"@p+@hZAc!܄xnA"Ap:w!!-D<D!B!D!@x!B8 x)FC8  B1D; !"](@cv @|D<`5.Dc8J!@!IDAT ""D0|;!!B"B1N`J!D@i! "Q"B`! "@Y PDٽ ˷KZؽ r٣۽ Xh]vu+66VfҪUl\܀ Iw" !RSSagg &#77Νu ?~ vzܸqp}<~ݻ~{׭[7\:ZM41j֭ڵkF <==qQt:=zǏnݺ޽{>j5t:Nq/`XVˎc6mϛߞ*q='O(>Ǐ}V[azmjʤ]V^' *7o磤V`cSpAl gJ (Ʋ^NF/L0~:~mE_}ư~bRcƌ@$ 2~!V^ V h4`hҤ6ln݊;$""Iڵk6lc=;;;GΝ4 ޽kmٹeׯ2e ѩS'@˖-ѬY3\x.]پ]\\0qZ?ɯQQQjq)%8CT'WSǎ<ܹGÆ * &MSN.YGDDD Ǐk.CB^\GD JW\AZZi&dddBƍ ر#͛'O ..$!''մ^cǎEff&VX.]`۶mHKKݻ ˆ# UШQ#8q2e b 9r7FII .\nݺwԩlll0k,,^CVVIq--ZJ^!ΝC`` j51c \y6KGGGx TSUT*crT7{ {{{ 6 fɊT%!'ѣ2d4 4i^z!66O6=|Ue}e}T*r/LKKÜ9s'޽{HOOgA#H6Gvvv"!!A>|X w w1VСCEXX$IN 6Z-|||DIIImE(K___jř3gyyyKQ8z{{u։#G8&ĵk׌eNBT?vswǏk֬*J۷h[|yY8… VnZtDD&B$i&v???ѵk ߫ ϝ;W怀.>}jh$Ib߁:DX'a-j{eee IDLLQ;sRXZ{N QXs}Ç1/勥q|ٳ~~~fUrK&Ä ÑsΕ^I EDXX5jdhG*\:Q"UuU8T߾}Vp˜/e9'4oNNN6|,cy)GWZeXm.V%'*L jMQv޽k^^^((0׍,ggg١{dzgdߒc^^ 9)_,XsNRٳgALL oL6:RX漬isss#11M65M}I}@drmtjr<淹qN|8233<>;;]v5`^K&q,--EDDP$Qe"/.?W[|ṯ/0uT$''###"wz="##qEݻΕ~/sثbɜk⯿Bvv68???`ȑz??%| GzzzaNR}%X2h4(,,4i/kh4<0;cJIJ<Puq۱yf1d 9)G,cM9 KV ooo̚5 DzeˌrIJ<֔Ս͛7d PTT:ΨkIb"V|2.]x WWWH/v%Yƨ eyNojG!NT$''#44d|轀u$ JBPPΟ?G`^XS^V7t={6 ӧO_~#.\$QY[VٳB$yfC^/r|ɩұÇF;v옐$I|7߁:DX!$IUs]T8z1uTRDrrr2'勥9֔B]ɓ'e˖B3/勥9֔ՍcQQ8qgϞM8qB\zհ}}Ib"1cƈMq!&6lhtSacc#n޼i޴4&V^-$I3g4(++K4mT7 Z-#JKKO%v,O<)Err8rHLLA"88XmՍ̙3$I"22Rdgg,Fc0'e}I!{O,YDٳGdddK RĎ;`^52___e^_r2XȨD,\Pxyy-ZwyGdddm3ydRč7%I2Ν;ƍWj<""`c 777b֭?y)))$ Xd qexzzXL8͚53A0h Ą u?=v ԩr+˨\\\j&} )) K,AÆ _}L>JcɍG@ȪEFF8x [nѩS'pss3eF"˷ʔ?GϞ=ѢE <@^^^WEfϞ#-- 둘 ob"""X aÇq}DFFnݺ}O>˳xb;v@VVrrr鉇TΛo`ݺutܸq3gδ؜p Y5[[[aƍwRRRмys7жm[9s}em׮]}oٲ @BB?D^^Zl)Tٳ1~xOXv-1x`Ή""##QZZO>FHHlmm> stream x+TT(TH-JN-()MQ( X( NU5Tp endstream endobj 5 0 obj 53 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 930 774] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 930 /Height 774 /ColorSpace 9 0 R /Interpolate true /Intent /Perceptual /BitsPerComponent 8 /Filter /FlateDecode >> stream xmYYi檘D H+;JEOl.jI[)i6¦]D4jX&)b1hXk5x̵yZSOe @ @ C ?}6@ \s],n @8|Dž=ҝU@ \d܋|Y; @8z*붲ϸ|?n>!lO{?>HoODzI>O'nO~S}<?穟agT]~{|vpe??~a}I_t>Ow3>n{3>%l_gATz_)ۃ/e!a<O{haۿ~p߮9t=3F^xg~i/Po//>+~m?_a|^tz}?95/\}g^r{v^zGR]k /|߾ /Ɨ7<z{^o~l_ͯo1۫d{ХWo_my޾5Ma7=n?iGaa{ڇ}.c^ǼkG?noR= _/{7w˿/[d+l{kƯ27}ەl?Si:9l׽-_Cv{lOG>᧯?}l ۓ~oo'v{J}S~QO 7?v-noߞǹ{w~^.} e׾煿f~~׿GE?c/m%_b~\o!}OpOėv^''ⷞʰ=啿eWߞ߶۫'~i~{<-l{k~v߽_{7=up?uv{x}A=߼nGrQ^rErEZ5Zur7F5r.r-7\ErErr.*k$.*wrWzwyت^K5r_׮79p'sWrǂuA߮`ܱ`[X-̘;} ܱ 澅cz]T_3_eLCQae7[s/ v~)rݭܗk\sw.r_5r ܗk΍7rݹ;BZ?Mܣ{CVToo@ۓw5T?{uw_߀w=NhMi{]żlgr%▇ڷryy>3Ag hw34}ό29eձ.g^Zn_nQQ~,g,tU^B \1 |Ƃ>cX31 |Ƃ>c|V>cacW*Z Q:3?=0µԲ}Q׿㦥;JDc|ii֏w}>eix:<7~4Y]|=DYj?F̋[ ^ztӌ)[^]yH{<?v˛> O{3?m&[>\ܓ>LLO0z˻߿clpߝ[ w}np#vJ컹}o2$v7w||$f>FOG#f>XLIb?'Os)?F̌$1ybČXqg}B3hL?j]EKQGG<*ʞi]c{r|m榅p5O _6ʽ#I`>27\5}^={mV}EZq/^^5'}JBw*kbwF܆ o*?NrMQ:{xBr ~6\?yyk&OҮÿf>/Vx\+n$*~j.*uʵOr\هν8kvU)iQq=kVuXFj"*_r?p}ToCQ|PAC;VJ8Y\t+/ek/ wjk.{{_SQ~GO5kw?}=DZSQܾ^}gVw]܋Qgr~+Ľ r?[!P\#qQ\nT.*H\T.*Й\->u UNۼcaG*7Jߛ9uw,wkpSP;rk-\ wֵ\T.}wg>_ƻxyٙLSUZa#fc3߱`<~Pz)*xuǂT\<;kGr*; rõ+]]IlK~ظt'U嚯~G ;c!|/w,p-k^Le &3jh?i|U2D7\}fTiSԺ&Qv(t}~ *e/7Wq7dPU嚛w-\)knWgߣ7xهi$>!|"};kϔ}E~ϭ_ʽ/ŭDrQzϙ;+Sġ db)|^nQ}U\s9)|+DOy3Un9yIb3 V^6;{/1n3ko8[!Ao[!WB|+>|+[>q/[V-c}U\lԓ:['ĭPEO7y۞)b7<Ͽefy|^S~Q|^.y9k^t6n_5rv .O~3>%lBrWr- ]T.*%w>33gkѝ>]wX]{E+_tz}?95/\}g^r{v^zGR]˵?q~{×Er_r-k\uWqx-9s-wmq+\T.*K'my޾5Ma7=n?iGaa{ڇ}.c^ǼkGȆ}~S(T.*k%. ^Tn}C/3 @8 ro @{B@>cnmUo @gH\jT)a @ pVA¨kT) A AVsyvO@@ S&U6T)A M6:!@*wo A M6:!@@Sn|q_D @(*WP @a(TnѼ\- @ m6)B$ @*WmsGK @M&&!@*C 4 rX @8hFn'\sV&|ez< @A02uGfQy@ p. }"\gl\=/ @Q3Wrg  @uf,{^_  Zg$A垃 @y%XT1*$X @8DH\=G%@ J@ יbTy}I.@ pjTv0utԒ㥓ǕKlw =bWO.9Wⱽ^+FG ,8K@@@ י){Җnlf6|xk~6}tnKY1; *(uD Y{Cyi[,vs֋Xbn7閗ܭS  @M@T>S*v_^띾5*SRg-r#" QK?*<4ڳѺg[XЖ+T׾Rg ;i|'I 9:LAy!XTީ\s ]8M;)wjzDpO2Իg;)6ݴT}I4VFi @:#qϦ*(;]˕#56UZnrͬUhnN>1abz($Mn}!w9})4rĘԸu7B7`U/{ Km\iQ|/o[7j&o`N|jə<,9mY :hoyKްւ J@ י-#M׌'E晫\}NӷȁhM%s} W^.4^^eڷŤYS+W͚ņ,(p b&F,J%/6 Bs9d_+ÛUW6 C_*S`rFaWaf;^ZKv呃^u^NN7UL]YZ^M5d@X& uFj-\}]Qwr;.>N& q'k/*w;L2HF%]\rZuUWy1?>Iʐ bֳ 3 rIZ` 9ٺ=:/EHu|,a(Ӛ*Js( լىə¢T-xωKm3!ݑsVruAߘ @-ZΌEoAjY{Tn]'~^b'E[Yt8QJ.ihR*Z 5VS$Ҽ8 i)J[Z66AN6 CoexzaJ8MD,}%X@{x &zU:K{i, Q3Wr'ܞ-/Z_9ې6=mTnT׍UEd* K!~:RFe86̴e3qF7F/Õi0lQ;KҾ34t.]Kol &-\gƢQym*M+A<Ļ?zЪK!6On{Cm_Ȃ\UbFR)Kvf|*V9Q,__g[n ֦zF2&,T% @`}Εg,ttB.!^Ц2Lg?:6jE+_pW@9:8-YcGQ)vzA.y2]$=eQ!ITQ&Y4e=9 Kvyh'uM}VK%7Y @uf,xJ嚻mM5o=cr-6 v߹V٩\Ӂ;sUg"cܬKgWQD޳grW4s1?;q(o4lQgÝ p h:3U|f*|V@ Zg$An?9 @vB@ יbTNI!@A@T>m' @Nh:3U ) @6j &I_L_ 6-\gƢ'Un*+"ʯ/R&-/ʄC[1S/jpuҹ6BiU%^㯬Ser(MQ.?W*JS3oerήw%9ٱn9Y2ˀ 3$ uFjԜ8I8IgMڒw_٨$;06d%YOA=c~B5WOZ(K)7SlΨjtI``r't]Qb ݫ+@Ua6RV;lR*) .Zj=£9̈́&0 ߓ2W }SOSؠ8fWƋq¬vcSf*„oW0sg2*E:aV\;ͨ`Ը-Gc l6;u'%gϱnRNt1GwZp-T;g3ʂz;6-&"D'acsAJ0K8:ʾ/┮4;{Ba[Z_׹:%ѓ.%_ WLuC ,D Z; @`Cr9QIsP&Tf3s\ g Kn:i*ńbsF>cW?F ޹d5l38axBc_ j`/Xmt {^T-2qH\֞o^婆rQ,m+9Q(GAr0 l@T;2OӤm_NSLulꤛ|T|Y"ۭ,{r6|ޜʢɌJ#RKF|.5G=WwV+ nXYTSF &uQU 7*P v!@`vr9>w?T^ ǰ[fV^Õץ{ _,1yoOtj%jE[7qIuhE*e[T%^DϹO/mkl]!W ˤ)5XJZJK+O حuyMS$\Bb2q`nq޺bty [<.&65 l7NֹcQB:Kn__d U:KMʅbyz ]X fe6:Lo& GGhBPvr}.U{ٌ5kn-tQPW &-V=i3_*AkN ;:ӣEeGvg+å+?NXZ.q} K[iq|4r%淚,OcAH  pN [;*WPl̘VUOcF\=9+WO.{(%R^gjWMX4]VŝpBѭ*ZmmXZlBِۣ=F@` 57>mN;l{r +\Ӊiylxgڛ̟_oea։G4;m͓0%e0MtV:.kٳW,^{;M skGڐhg҂ JCn_쳛0thZ鲊V`cIe.:әY_eؒnɪĢq+j9%NacEGi/@`Y#M׌'E5w,V1]]U嚿uM7iSL۵E:qZmiuq\,^FΛ5ygp**7/_Z48Υki/ֽ:Ɯe8>֙-s+/%9z1R 0WTj9TsViOmt,մWSfٴkiT1qk"Ŭ6SD6A{)6N3Rc_Z`+AL4#ŷ5M :sr4vr˓n' {ڣ1v ({tO1DA*PC {u‹9\ŮҜlUmU3GSPִttń.O*ﶮ%TtՒ Ժ]~=9ʳkmgr7,w͆=e)cgv5o{6d!gܳI "p~T5ܕ}s+:Vu֛[*4LeS:'gtv^,2XWPӨYSP O9zW [5kκCF;ьGyB|쮄{)JDA^T[Z&YS;#ͦ.u>)|qmT .Цju@ح5w܋kz&cLbݫ\xR1+Df-y6ۮU:*Q3^W5tR qNMG g!, 1J`qA!\GE~aµ^QZ.-.[kCzJ5[C!MTm: j8e\dKkXil;s.S٬k2Iۋ.۳Kd4Sv'6 36Q#ebD. VNW]#! 3v&8[JNeJ.lj9gSe97};"s*[M6jp \0{\v& 8T{7$|yNbp-L %#sQT|\JH8AS[; @+Ty=  \d܋|Y; @8PȲ.@ p r/g @@#˺f OE}I0[`²v ;d p Zɱ"<7*[yٯL8UJh]<(s Go*:l.(LWpROպE3\?Ч ^p E>@_;Ut$ d/6$wVJN홛eHA#!efR?>``g\s]^&^=iu\ӂth.fF]enO^)OG䂀' nwsԯKg_mE;wP/eO 0 3"SI{zsW (L8iu : Q!orҳtS*z*=WOXfPj6N 2mCOCn Jga͵^gBY8[*SFo61YVjU:_vl1[GseZZ~9 YiblȘ|*_'a. STNHjEՕ4s`N^ qNcUa4֡FjG$rWI1 mkUDV2;K *K^48a4gUy8Z0̍v18;Kڭ6yVR M3n7ȼ4Fya.Rg6kj1է:Q[C!pj*7;Jerb9SN}.DT;g3ʂz;6-&ҥDAx$לl®9#CV<BcԴ'Wcg*IS3j˯CeRLm.y 0멻!{M_"VWxQSsEzIPYbjGVC#Ҧh}C mSS&DΟ\SmEFfzUS3@)\wegkڟ̉~`>VzbpnY9 LKW^6|Hz oFUr߂J1ֺNC}C:4pNM.l46Ji}MWYOq0w(b[/_rSylKrUhʖfd_bU},QasLe&Uy@ي{B!mYi* -ֵAKX6Fz})av[ hrU,^fmk:K4խcc| ߢՎL̰WTc_WdfT(_Mi9}Si{_vû#nf{K^h/6rs#\G΢lvGԻ7iF/_AՁm.)`,O?Ir *m'0eҖ4؏we򆢭't55H1=3՞qQOacQp7Ɲ%zm `[l*7)c`*@y93Q>u?0I@c5ȳFkmZWSz2WCW10uZiJv:9I2W>[}.Wq'ܸVr΢~ׯUj*+jq&ڦg|{lQoF<[2d_YpGp(νvuq{vf_Vi%;~Kǂ)@CW[̖_@T.&PBy;;Մ]L媆.ZkvmlI~岱ES7CH?7zI=瘴_TbcD6r;e%u&ZARC Jtjui&0d@YͦjdW"YŔ2!e Me UV5mY3. ,\ȰirE^hQ/HuMlQ9 Ah02[e)kGRPrOD)gCf93C_E|jZe]S! ͷe4O9{aʦ7Yaa%Z݇^.o-'_2i]R'8KVˋYEᩓ ػ{STӰB5 K4:r : T=:#ձ6RV;F֧K$xz.zѭjJqyJU?夆XlF<{D4:o#%[~f)^$" ̗mBN¼Y2Zf9pIR[NNky,eW`[e3L';tA=%\޼i5|)]VFGJ:11-/"fPqL|^D.eبV#WYsSˌw/T:O5LXXMV ۑ\f 6"[yK[|JeowkaF-i;vn}4qG];&-o[$̂ FysIR32M!ǡ4J!7hYOMfK;nٶ%u LIpmF"=5.snZ9^3z2J˻nwY j`Slft >(e6'ƭ,QTյbs]p(N238+7Tr15=,14oMjYzXK KS{ Smr}5Qϕ3*5|Mn=Rj턫`ڑ)UY\EQݫYj#ueY}I!rʧPC\7JTѺLJZLDWSZPEbKY_6=lvM˄*L *0Ux-qPwYdySK1~.CtT<WON&ۯd{9R8K0_!5\6M C7fcn#OX~ja^%f҅e /z505W'mt fvKX^vT=o~rc'=P9k;B2VQյ$k>p+3\; ҹ,]U?\tE{֔R4T+VBqAS2k*[Dz_ȯJ-*Sj㷥RizPՔܯy?,&Qq&oJE Hd.;oj}y%b/+Z*qټf6,H*n=Gd86RG.uP֎3W36o76eQ![m2.m"`d)BIfUO\W'Z2᭒1R̥VMp4HyQC(:F_[ܖQI|䖷\U{u+ژrI+YP< c):Y 3 R :r j/z 㒗;l=Ƽuɡe:AZɺTf),6 [3gݒ)u_OjOb3Sў)LE $clJ)K 2vGDž:w:3?R"ߦJg&R8% MaM,R4t!*3E0O)ւYhNl8\2&uFj 2ߕuv_Y.7AIy81ctzԙBlwO8%oYC74ԒG`Z̶ܙ3cQŨ- ҝ-fVgȝu+]> ,NO%`!wiۓ6Ns۩eM3xO7WhqLhi4eAT>w_?LяMR}Y1vH4fuuUnl@b-\gƢQ'  @j{ G @uf,p @N}Ppt(@ -\gƢQ'  @j眨6Eu‘蔰oAA7@{K@T>*מC'QC|ug3?nguKCZyWWlnQC(.7>0- 8_^$f;-H'KGtr%ņz]%:*wܩ%_ҸjyzSCfbd{59U0IѬ^ubuZjޭ6uJsab:U\J+I1[`T , @眀3cQS*NPtb.Q3WLxҭX.!qNg7;*L8Uδu>V-j݂،w Ar]҂',َ JNδ.gr7U IzN>q*J{P| _z\s6uJI!v/y׶řV2a4ӗM'&!@"uf,xBO7g 靅si2d*uj1Spِ(tc!IGcc}ETa/e;ӱsLxIWP%I]vU2K[x.|bxCLΐ,5eGpT ʕ[y?@}&T?]ug8ؐY;[(D;XqfTS>J#$. htgmjDCNj;k3uֳg^nW7gbTqPY.^Z\CD,=.5U^-MIKQUl3 [ف G@ יkhƳ2!Uu,lH,]S68>Cٞk2)OQR#mFF-+V(s4tŒh$pD2/e %+c3*KՌ ^Yl]o&be`"ǖls.sZzn-2cqқj"+ckHl'@ Sr3 C@T>*A>ٓ9Z[ENeG9@3cQrU:х;K5dTYIjC}vr=b9ʹ! @\uf,x7*\0e @gM@T>ܳ>tԇ @uf,e @ j{և @]ZΌErL1@ 5Q3WrQ @K@ יbTn)@ & uFjTY:C t h:3U2@ pDH\=CG}@ .-\gƢQ] @Κ}Pg}@ %XT1* @Y:#q*!@p*Fvb @8kZg$A> @@3cQŨ.S  @gM@T>ܳ>tԇ @uf,e @ j{և @]ZΌErL1@ 5Q3WrQ @K@ יbTn)@ & uFjTY:C t h:3U2@ pDH\sf*AGGqqk.㥓Ǖ˃_Ј-cFS @`]ZΌEO딶--(S4w%wm-B֖Nh+o& @-\gƢr6]33*CGGw߻5/۫W^t"=/"tr2z_ds Go.rRi^Fpqc@V! uFj-\}]Q*W)פq*r3'ZE٩$cs+t~=-~n,C[  @+uf,x *Wڭ\YcxlM:ŗ>;z8MwS˳Z6g9.RXj"_3ek!rZ[cZWk#c@ Q3W5(7-ʍ*8kj9Q^<,5 "Ti&jvYL  @X3cQŨ\%#E4uJN'P)[5e:W>#mJRy%WE.@ U jgUAwV~S|IY%絨Viǘ(T>ecEK|QTifg1$gjb@ -\gƢT8^\37/ܢ wT)ŧn٥(M]Ƹ?t/Q9ĘPTY5b6,M\&݉Iqk2.g,ه @DH\3rgX+UsGO宑tBtA®=՜Ρ  @uf,JVsyܫ½ g4 @ pDH\s*xoL37Hڃ:^5VQr;kE5C @uf,Tno%C @ :#q*7" @`h:3UݻcIC @}P! @{G@ יbTK @DH\s*W^}8v"bX}4VxmWOE I@>c|.UA{F@ יy{?˨Gh:Иjnnfљ;V}^Wǣ?N΋6;:]bOMyeglѵnP% pB Q'l`w_ 6z*^--{J+ E[2_@+:#qό_5&\D=no G{*Wr_)ti%|j`hN|c*䲩mPN6Une1K%Yomry76F/N(Qލ۳ ˍ..VV, c"{1TpuM׫ -\gƢgTFUq2]T&ÇܶRy4ch׺S0 |2l[OWhr m%VP'zb#APr?+7S,x|V5^er[U: j L+P e$Z3r^͗7 Zg$QǕ5oבg͸ͨ;WQh!m7]z˿/gB:o[gYJJU䫎2o8K$͑tG&V\Ml[9ᖒW_XZ ńŖR-+KW}nOU'XTQSSш[/&{*WqzXfdrovw,G3TnKo3Ic&fyRtjzҥ?M~NttE|酪\mZTt[;ȶ!55Ka6aK$Qfٷw6fumW[KšaXQe@`kp*Qn!j&Vp_4zGi`:S*׷Kk;=(e(B`E^b껖}[Փ}qP nkG -\gƢT$[{ZSjfTԒw:~:݅*&&CՋV󩮊b O@T>\scvdv|-ܫ`*tb0?N${6OZjwɭ\,ۏR+9=­T]s4M 6?Qh0gWeHn-eHնwHK0C]ZU0=&#ضW*WSK,lI@6 XTTёܦkƓ"sr?ttdnW0]\P>{"_`h%H9\(DV0.Q 1otͤ:XcA՚24i$\%a9[0Zhp*Eāk2uݼլc\v.#v^9iy @`SZg$قo;^SO^U0᫪qj5J+֦dXirh$y(g[)WUIYQmn+*isݖmlZdQXWs<cZy3*zS3 @`CZΌEoAjYʽ\JXtVL#)s/w*v5\( ^sNh뗳YlG8ҎV\O{r 3 @+:#q~\/k\8SӔL7{QhUf#:pkN_n&hw_ftc;ՈY*gd®e :@p*/kȭX(ԉݍB6weZ'QE/*&k衂v[*жtcX Yk9c%0 @`}Zg$/[XZ`P'N%.6՗^S?JRDzTb닓~s>U2|G rʗ[pT0Wct}25n uf,xJ嚻mM5o=iԌ޻tȎUnt#nEI5_P&Fiܔn0]_A 4 LvGvkqKh+.D b̪ܸA Q3WLvQd۹crZQ3z. @ -\gƢQ9 keuU]1\Jbu KC # uFj3V+~cA‡QBCVN^]u  -\gƢL%jB Q3Wr@- @Pp*F^  @E@T>:t @.-\gƢQub!@a:#q*4B E@ יbTzX@ pXDH\=M @Buf,{^', Q3W+h'@ sA@ יbT8, OZg$9W*|[V@..-\gƢQE!@:#q*w- B K@ יbT}Ѱr@ DH\s*:gXg,$ @L@ יT7Uz&T! DH\s*^3 @Pp*F* !@}Pu  @@uf,(2 @/Zg$Aׁ@ E@ יbT @`j_n @-\gƢQ"C@ " uFjT~H @Pp*F* !@}Pu  @@uf,(2 @/Zg$Aׁ@ E@ יbT @`j_n @-\gƢQ"C@ " uFjT~H @Pp*F* !@}Pu  @@uf,(2 @/Zg$Aׁ@ E@ יbT @`j_n @-\gƢQ"C@ " uFjT~H @Pp*F* !@}Pu  @@uf,(2 @/Zg$938 xJԵQorZ楓G\Dg+.ae`Z l@@ י){Җnlf6|xk~73S<|,*cqZɥґi;KCvg$> @5Q3WL\}O^6r9Qp-v~EnkA^W ֽJȅ,; 쌀3cQŨܝH\scvdvrM' sᕑS1V"9}]&ե>e+`V(^ֲө5c-K.ʹvkڎ}^ &u叴mӕl֖ݟoӪP_2QQyyٟ1gqʭnڔ%^a[y:C`Hlْ2K3e @;$ uFj-\}]QTͽ ,=[wE@9㤚r*N)) * kUEbHwZI UQםj4%M䮣 {j̏9;ѣB ZΌEoAjY]kXTaIsN.AZT45ڵ1Z6JBrk>Ciky#[BLGW0Ai3b4 쌀}]~TnVQ[KXUw^/lڴ5d o^p-T/ٻYhjc%b4(>0I؅ @`Gp*Fl#r^h\"%۫+Z܁70a @`DH\s*xoL37H"lN̵'=?;sc y~r`M @uf,{N^, GZg$AǗk @焀3cQŨs ` @<:#q\y C4rcA >$ @Auf,x^2tQ|74&Z3A:!3 @J@T>3*՘(Rsx@TKiBC  -\gƢgTFUq2Eָ @DH\3r+Mkt-(+#3/q+ۺE An@ pp*^T^:jujqdO E`r7 ʹ\a Zg$TF@NU;e_ *WB ZΌEOӼmN[h:3U3AĭQޭ@j}|h\3"r@ p jgR]SNjX @3cQŨC9 @. Q3W=73[frw ,n  @h:3U|*- @:#q*!@& h:3Uʝ$ @:#q*!@& h:3Uʝ$ @:#qϬʽtrzrWHE@ G@ יUTnG^w[P p"(a @huf,x*_˭6wH+Y{{#ڇY]S!_"ZZڕ5*2LddX*<e%"@ Dj斔[ߔ0|0YaG @~ux4B&@ tH#7HOW$@uX%VG tH#7HOW$@uX%VG tH#7HOW$@u|fO] @"D\soʭϏ_-CJ @@#H{/}a׳ @`D RH5ؚ @ :ҎT,vLD p@֑kܳo @NuX= @lH#7Hg  @&H;R{=0 @ZG"nytʝ>klz7vE @rpiG*3!DcS}@Gx@֑kLRp|)!@ G#HŏN\Or @|B RH5L˛<4 @:ҎT @O RH5R}F @

𮘚 @>H#7HM @\GڑOIg?7}5Zb-Ihj @:qsͽ)_??~ŷ%.eh'k.L>- @o"H;R})wJ3J#L & /~ @|H#7ܗrxTqKX @o)H;RRTݘcʥv,V ? @V RH5O%w*ֻUl~ƛˤo  @U בvǦ܏f_׻V#;?m컎 @[DjQ)fJm󳹤9x-x]$ @@#HŏJX(6'1nv>7aj.2 @x+H#7<,7X?{Q=Gr9/ t @-rpiG*3=}M 4u˻`%G)w|P @M"D\sg|5:('`M @g:ҎTR,דgUY "D\Rfq_  @u+gaZ @E"D\#  @@#HR @:qs"w6 @9#K{Lg @^D RH5RA @ :ҎT|oMn6Y${JO>_4RЌ6 @/.u$3RnDԵ0jrM}g\vƪ @o$H;RRnk$kj]s܃}b]*9v7. @ DjQ)v(6Oz#`_.{w"qh @@#Hg=lzeS$n}j[-n @HZG"n7=pQ<͝) 9Ωp´M @wuĔ<^ ~5.G.-nrw/O}lӍt- @I RH5xCmkM ??Ǽm:K}3i @|Y\GڑOM%x6pM]M]HZ hSo&\/O!@uRדܡE @ u$7I˻< HuMR0B @/$u$) e[%@|7\Gڑz  @_H RH5R˶Jn9#KO @@֑k/tm rpiG*>%.g4#w4uk]+R:^ @:qsͽ)F_??~Y%R.egYOa"q9vTưy#]C @Wu;9LHg*JGd~TsT^͠qc+t @@֑kKt$|GM%"ٵE(mgi+cQʚA_ @rpiG*~TQsΪ͛.⒈WNr{EnoLgO#v# @:qsSnI5YYg.=kY6=;g.'|1N @rpiG*~l-`n"٥-ֲ)FDmklݜf(L @%"D\󨔻DԔP#Fclrh9ȱ?N4ϵvW;$@x-\Gڑrw$)_S$3An]4)DONn#׺vC Dja) jZУZmޭ0 8lau@ GrpiG*3ִ=Hݹ}우s뜩HNvgk_6a=6I lH#7ܙr}Y}}Z&%@A בvb)w~I mJ  @:qsͷN˛<}֟u @7 :ҎTS  @,u$)wr @9#K*  @,u$)wr @9#K*  @,u$)wr @9#ߟr+V>u[њ ߌ wy*Q*  @/u$;Sn͖kl,?{mfұNQ͠ @ :ҎT|_d}{Qۮn\\WZ( @/'u$暻Rn݉d׭r:֒{mT3p8 @|]\GڑN |k [~.mz0|uT,Ur23-&l8 @xAH#7ܕrDa HwΝ7[UA1Wm} @^O בvRٿH&uq+t;4jM<3Ot8 @xAH#7ܝr]]jicq2-y#jw; @x)\GڑOM!joMm^p辪?RM}  @^M RH5:g| Qd>6äf']䶛TI rpiG*3;=^Z7T9w|~`6y3ymE "D\sgz5| f$@@#HR,דODx@֑ku]ެ1c8N@#H:~@ @DjF} @grpiG*r?m  @S"D\#>X @39#K6 @)ZG"nrr,B \GڑI֯k>x6?T? .w- H۷_J @)u$Sn G0w K,]w:]??~uuy w @x@#H";SO)eZ*Lyk꘧ͣk-8n @B RH5wȗEqr} B%b6j^N+~վf]s>e~c @?!H;R})wN=?wl |P~.i+į;0f @ZG"n?w,Yh \477Yb}Zw.ZN @ \GڑOIuGk֝!1GkJW)v;p @ODj`5o651C5aKnSڜעAݍߝXM grpiG*/1f%)5,yZ^ &:C}$88f @x@֑kKYtМ,$Rg3GsNv-gs}3Gs @rpiG*7.D},qy zfeT7᥮κ.U{%b& 1 @"D\s}ƴ5 wQ5vf @ H;R{c~I"] @ DjF=/˛<=q \Gڑ  @|B RH5R' !@x@#HRsnU @>!u$)چ @Djv~s- @l\GڑOH%h"wѨ\mYlGϮN^__r @LH#7ܛrdid6y^՚e5=mQ1Š\ɥ'@u;GK^-B[\. vlfl&jz̶֥kaIv-h[7 @9ZG"n/垳9KMk^] @^O בvb)s7s~I"@ DjFʖ7+xh @$H;R;bZ @"D\#ޏo @ :ҎT,>莘 @~H#7H㛁 @A9#K#%@_ RH5Rf @x@#Hŧ峵[9aƱ[l|ٿ#k:ӠMG\ӗvmqMy~.^XAش @o(u${Sn>~~å쯵]*s]z1UGȚb2kOT󕃶'֭M p@#H) ι 3Aeif:s^0](7_c<7<) @^ בv;Sڧ)ߥ. ֞\4b/4(Evy}c۠ZzZuAy @:qs͝)wdo߯&]1 @,\GڑܳnA3?$7@4 @:qsi˛<=q \GڑOhB H#7HϹAV!@@#HR' !@x@֑k  @|B בvb)چ @!H;R)j 4 ~AX^4:,Zl̈́6î~r`/\z<.u6gv<(膔; Lז/,^En`u[tH DjΔ@kHf& SD9#KzLsk @_Z RH5Rfqm @=rpiG*rU @R RH5R[=( @{:ҎT, @@֑kܷ{pQ @uX}?WAxKH#7Ho @!H;R))w ?BuU{  @"D\soʭϏ_խ9vuK_.)A%@Z בvRVg ן)vy ~/] @$H#7ܗrc ןOo &N @@#HŏJ>75|îIxu @. Dj)NyrOt/C @@#HŏMۛq7o5.)uL@֑krkk>vy5@)* @@#HŏJTWRޅX]Rn 0"u$暇?ݾvYn%Yw-WC9#ߙrl6ZԽEv|rLP˳d x]" @@֑kL7f{ @L בvb)Eg  @E"D\#;c?+x5\GڑWC ZG"nrP @rpiG*r_V @@DjF C  @WuX}[i? @!u$)7 5 @^M בvSnrGүauiYnRuqʦ#Ѩ4[YҚwub}  @CH#7ܙrK܋V_m7`Ky]``dmRGe:5?:-.??~E" @rpiG*3嶻!/\/saюʅWc:j`Q'g(Ks)[8:l @N RH5 喼<ݝX/J2Lr^Om]+lŘR1wsk @9#r8q%ז;wyowbv|uDCVr0j\^6` ']ѹvo @zH#7לrk(8Fx:fԹ/-]v\4g28;8jALJOU9nT GS @P בvR /i5msy}PKݑ4m,Pג-tY)bɹfݩ]wpꨬA pY RH5ܚQ.#'wr+9]t;6K)w(]arEu[bӼZtu)]=)]\:\S]'turǹqqA @"D\so= ]֪6eE5!1]QiY}5r]8=::5mƒK mrpiG*/?A]`ӗRtRW氷N[`eIKN{ی*+S[閝]uko۝ @{ Dj澔=t5#7A  erpiG*r_r=}a @DjFwpyǸV j9#Kv+ @ "D\#冡 @ :ҎT,ڭ @:qs @&H;Rj~ @B RH5Rnj @@#H_'.՝mYl׹~XӑYy\uKoӓFߕvkOX!^o~ @'u$Sn tG_??~_ ))^իwe/pQve8jJ׺sp)J  @ :ҎT|om2_1gx׍WFɧvuG]6ѵ+kt-  {q @:qs͝)wr9l`ʉf4t7jn]2d3׉&o^LXsa}aZ@ @P בvR u$@\GڑK}tžK>_^Ǘrzyu/szԬp cf" @"D\s_ޢP^hlj!G]O85nnTsv8M_3mqGyRm @uޔ#>*]kVݏ(ʨ7E&>)b`]AҵN5.{ [eʬ5# isDZG"n?~C\rM}:f%@|\Gڑ3bOr_v "ZG"nr\1v@@#HRJ!@H#7HaA j9#Kv+ @ "D\#冡 @ :ҎT,ڭ @:qs @&H;R))w(^/$tWŮ!8*nղlԌj::}~ظ @ Djޔ[گ}9RׅSgWirtZ֬%%Ro3ô2bu=hu&@|K\GڑKS43hn:%4h8DIu㘨9i?S @(u$RnP.]/0(GvXcWGuz  @"u${SFV]0KrwwRʝv2mcE 9#r#ަ;C楔;7))H9 @ Dj愔Nq7u+)w Rt{]J]C @/.H;R )7SRnPloz8"@@֑k^1J' @9#\ޢ0%³܁? % @7:qs˥qV]J>c ] @ :ҎT%l#it=۟2O uzkfST @/${gS @/'p=~n @:Kr2 @@rC/ @o 0k}v¦ @ )O @$$F) endstream endobj 8 0 obj 46750 endobj 10 0 obj << /Length 11 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xk`ǿi VN^ءJhҭF]MxH6iLҪAa&DA!{ [|7&'$'OU'X6WcAyl.d4ulT`ܳw7M']28 ȸ1k#θJDJ< %vo.B,z3iEi1˳mYʟd<9.&?g}l@俿Siϩ GvN.SGŽn3{%z\R.glvGABsk7;Z*Dx=9|þp> endobj 12 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 1 0 obj << /Producer (Mac OS X 10.5.8 Quartz PDFContext) /CreationDate (D:20091120221508Z00'00') /ModDate (D:20091120221508Z00'00') >> endobj xref 0 13 0000000000 65535 f 0000048190 00000 n 0000000167 00000 n 0000048057 00000 n 0000000022 00000 n 0000000149 00000 n 0000000271 00000 n 0000000360 00000 n 0000047314 00000 n 0000048021 00000 n 0000047335 00000 n 0000048001 00000 n 0000048140 00000 n trailer << /Size 13 /Root 12 0 R /Info 1 0 R /ID [ <529b7a100d039d6ee28fa5da384a7825> <529b7a100d039d6ee28fa5da384a7825> ] >> startxref 48332 %%EOF ipyparallel-7.1.0/docs/source/tutorial/figs/mec_simple.png000066400000000000000000001430701412533144600237130ustar00rootroot00000000000000PNG  IHDRg(*iCCPICC Profilexϋ@ǿi],J"C޺v64IjIZu7^E'APq+"n|3c{x|7SF^Zk+7Թm1d¼ird|ǚ!^`4!Hp/̹+|'CҸ-״Ѫ?'^K~͹+ck=<qgֈ?3H|ׅz #}IxIU8ywvX<Ru;YQ+%>"aQCڹ7._hͱ lݶ&l*׷YF>Qdbβ.BOcЩD@/#U(į!Lj;_JW6J"۸B}o~4bZ̽j=f۬^H[icoy"u1Cq̵蝠h6\'-4l|km6}AXO֏*jQFEh("3I#pdPy9=-t{lwCunSu15/jw?*@ pHYs   IDATxeUyxV5ִ<bxTNzԆPYL-=.J"@PQ$.1^@5 2nkllYP6.#tJ kpOgss~U}oWwޟs{7v ,7,s` 0) z߽    '{vsɞ}2    #Iˎz9#f+\ ۠ x߂>2l:"N7Z黾;jZܺػp>zW~]n>kF\]ձan&O q    L-^znUUu%F?KK/||ol5+?Wq{صϯ|S7₆nv_#7< zk>q4WM~w5O~g@@@&mW8[Uug'!/r}f_|E*'7>;xȵtOԨr0"T]HaOW^ԆT:ht/{~686^}{-ыgf^%w_P^م3׎9å/>G}&+BUv\&n?so>zk_Xw>#}co?z?%*?o|ϼ>Ґ % _/cszU+;-i_\/'ә?;ԝg}_SM)K7~7wv(m?zY'=~n~_^znyKӪ^A@@@`|"OW.+ ݨ޴_wؗh?xޏ?/^c&2yſ#+=b] Zj ,F5߿g|4-w}g3o_Gßyly;7?~*ZeVo!ڗI] io#g~|]ϑ ]x+di@]%~_};d`o{~׈ޯ}qO>KG{"׿L9Ro=CPwʿֽGnwC;c+|=/}E'ǏP~ Q!V@@@@`"6ZkSGKE38F{y3t߽H ߩ_FޔZ^5kck_ ^}/_\x3e?s7?;Vꙻ7|tyl.) {T/1uNv=2W\f~μfjtzS'q3řO|6 >⋿i{_{z@]TBOA{ߏpοB7>M9DxQMQv,7etg`zFyTZ,\6Tzx̐)najuowZw]uYg3VDz&qhzJl^J>77zC+>?]R?JI1;]?0UCI wB4   C!p=[^&!ݰ    p: a^;-  Ds68Di"`=G-9M<`+@[3w|`dK''3丹Ćnpz ri#?R9ͥFtS     02!y֣ =,dGy$\,(?;׶6XYbh    0N$AE%    S@nLT wn!pVh     06 tm00@ ۻ=z @@@@`~m$%e "PhވF(    pr=9s K@@@@"ps#X89{rI"#1nnz     0}5ͭ A@@@'^_{wkVf@@@@`Ϊ 77%    c&nV]Y&(3@62A  DkUnn J@@@@L лᬺpsLP    0ffՅe1n#o8 [/ Y?$-ŗxڬ{_ڸ봔Az=q[z4" y+;mkJή1J8&A5@7k}@[] x3Bow$k눃qc~v9b-uTm$_X-H%*kSSF1U!  0][NusGKcWyBl!^,\8>_r<򂟑kCwwKhs~띕/o^< ( U}brs]-Y Yrte c>4L    Dl477mPW,HBjvͷ Aj ēez嶥Ls7_隼(JX+ eoXߨ:wr;Wzb(@Vޫ9;?]5DBX3-#c4 dij9ɳ.Ae)g@;`5 {\Y:$S k&T> @@N/&KF4<˗ &n|ͩXk[.䔊6rhJ[9EZ6ݭUiWWA]9֭=Zyխ &Kg@j:>SytvC"Wβg7'%z\rO>B+հDyU_`FJ Oaz2U3oS,杻;C j {d/^69oP>+   GskUɻUkR(_YqÔQmgX̜eA?o% ԕl-q_ƲveI償 fО2ۖ 6U4G>nČεO3?j PD:ocr?sZ]kg+ݔqP<6ޙwVV%׋OJA@@HmzVLդQ2х9ꂵe:l(+eo@͒ʑ :UCJJ/BAKn"r]*$>ш}ڳ-ܥhW s'QYβJ2_Gmr|0FO6T68{C"@@(ͥj)Il@Lr)R@9&5RMRX7qlQsUyVdUnKZo@!@@ @Hn\#i!+pebN:㾣]&9qrYʣϮ '2`r+sLtr#mdL9W6@@@Ol47+DtڐSvi0K8Krm댷S.sUfKEMJZ!ڗl\`y=\:xN%ewicNL ςD )'`GjCֳI XOA'U벳:%7_! Nw);]&>&a6⨪b|{[w;pjb+ 酂tkHb5Q˴mM<݂_!vaMN6<%p/W9{ECV\wSɷINvKua07=  @hn67Wvw7n+툆&VXT{+/7ѧ'LVMw鬐?gr!?A.,0d潓{,3Lۨ]QJK~7CBĄt)vH@4ܲvw/QNL8Gs}n.ݥ5e畟+A_J;+{њ[XbΞ6N[n8&5wx~eiʴ *eOi Ak!;6yc2Uz4\Zkesb[76k?#GmA@@N  |}]%ʓ3`UswtrΈTrg,9{{2zSPnu|.݌>2Igz 8B'fY%fէΕ=$ZRPn#ow5ZNDMo[,:-neQTHY  膌#6Esڳ-q|}*{UtB2d:ǫj[[ijRrr#/PO v )mA/ayrL:K|"P**5 Kjq=T%:Q%3IP#zPC`yTOurge{P债z4TX9h99KEKEzz2Vxڄ#:n鰯ɻbqr'P6~99pH22̊j^s.z~ld(vsY@<*;|ɓ2UD JKNp޷pulKUI()To^R"k:4qJs*^zwX2kˈh-t,!1W   0Y.'Ϊ3bI^] Oy]a U|Mdl<>*ܗ[eˤ^:LWIOCNpˑyxzyt ~K#TRksDh AO`p7s/t3ҿTo۸@@&͍> ]3:SeNTymKZdU쪢+J hfLYwhU8Jd%&V'0Yox4>Z|8u^%zzyƟoVkyt IDAT׳H`lJ ʳb'=U䁙[JX L'M|Ԑ:SX.JjQ!Um3dH㶕5TAlvhzrQ_K ,;ÇJpsrڠ4\?ԂL%S9mP@@@@OnjA@@@ܩ6(     '7    SInTNZ$7w* J A-T;A@@@@@@@`* ͝i    ~ps|P     0^6jx%!3ŴOSmk )K{ȫJ"<ڎUDcDjY%mXצIQ^or)y IyU*ip(v*h^7!nM*ɒj[yIySq!m1nVZ]Ȟw[~|},S#9 n3r|y~eGUoWimB giTIzR/Uda$4F!^jm~UFWzqܴa2b cu*hNesֶa@I*V9iKMcBK؞,K!ʿ,l9%zη86p{ fJOG-mlm,FHH`߳E,T\ƻ~DF%Io9MV mD#+ eZv4$֩a^/u3|w =!vJd۴s(f#(Sy1:&Qύ֔h^Q)E֞nvGmh?`^(jfiԅ'-7-aȞ1DFGLP{X7+te+PCQWDr>;ˍK#rB.0rQгZfXh ##01nW.mok\']z2QӮ[(vQ_EפW Jc%MlJ4nxCV+-o<!cQ٩^5Z))[76(֕[>i:X^g,w+iicm {O;r+*ШxTȠ<]+G pHjQ`Vau29H xFO?,Y=KqBܰ䦞f i Ik&:Gu;|ĸ!!szJA:ES峫 gu 29ĩ+ F)I7s=jXG{#GXX\4VH٩󏔡P^nًBx 4@{|}ο_ .9PkMrڜ"'lBaLKGީ_7~ÏFU$-G- U% CIBmt۱+4 v@tz2܌t&- oh촇ex@2HtfMK|)Nt0-O&C:-d Nۮ©?M~=X#wsce;=T"@٣r%pinSXs?v) p i$"lHM@nIE    "77    '/A; `$S ?d.p2cRRA@ ^9,'hեU_ZWY7%R].q@ =dі^R&HR*NJ}ܽ26o5eP^DQ>Y<)͐GGj*x<^|xw:,w2r{wwYgj=UQAEٕb56ε%Nt ;Ӌq d߲ʤGKK{ߴJT4=ۢ4I}O    ss٥Il?yJmו;+{њ[_6ND z=@L[]VjK˝֠.iaZݡ#ڤxg_67Mk͊zπhHQ)ǶwO(wV.9gLt(HZ7Ny2{Ɠ* ~XkA~q|8D.wΒsѼ&䞪h2+|*EQG>v3 Ė(*MOÝ+G={HVSkU}=>wk-$._ibבL{:2*ֵD[VwNŨbuHaK6JzvR@@|dl˜V9Q(+Cv*zLA\55Kck5mW eTijCՐC\ 03H^ TqrY~TGW1blT~te+Yޥp' vg܉>%(R{t5[`IqkH#p WN6y'T I>@0a ~a_,N D$KBJ^iw+z2x%IQ@@&?{_{/֟3)E{ptkDZM?7Tk'b0)'oE  0rۏSwȇګo҂_$jANy ;;4[A@'ia ʧVyxG%͍pN):ٔaP +cRlQ  0psG 窖^O1NB{Jv%]UȰ@@`h04     0>ps# ܡ`;>@@@@`h -5A94ь21~tYH'_˭eDK]О~Cs8Ʌ1뮒3hH O#-1--<#u(9nYʍ6x͓hrr1ߪ- @tZvEV]>C*jfV;{\-3jXui\dyZlbհJ^AB)*NJ3ܽݥ4l]6WX``~AIn9۸7aZիr&娃<?C"YѲ\vɶyI0lsVN*VaOBI!n57V3B\N ^[a7`TMnXɾh.;D^9lN/订^N,]{ǙVLۨ]QJ پ%Ng%@)}*hvTg@OK{T.sK ބ|1?N QZdk(zz›>Ew2v4űo_j*5ؼ}@-wd]uvnr&Ku_i>6J-0PxV:5Y`]G0K座**d?Zr5Kx{+76)QYˉ5f}6Y;GbJ\bPݲD/4=VDwgB1t8 _=UaC)5bHFÂ~/i#F7HAB"ʓ3`WIHbG,̵ Έ";˝{4Y+*U.jv;ufO݌C}jD-JpQOZ6>ٹWNVhם*kThvT/ݥ3 r1BmqLKm=ơbY E&EW, ºY2)AI16:Wn5gw ߩa1^/R٣HKT߮,4/Jr,>8_٣SeAIլCc˶+m5^v`r3?w}soGz>`j Ş)mq*}&*ʱmRjtM]mbp诓q3r1&gVPCC{Ϗs?dXTm,jjk X:݋83MCɖqiNSPR;vAʔI9ٖ9;nzeStX/)Ffil mGBՐCusd "Iz+>"(O\z:縧*=vm 1VD:6}EFiC}cԙ$uRw("|Y}ukYlZzx* ti=9g,'R2Yk- OPlo<)aҐMxt%u.Vf(i9ngF)O *6IQ;$G;w}{Cfє}/Xڮ*PLX&垯35=ZfBE|5&%_ 6x~sQO#\HyO4_-} ӊ3(2 Zt9#Sg# IDAT9s=۶Jʯ=07WKjQ42{˾5,\: \ [עlk:]sX$U2I:[;ɮ[nc 5<4 f9vXF~OWa! F̝I Er}Z w.kݟd#3W5U*(-a5vUKy @:ɄwA})n [0c2Cl+v%׎JK;q͏sA~w8o CwzXAw4-j,iW%UJr^{(:}/ mܢ"Kt3 & g\t冷i^]GͪOoXvlpÂL茳hD+Yn<<ҽtf,?Ebjx=cUlr{59(SMXpVqǶ"Q3ȫ;vW?׸us+>۫jnLr:3|G-y8$8x?h4]oc&|F]uE-~ÏƊ5O҂t]|B;i<8A'Y݄'쮲e /fl} }SبjWBHb 7Cj>hj';E }K]#c^2%_`Mm:sjTߌa;7;wRw-xM LWwRd_ժ;+˽YdSs{/vK1:1IK,ɜ+9byJe4%sQrATSyjX:E=ʒ]~U2(9YE_rĽ<:#dn~glTC4*ͲekZWğ64Rc9m<$x=;kb>ESZY>VԪoj&ؤon!!3DV `js+ 9i bj;#_k.06na7(ޘnmL1W֓E%eɒ8WYtAyuSpRS4SIGŻcTPAזKQ1cSAw+d|0q a<&7rM_ެ7iam!\42U8N.D˓F`:t=U0!j29 ^qS񱪙쥘˄D.V% *ǥoVMʥO^> , 0pg۫8+HԤhSj=F'uXUfH~@Ydŧ7c+-B)oI C$     P@ZF)HZ(A@@@NgB$aJe,-A@@@Nܡ#    'P?y<)?X{fn\^\=2˹͏ЌD}z Mnpf/ųz;lJMj>'T)#p꣹^6除-[|"#2%Yb49gQZ.vS8%uꦏ(a]A tSOGs`GVwȜ$~S`rc54U :ߦ7T]@@N*S͝%Om,M x Mn9/w *~4ו0Go5ό*NS]f{)zǶXWq?J2~tHSCZ⢬^٧A.ev/A;כ[X;J.챤NVeDkGCKF|Lv%F9z #$flOw ̲ҒYd&hZݲR^Z٣rGP_zw%Y Cy %`ezJRP   p 9T]&RV[Rj!iB_ޔIۚ[^6lNxVj:>ϻݙ-B_6y)f/L蛭~}]]CmM#f> 1_8VƲu di{V&O MnzJ6[u(UmzB?3D)I&P?KZ0<х]qT|gbQZ'iBq'ԪmwVTI/%z%se gMW*6קA92/Mc+%FOCUҲe39;_wĎ6.=J ɮz6X*,P7t8v8au@@`J fssm=Lۿq  voK4jʁd`|̒@@@rM_ެ'iadV84~ st=rauL9c   @hn/i!ۺS ڎF67HV->A<a i#P?7w? ( !NߜAcB"B#h4($hn!"4>NߜAcB"B#h4($hn!"4>NߜAcB"B#h4($hn!"4>NߜAcB"B#h4($hn!"4>NߜAcB"B#h4($hn!"4>NߜAcB/+ BՈ|I̺!!n1-$fK{/g*unWtLIotAT%P?us֤jWxdm*ovʓ*գh Zo`鶏6-ypw!@@@t(ͥ)P^7Wyʹ:)I57-Bl55xY9oQvu^IE[q@@@@h͓-RWOjPsmʁsewVM)A@@Jl477mPW,Hg Aj ēy2)=w}som : -St֝2V%u>oFқV]X:Vmuw)V.Z{˓kt׈DUjnu|m+XL]̓h`7:k4<˗ &n|ͦw ДHN#Ow5?|7I U.>wpwVVLC#60G7=Iy3gd3X95 XlK8X[:;#ѕ5F@@@k4ke4 iK$6 t ]6%{|5 3"'c|?}c-Y/v7nUjL+Uof@@@@6ܪU ,P|MWGyj(֬6,> ۔f5{{3Ȕ`{q氖xµՄD`zp=NRH'3LoνqT+v=cU[yoYTJ0:885MC(5rs؄t煸ܛ~[k8c,k-ml W_wƾg5Zz[<Sf&Q7גq9@@@@\oS GEQ=<=}y :3<}sawwYV.doq?hwwOVFimc6εzw='JO z-J|^.ҩ8UwG${{#w5={Gj?cѣծD|KjF@@ A~4~"ˡ)`ˇ,09h-, ,n._v(l_S'=Z+xlic᛼E?"جj/ګ uViYoSZzGųc Q6=XMcMk:i"P6eKZU ž\n{ܽWׄ0=L-X $Ӧ<䝆5(yvp5SŞ.;B#Oܹ1n595Kgg8j@nu|.5,Τkb"ӫks娧~H%ei<LJrg,~ۉʭw촲C=j3'jݸHrv%x±v,;N7|GZRgJWzd꒲^cBBGQ۞mEOǝN00Դd[UWﺪt?#Iզ\Π,^A=^7v.Sw۪cS٣,M2J#u"99ӄbUhp̼.9Qeŭh03@@ @hn\"<]FQBBZP==sU_Nt"T兝''S)e̓crxk'vw)S&8u,6Cr2_GWjsEVC|=?`=c @&<0% u#c3jqvUbXP[*]6z5HW7@M_i' ÊU୿ӊ]u͘tv^iH^s@ױ3ZyTUSr_䡗?,~5pTzU  PHl47+1)10[xF\'ӡW>M:E8{д3^=g[uU(ԦƢb2V38WFN"݂sjF] rY/֡Q>36@@@h37FܥɲK+[4%ʜQtPO h]6iQ@8 #N]pa^,FN:'k[ :j0A|c ʚlW  ĜOA{qz7*@@ @hn?I 2r||,Т W O\yBrk3NTg7RrsC͈:-@u4Pkl e+!IWZ,:8g,AR g('S_{<"*oPP4r; vsvΡ гtN>*jU)Fsɣ*o&Ǎu&b.}g&27+k-(WS_Uo4Wkɛd`ҷ,}3ꥥ&r J o'hLL,0]znƍJkkk|FⲣPm1!TK}_VjIV!ЂQq +5В^Xi"GP>w rM_ެSNe ]C!0 63( %@ @'P6Uw ZuS%(!n oDWJeOZWo9X(  4ig<X5Jte+c{f  !iS    ~QBxB*I ~¨\c6     'P?7n0jA@@@@1@ǐ    &h C> ;@@@@`6aDsC &    c hcHa@4w؄!@@@@` t     0l0c!A@@@Ma|1@4w 1$ ;l    0:6DsMA@@@@1@ǐ    &h C> ;@@@@`6aDsC @hˆbZo5e/ Y?$-ŗxڬ7񾺵 –6.:-xܯW %J4 D7#\gSu8^7!nMb*ɒj[1!%n(ovʑn:`sqḛ^waz\%}2jk+=`G`{{y)d*5MC=mՆ|@+@\j{sj;IBވ+9_;k&bu-KBx{ }$T/:݋#1qOûb8cqe@tz72P   \یe|ZW2]FJI/W赴q- e@Gϴb*1wIMɉК[h@hnvToByT_חl# +o3· Aj ēfvuV]=mm5lbuil09e geBg< >N+]s>>at Զݥf~Je`d68^`neb#WRx)7:'T©L# s-1؋j9@/;; A@@#0h.y/ k05k.xQԔНʯUK`u*rLMr`ɓ~./Ij)M25v@@RuJ)bjBx5˻|HW15a>Z:;#D^69pmrkݜK]̝p"uMlMJ괤itKY`/ *bObdaTtuyqEl\Xh@hnդk!. Fe g\Kj!;6GVM0-0u{ ?8$cb4s"Qx]v;t@ ,   C#P6B 7A&b%V(Po(\/~4B;TYvCI/p8" }@@@ ԏNƝ -{T 6عB>ׯu-ZL3F=Xf䒓Inn/,m ŠzThaEٰ򷖳$b@@@Gl47+k2;ADbj3r~["8VPjێ˺Kaٺc{R'usR;_&E4NRԝ,T%N{)z//;%    9tճyuM*e?o/ oTyy2܇0H?]*;Վn/˵]/ 1Vpge۔=luJSI""okM3?(?e=N_pLjBK߸q^ommnT8v5!¢[$wU҇-TWlK)    01kf'#iabC51LnM@@@@`ԏU)(Z 'LQ3J!$S ڃZgSTQ[/]+aڛ']i44I$P6KSf)     `ͅk*d(j 77%    c&h'Ã à    c&h'Ã à    c&h'à @h1 ov!^aHLQ@4w1 +!#;} C ;B @@@@`TGsQ&hn0*4B*U!ϐV;-d@@@@tFsy*<ԀL#>F] wZpE L#N#i     0B C@47ZLDsj,@Ds8T@4w ʂ@47ZLDsj,@Ds8T@4w ʂ@47ZLDsj,@Ds8T@4w ʂ@47ZLDsj,@Ds8T@4w ʂ@47ZLDsj,@Ds8T@4w ʂ@47ZLDsj,@Ds8T@4w ʂ@47ZLܗ!j|I̺!!n1-$fnvEz^d)r\ OUA@@@\&*ɒj[1!ޕ,6%]dSszə6S>XuΉc0Y)ն_uwD'T°   0\e>Ur.iJR#75"4agE[ƙ]oJ=mpAəv˳Mncឪ l @hCF`i\[ i͌wtyy&?wx~e`,/񾳲7mrt}¤8OU!@@@A~4jYaX$eEͯ|q|}Ko1 ʶg[×=kls`/ȱ:Sc,n̶z`[~,=! jJ\9)y˝(I@gk 5LY^{-,-8RV=UɆh@hn;-4mqLFˤHOLے>n<Wo1K7rVuJ&Yח{V~&tWκ) y!V|nj"3dݎnMe<77YNH9R{-fQ)W67Se;0 fssm=LۿqV{wKvvpe08(!RE6r8źH0,XNMXKx킅Lȍ],t"2,y*bx"9$VS;;;S\\{jJ >m}5լ̮9N.ߴF:7\h:fm ^s( _@@#dBGZ{ͥY%+5vfM4Y.╗Z֎Oymrɕs/KV;<89Pr  б@lnq:E ӷǖ@)+{_bOꗽ{ r݄m `[:źR.QFQsG! vyv @@`"r%- @xG@Q}m.a>@@w.@6@  P&>J Pr5g E@Y}60:]FM$[fC^79Y  Mr~Τ6׾tLBk#alts_2@@`gsRJ2I&O A@@Sd~M)IU]])RWI G^[&{篕x{ֻC@@>gs+\^SedZ%__)_e@@@fsa}$~P[dhI'IbuN@@h͕ͭ_3ܮԟ(WJC$  pME0Ha}1@@`WC]${_=[P;6%@@:0E b?3cW]0/In\gJ*%k )<@@v\}64h9ߍ?=៚E~ľș_翺cg'y-  46^ifXM'l\P3&S\@@]hͭ|mW.y  @lnQhx$oPjb8_6K=@@7h!mT`)so@  4ks)Z1(  @@l.an/@@zh-N0h@@|@@ Aܻ|W˻YA  xzv2]B;3A~#  Li6w{+-7nۋfsl@@*gs7{c卙!Rqnmb7  @3bg' W8@@z93jZ.W*nvB@@ #S67^nxb@@=esϦ XۻH{@@@l: U*R`<\Sd  zVϠ]#h~ i  Phhe GӉA=d^&ʇt=   ͕y\Q8ұbu],26H8_@@^hݨ6NL"dT%;wˎ@@JSr{TFG@@@ M672Arbyn)!  @lkE 'ld,@@h͕s2넯  lS}60w@@"4[0h@@|@@ ]'  @=gse04ߞ-@@`ozXwA@@`}es'*  F}6CC\oʏ@@}es[O@@hej]  $@6'xE@Rln  Гܞ@@KN]^>}# @o\.AT8!m6Fd?*@@gs7[7WpaoQO  )[6U΋ۏRS9@@@o4.nu-jy7$ @@`- f7:'[1@@Wfs\-W@@gs s@@ '4k._ s&A@Yln7@@ ۅ*}" ,>jtX4^d$%iuνuż#N7qg}@@`fs҂D'~L*Q-`|{aG=0#yI4.`Zl|C@80AL'OwnhһH'g6ΕWyߛi&ν4mav  @lnQb0WU$Pїar4N,ۜj9O>@@+[-~~ a >&d: W^!o {@@Al4]pX~jS$ IDAT1~/-vx -b/  pP͵gӅq+ ԭ+-pPE@Hgs[-(#݈se|xJlMmS"y^E)MB@Y}6waD3hG~mTdbB {Y=7a @8\9?0Wbh4 WWW?bWY!ufJlL8w)|E@޲6tI&e*yNNjiW?k!1;WTC`4QLL\z6;@@޳@l_^^\__vEB"y͏! lK.Fb[oЏMG8S@@*4[έhG/+I]0.m'  @ooj2:   Я@lnqwS/4# @l.a[/B@XKi6)anф=  = 0<  @dsPO@@gs7]P,4̭fpzװͽL(B@' h+axͣp佸̄R 1{_{wC46@@F}6w돠MNCA| 7!lrlv»A4G޾D@#o;̽8>RoQ2iu,{qvKU:~ƹ X-a>  {(o67-gjӵN8fERlrғ'QZ~.yozN  \&ew˕doÏT!e'OwGhx(tu&  u:+lgW(mzcqW78'$=Ƴ(7.ok粍  .(}}ddzZY'A=Jù;l"Ye~N[%}' @@`ov"+Q~ƲyZN}̔Ϡ˞ga8X;[-՝{;  D6W?:!Қ:]%Fh: N/_iUa!ߡʬƫ_6Ά-@@w#>aт 8x$fjp@I+ZT""0y.@@!4+/~||yys}}-(]k=D  :64]w1!֙ϱ/m{ C@x6,Z(vWPg6  js3Ȣ  n 4ksha A@h->K&  fsM؃  гܞo# t!@6 UD@Y}6Ղbn9w N;x Tս^o;nV8`G~@@vRi6¦aIr\9YR6Uw9ds^EYi*ξW;Nʌ@@ht|t7< (&6]=D*89λ4o{qvKHjM;t2tk)   4'Y+il~68iqd4VyږJOl2paC@M Z=E-DX.WA9V)וCǃb9.!l CZ]-g|^? EhwcF@-bmf5t .e䵽;)Up.^;?r~iaq$~q j0b7@@vT}60l1nf l-Xp&:c1ӧ9Y3A]INƒM-Mŭ_dJ0ki??tTK$,P~%2  %4<`*l)*lMNJ+bIQ\<b)uٳ *LX  Џ@o\ BijJ^MpPA3kq#DZY!p u`Iڈxy9XMM7[2=  Џ@l_^^\__K zQM>dՆ7! lhb4GЊs=AJzy]1B@Fi6GЊ#W! C?L @6}K%}_G@[i6 VZop@@-gs6-c  l$4[0h@@|@@ ]'  @/(濜{Yh wR)~om߽CniQ;L&  4WZ05D5mtn֠Wf_}\0w= S]󧪕ޯ/"lw6  . n42Μ̗`0Iw%LGae,[n%1uit}s@@`fsQ{q|VIVJ_j9w]/œ+l֪3v#   >YL*w|t9/tpMmR?^VR@);&  N 4ks7m֥o/n? @% pteh?wK5O]>w˕ħqҰ7.m(7Um^;6@@vN}6w0WͮQ0/zX@e ]Z+t[q3  M]f7~mI6[lGʴ@@h-lXP=q %*+  oRY7f @ln6p_W @l.acB@@h-^9anф=  = 0<  @dsPO@@gs-(fߵk2/:K F0[,;;ս N4g@@J &J-A\+tu:U/͚ l,| 2\ѽ}I5ce   @>6 aq3zTprv\~|yt$[a͡t|t7.v2 `XSb1ҫΫ;+jK*zm@@fsUk K<=4rNʲ^PL/;Jë&Xgr+BԌ:Uusa6@@*W6WZ^wMZW'w1<|Jr2oy; \l*wy39f,s{`0Y&_@@Ngsw ??"ZR{;7\ҹ;  쫀 M]f7m_ML{|\  CMũoZ}c^i!oX rr  }Vxvu>[LB@Li6w[+-tvAt  R@@`fs@[4a  @ds{   Ѕ.T@@g@{ՙ[o%kZ9KwFҥ"Fcc|@@Di6†a/|84_S54L<'\B[*of`SOr-JǢepL^wq'  н@lIZțrӉދ@E_ >Xu+sXgR? h>?&^e59 %Olu{]`dd*B@Hi68 bGgļf X-q\v5++H>RRQΎ7@@m ntgMlW^$@@؎@_\u%EFɥW 8=5>7l<=X4pmd4Zp,C e'^@@6~||yys}}-^||Al.@xKnoy=eĵO0+D@Li6x{=aﯞ D@RܷƝKg;3& @lnq O\y5  ME ܢ {@@z `x@@.vJ  = ZP-,氯5߼z{k95RIzo;S}gxc%sc@@@lnqM\'| ra@ ʄ7xM}ѷl{3w䷲{Tj_9<uw4XYLҜ]6V|  [ht|t7< ahBCyƦe>%mJ[]~|y fxvKH7iTk[tq,8  [h-Y+Ѡ~RMNCUNF`f#]Ҫd9g5?cvX؁  NlnZ:),MۙqJcɒV(\-?I76K;Zxwٺ1zc]@@@_5 `uac6 N|*b+["l>ymNoyZlVyfcŃ@@`}ڹM:ƭ*PZ]k>wO'mʖbku<=Ch6 AL4ՏC&tip@@B`7yk0 7?Oeb2f{Ket^W?V61o8V؏  P'>bU=>} u ԅfWNJp[   zڐ40)eb~*b :哋mP9"XIC(Vr(03C)TP|\>A@W~@@Wgsw ??"Z{edHh(ytLv!  thvC`Tpk G@6h-G/,wB9\> l[m_;O}g>  KMŕ(Zإ\@@@l.a.@@vNi6xE  ,@6  ]B>@@zhtAKd!󒅴9[miGuf-e4\Ot%ێ5^4  @@lnqM\y*/. MSNLx)cұmdwEK}i0ގƓ). j4O/  @[xmvCO #oI/qx|#cn244a{XmI4\_els^م2OW@@K\檋|Ǔ X-qDkϾ>+Nə urwg:4ΜPfFzPƹTΥ @@`{;-:3Z~.m8Zxw.qZ,oM5GeǙ_ݡ>j^[+#nP  t-k6WX'e9y)N"GyǫzG8:2q7>]l>tX3Cv,]K.@@x;^.(}:'WW겴9 $ v%ŲQ\bÏn1mWQ(f5tϦ Ry|E@NlnrZ%hr."P13ϧ踬N!i]f5oL`I_Ɛ̄ @@gs7]PC[Rp 9 )K_QUzmz5 IDAT25 ::UC6iYa4(V3?} u^)4  Mr~nVs]RՔ̦隁2% F53JBxIf\?1Ib0qriu dfh:3v ^)p *lb8@8(޲6n%S;LCxo*(y$wAi\k&\8ڈx8k;, )8V| f.9gd0@C&4"_k. @l_^^\__ˣekͅK\JZ7+  lhb4Cg|:mFg@@ 4/p^ٓ%x9a&*l  Ool! #4[\iu9@@Mgs s1  :M> s&A@Yln7@@ ۅ*}" ,>jA1X0 1,609zhѳ0<  fs҂Do'\L +o/Ix@@Wgs7{m2< ϯ 92zTprfJj'  ͭR|H?z .=<\;  k^6Nl1nYaZFr   />jA1*n;R` +h\SK]d  9\sJ[4a  @ds{   Ѕ.T@@gWZ^g2h/}߯2e$ޙםHz)kz-3 m2 [Ξ˛dT6@@^h-a=5M즿,ʏr %ʡsLݒ739 } V7d2XSoUr5ħ@@`c A3jo$S˘QV`TjvChN2iaA4Gΰ4FƳS<9CN㓧UfdF@h$4[|0{.VD̊d4V˹ I%*[I$ݵ8; T%W*qDqNm@@Mzf'~2Rh>L-쐞#P=Ig:Z~~mVpO~qpXfN-@@ v"CFŞ\}+ x]h8p4EW[mJ iKJ2]/ֶ G_82\]pX2@@"6<ҩ:jXgW(m0I=1!Nqp96N6 3|UZ=Ln+u6]2\J<_6@@zD&}};dRΪWy+YLo _kUZ-ovCe l! lA}6we<51jEe҇d=::UC /T,㴡W D k[% %̳p6|4<Ϡ2U$B@h'4+4̵q<4jb*^wv$ 셭AĪδSlk\P1WZ=#)1cg3dh:om55fr6  }esMF zMˋECR0D]eٵqo|fߺVo..CǐYI$^*3iua`eYr @s\?O?>o\n.OEw;4C  M]fo>Ƴik̙%  Н@lnq&{bOSWÑh # 'WmIqEq@yJ -Mf  ޿@yM؃  гܞo# t!@6 UD@Y}6݂b[,+7o K[ycxڮm`b\pV(T~5l5ygw*@@*fs+-l暘O9ev&Sr5dg79o_osVKpuão;;ߪC*~!U|Mb\\s(@@nd: e:t|t7<}nkrJ7iƄe/ ]=D*WНpqiDy7%lNz*<3;d{(m@@5fsnJĺZγYV۹i>53Z,>0{|Z ]J> Y~ghϩ(;l28" %W6WeE5꫺^՞[QۅI实g%geWfSgW0Qқ~2w^zz  P-[6WN nٚm1W\z$P5bZ9‡̨:rU1uzfz   @@_\=V4| K?)}x.5O+W5q?ٿ3 rCkuo  u}fs]Umǒt(Jj >lu||rfDYĸw*Ckt/   nXM H5P#V{Y|6*}u0)QnSoeWp2 |RF YG,^8BV}C͋17VeL@@`fs+-P7 " '>K{xjb@@`fsD[4a  @ds{   Ѕ.T@@gV rͼW@"ޑ &rU2k7ʴ0ʯkcaEɌ@@]i6w{+-HX|RU\Kn {wG~_./>xqEOƁbU+Pw}$Q8JfhQq]˛  @lfM㓧UϦ=zT!,=__g'ܸo>WK;{Au<|&Dq 9\s][+  y|{6 s%/{~Ϋ .c= X-6}}V*xͷûk+𦵴5&{~ MYr-Ǫ@@hݬh5$0{:{Qiܮ@G0J0;KVwvYW힜*[LF# nX ?jٝ8R`>R'pry]{D.02=I ΖR1quK8],B]eK%oءRkaK宖3;Ӱ@@ #4[B6WƑ~zjx'e Յ|&{~u[SP4+}G@ ӋdO$`ԃ~i^aܱ]}Fə] m,ΔʹX=D* pɗO3tBl! (4[4kʢ :s%M*輮~0,~ ̶T: r^$ʁmHl[184tM^nwInJӰ'd؟?   A~~E~.^׋]sB@:lG:&:E@8x"ئŞسMDBf js;8%0}@@`sJ -lOK@@gs s;5t  @lnq$ܢ {@@z `x@@.vJ  = 0<  @dsPO@@|@@ ]'  @ds{   Ѕ.T@@g=G@Bln  гܞo# t!@6 UD@Yln7@@ ۅ*}" ,@6  ]B>@@z `x@@.vJ  = 0<  @dsPO@@|@@ _bZu})W?SW3Z  a(kYԟflJ=3~?JxF~/  490F6 LsRJR.h  `W$K+7y  = 4[-bmkW&=6~[ZRJ7JM8@@`zJdJ]y>Lb6IR/A@8T".\[+J#%ҡz=]ω@@"k6ﶭ$K+${Sw6@@H}6wgAtT@@^5U{l'6@@F`*dy3p E@v_lѾW~_+%{: dAxD  .>u,P<鏳j̞Xm㸖X9 ?# @B~d\Vjğ)+U؏  oE ]p=*%?M?kFM|@@h͕sױKW@@Ch%=;\7  MK!-@@g=G@Bln  гܞo# t!@6 UD@Yln7@@ ۅ*}" ,@6  ]B>@@z `x@@.vJ  = 0<  @dsPO@@|@@ ]'  @ds{   Ѕ.T@@g=G@Bln  гܞo# t!@6 UD@Yln7@@ ۅ*}" ,@6  ]-<lIDATB>@@z `x@@.vJ  = 0<  @dsPO@@|@@ ]'  @ds{   Ѕ.T@@g=G@Bln  гܞo# t!@6 UD@Yln7@@ ۅ*}" ,@6  ]B>@@z `x@@.vJ   H+X]A@@w[Xg?KO#-ea'  @6> stream x+TT(TH-JN-()MQ( (NU5Tpj?& endstream endobj 5 0 obj 55 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 1314 806] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 1314 /Height 806 /ColorSpace 9 0 R /Interpolate true /Intent /Perceptual /BitsPerComponent 8 /Filter /FlateDecode >> stream xiuUq(NlHbiCcz046`ZK]I*IH*UTJ*J껒T% HBl8I2Ⱥo}߬>s}7߸h{W]u^=}PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%P:{RJ@ (%PJ@ (%n+'{/%PJ@ (%P5ec˿PJ@ (%PJ@ \4es|ۿoݸPJ@ (%PJ@ %MyʶLةM}=~ S_OeIS_Oz׭S_Ozӧ~zS_\ۦ=uS_ϙsS_wM}=oS_/{S_/g׋z}S_/z˧~fS_z׫^3کM}~8w>~}gwA;ïɳu|o2_k^DZ~b\?1?(O'SqAq`ĸ~b?%`)3~)yhbgs SU[z߇b;߅P)G_lH鄦l}~ 1g_5e/7'kVj֔ɚlM<\k͓֔5cMٚ1Y3Д)kS6O6t\c5ckԔ] t/Sm=x}k }U#w>^ϔg}[G?{w}3w{g&}q{zOdS{U Ou[ïm7}0cw9x3.{Jn}ofrrm 7{nyΫotWyo|tو}]z7l2wxCڭI17<ύw3ۏ(_;_}X;?gß{GO;s=x'Ưqz]<5ekXSlL 4ekɚlM<\k֔ɚA9#}>b4y^F>\۪bgbL ϲ/4,g o~tO6\?>ֻmq\_w^|;mj{ Ɣj u'Ϧlg=ڔ緽fk38 ^r}>s6?g?~b0e[1ri;_&Yp41ɇÔ!isp}6\zw'V ɇ^z+6~Og]d-Fu5+5ekd@Sl5ekɚlM٘h֔5NI>FgfK0Wg.ԣXP>ecvWS#b,{ڧl{]Lgϵ S[!S5pϚS6_{P{}dm `mgٯn}m q~K~^Ԝ];ƃo|=ͮGF[0Ps7 Ϟq$)!a П}F|Sh~ʵMskϵ 0eKIqmn}]vc?1w,?~? >q88~b\?1gOO3O'Łq83 1'poc`,;;3](ͧ5~^ޣ@mo_#ΜUv%<>k-ڿlO}We:|oo8o?k<˶~4e1v?.F, ~ٗ=ٙoq)=e6iۧGma}\gOٷ?O{483S5e?[ϲ,_3ֳl=kzgeY6?ֳy ;tfӮMm&Y=3]e{A!Iڳ8eO^4}wFǧo?c͓E޳#jD_,6.|C"9FkVYe5=ֳl~ gzϯYe5=ֳl~ <7br`x^rC SU[jLܫ쳆Nk`IV$3􎙛ulbq.7Nϣ+DZlM<\k֔ɚlMYRSlL 4ekXSlk֔ɚlM<\$kAvє=2e,|8)ɚa|\I4O5#Ł8=xvǚ5ep )[S6&k5edXSl5ekd'S'1kϲ7YlMُ7r^$Gu5+5ekd@Sl5ekɚlM٘h֔5)zA}PJ@ (%PJ@ ( sU0u^_"ϚPJ@ (%PJ@ ,& 4emXS3TJ@ (%PJ@ 'S=^$הFlM4)PJ@ (%PW!~6e[WMy֔}PJ@ (%P h.gq$wKPJ@ (%P)MuԔ}8^PJ@ (%PД]㤦@+%PJ@ (%.Sm7#_eAPJ@ (%ؘ@a#aMٚPJ@ (%PJ@ (20e ښ5eJ@ (%PJ@ (%);|A[S"R (%PJ@ (%OK)[S66J@ (%PJ@ (%P&)I7.]"PJ@ (%PW-MuԔ}~PJ@ (%P4e8)RI (%PJ@ (%p!nr2f| JMW폌W (%PJ@ (%IU_ÿ+hM~u#r P|[L;3hF wSC{1PJ@ \|H<)S}B[|C`Fss>Ι-g_{S^KYur^͑KNjkO`w_ߋ~*% l"{>\S][&>nd3v-qȜ{qgv;g&-ƙv V/nXa.R`ⲿKh.AH`K[z1TJ@ (%.J<54eڗ; R?q[4-ɎɅ.mYwk'r옠4gxN1*sDK6([vZ&J]Չx˖<^uJ@ (%.n6bBVҔq|]7Rj-.Ƚ->+/:;uh[>uO7k<^dfmPbma~2乥wx)%Prv'_MTe?hyծG_2<|(If3o8]EA^`@L 28aF&Qe*F)D5 Rj}qY$Pʑ)?\u35i8%z*Mi`x8dP.AKham]wĹ&`Pht@;^+3~vqà sK6xy%8gPP]]e0PJ@ (%.Yy˔=‡w>d dX05p [@yl1eUV:vEg{t},6+/}i-^O>3a_,`}} P-ҊٰLd)5\ma8X:8+g~^$Y PJ@ \4eԥrq^v*?`1o.F (%p h.gq?es>څyh4p.d,s x @ٛIguŠ[0γmF0dY2Jff+a%PJ2%)ɽLQᓛ/KϬ̲ #]܋p C7 r sYՀxU%B^)\Fx̴pq{W?%F٧ll>;]AqYn4gQ#q)`WࣥPJ@ (%pД=>P֔}$?qhgxfh: (#U&+.M]}9V;79ӷ^<ɢ|8Д])L|Z_mu?27f܊l*&3v}L О>, dd~-'fe2lb]KWf :q1=#=n*LǷ+۝15t_φ(e# |:H;|&h\*;gLe8T}4_Tyk2;&YܶU] =xw }Д=>P^Fw'_nȰt7&ە72lb gA>n^.ۼ,.w-;K=}n& "ec%_a/^ޮLf1Zվs;GolGxwJ@Sv9;?/?eotJĹ=3nhW+m4ox>LKoU69n{xҌ峸З % 2bʭK*@e ƫve#Rjk3?l0-]~kҗ|,K `]Z|Z>/ؾvdm§rv'7NO`ǐAxݲLc8;%qW`[<43e c),[[[ɍ`ؤ$Y-%Lx]el*HgJ Xqjm;Pdt,3P5@Wf>ȶ,qEaZ|6٫#UNT&G6=.6`ևҪe˧jw]yI0jqZC/d Eif f0(@墏 e1T'0ha9\b^i  YMrޮ`2.0]f%b%v*3km^k˿ae6 0l,I7R˼_mues Kme Z]-=n[DД=>PʵSv Ƭl) K>,;p7k_Ze3@÷j-xt/pX$2--.Go ޒA{xy)[ˎ,9i`l2le |;/[|VfRޮ 5 L [7]˶є'A#dFV޻(+{*+.WIIf̔rCG\@$AiL}OZz0YcAe)+̰W[WV:AC0%t+{E{yOك_Y3P@%v |X$h׎fH8F Lu[lь^jqsSsərRh[lϤg[R%q)ɎtHp `փ4YSbUV[vP26^#K4}8gi3.'qTA8(r\{[Um=11wukMZwSlͱCeC]aMi[Lf2[L ^瞀TjnO`SZuFV{SMkFy#ÝN)t7usz#q]0tJЪ:oWqΦ;Rzp2dq5veAI^-{;Ro^ثMt-3tn0}MgR`:N*yƘf.MJm[wҮH>Z5Ү-E>BVlUGV2>dJǃ1ĜBy˔mjh]]dsְ鳠eR%=˵N]veE*,peY2S$Z  }dsc)|"0vVƒ/Iޢ@eiMC¡i Z|@KK&#ղC| lVɽ^eo <ġ\ g af%%J,[u>-%o@ysǎ*d#snX2o&;据qFM@S@]*9eOeY$3w`jIrWl^bC$0@oUV;Qme-e 5_t|:s9-; >L4Z Khd?CJXB-Qy&3ˢ-ʰv%Ɛ1$ d^v2jLF5d>0b} W--ݫ8|6  l>°IK͢J`14L vfJ$33ve J 耽ZP% #P\{2 ep UYcLqn%q~lJv&( \4eԥ5ezU-+%.bS8E޵ά<[ɈWJ@ (+rv'O|reƕUKDqw"Ǹ}y9 %oRٙG;(%Z([@W1"|~kt0%4e8)N{W]\bs]W!OܣGdW0k@zn {f*%.JRٙXiv.4rG#J0>D{yN}~u$xJ>'|~#| g^m>$V{1~J@ (%p h.gqRSi[IfwDƭoٟN}~uxJ>'|~#| gһv˭ߞv'<~qB;*%DR){]wA[?(Y?C59w|Ba9Y9kknKڽ'pJ@ (%0rv'7N2_wD2`ʑ\-d |`R@s5Xq 8hxKF'7lp!(BՖVP JJЌlTjn\aJA92Xɘ5U,ܸZjc󰾴Z${ ]džIPzM7YY';ނ*3dPxÐ O(ْC$cj4|&˲'an̲bߨ㉓T`q(aJpbdۗpfmʘ̲>FhL2`Cمjޗ2#!0@(pEY)0I`T@(aa%8fy8e6o:K'!r/O߫җHAUV2FbJVe1x'Fae:]2' w67ۍ춅]I&3xl8gKS8yDZxSkupV*{1(1̘Inc{QbLfNv|]]%-b81dhwe>얕ɽ`Un@X@ (%T˔mo +`"޷0Ϙ}2 ct$#|'INgYB>ޢL}7 3xl/ghlҴx5+A%03°fOt5GΦe)}[%;8fA 5|eOv`>r/e%PH@Sv9;ۧl__%Kp{{ϴ43^ud7j,{Iv>)\B>n-vHN:V#4-sMy2f AEYe20X`2d̶7>,Y5Φe)[%;8fA K=ف[AdZPJ@ "Mu<ΔmWwy̿IGjiwl}qлAG̻٨4,α:xE٢Yw#$[tZK-FȦ-M#hL_J湚;˹]Xo|X ,2ϲjÒ`+3ν}~o]PJ@ =MliZ-=UwNZLy_\{vM[| 5[pHnA$;4Q>d߄{;Mتb&iٴ,e23|ڵOM#8ogʘʼUd!! +%^Д=>PL.q }^a ,d8o˖JLȖAP.y_wH//,*K9r#1kr5kXlw#MhIqM8[X,l4&#Y4)&-l-^Wvvg.vV(9(3ҧݠI>7-S14$[eƶV3+%Д]^B^wJJK}M^/'`n/]Z1얕aDGJ@ (%=MuM_)-ڞ`}OߵӆjPJM߄rPJ@ (%PJ@ \4e8)YЍ(%PJ@ (%'){|. PJ@ (%4 h.gqRSQJ@ (%PJ@ lO@S@]*5eoA (%PJ@ (%piД]㤦KgA7PJ@ (%؞Tj&PJ@ (%PJ$)IMٗςnD (%PJ@ (%=MuԔM(%PJ@ (%I@Sv9;/݈͟PJ@ (%PJ`{R){PJ@ (%PJ@ (Krv'5e_? %PJ@ (%4eԥRS7PJ@ (%P&MGvF (%@H@Sv9;B6,1kHun/XuG4vmUO;Jr klX&cT|k^3ޯNl%P[Д=>Pߚ%]~[K7G*Syչ-s<1dN5p⧽9l˖^?vp;GXy\ycgpҮk-`үB&PJ8 h.gqrS}>-xy+1Z0o17{B oiBi.q#N8Յw`˽lݾ{-z\y÷'w}gUPJ@ =MuԔ=,?9YwєM7nBPv<  AGZNvdi ϶^ ;龔̭Ӷ~J~שׁPJ@ (')#LC`|ᭂ/Y.Vf1, OЌh/W ˌ 2֌`o~sen,nw܂1N*vk7%RbdZdS*qNdyaej:@[[50 Ȟ̇3;2.TJ@ (%O@S@]*>eӝc`€xB팃U1`wDSnj$Z a$/aRhehg!F{s#a10 ؗljyJ}8*a*WC`! }/g+fgN oWne5a].+I8(% hKlGz7-ZH=[ L, \{ɮ^*<CGfpk1$w-J$0e*;{Y_V?Wgdˤz*w |XvasXkQ`̗2&۵'[niig8Lfw]̸obyv RfB J26JU_?Q -+!2PJ@ \4eԥ?eo~k [p{ٛI0a-%[2Q <P’5, < 1 3- Rղ$LpM rQr/hN /lŌk0cb Y }ٺō:e ].X$}r/:3%!p%A`M˽`ֶ*{3MZ܅,3nAPJ@ (MJ@ (%p ؐxM7S3>^J)?$ܿWla7pyW8Gw嶖]jyK8gMrwAJ@ (%pR ؘKSvw);]sI17y fc/ CՖ\u4F:Vǖjb&2etcd8dޱ[ޫ8|Ǻ0Lk/YKcv Jޫa{y;h(h Y_k8ZX4dXưlUvCi-onQK50*#zvcZAkP??^Mg6qN :o;I<߲*eA`/-.s| /Q š%kF03@{.9x-]%@}1m]-Jn1@5hس!9V/Y0@.1q;`_r#~Wr/D{.|p0kؖ{VY4@@ (%N3[4eC){࢟ й/(;~JM.eYp[nY&hteˇy.3n)+d2g ;<,*,ه2,S ʂyXfl)7jaޫ˽% VY[e-5Gc >%v۲J>aϙniUKzK.#P' kmMCS.A%AO>e) N 13x[nʖCr|Wh*@KuŦ%LGIjc`e>3_[nkgƬl) K>,;pM5H %dB ˬa08agYʆbPJtX;bc*7ſjOpO*=-KL8Ҵ?jX6l9>,Oy ,^aL-}ags>\U`1Uuh3bKCwZ:%3, K#a鲒C %\K"=lKvf>,c728a҇cEAvPq[Ss:>*de)i72fհ~hbAkwyXR썝+MlZ؟qٲUmde12вjrk񥹋;-@%tYIalqݵKv'+YePJ@ Wõ~b|O7WrĮLN眲̄ Lƍ2,tޗ&aYg`YW&d[0خAeStY % ^oC@VvCl -CJgjh] ]#gc.m[{-v[2v]d >Y⎭TJ@ ( Y6D~tN J-?{ vg#>,dfQ4-QK9eK4zy sxt>;g% J,2h6,"@cѫX]l&kw'ʆ٤1eA#rqZ윕`Yl@i&L +%$g5h>MڮR1AV!WlvYv [ ȸ8JE yIMX@Yfgv[]ٯ[PWy^$حll>;]A_g#e0xZ 2,3a#[z((%y%lGj_%@ t*%PeõϲE*%@PJ@ (epf >rµwv;镀PJ@ (%%g<2OS v=w#ґ{:-~b-u'xQ+%PJ@ (C$g5}++A'Y ۅ%*?(#sYYNڕ!aA%EFFA`K/epM (mCՖO cٜ&wQJ@ (% Y63e>329-&Yo K1,0y %T pUyWna1+KI 0%g c Fop%gT}LtZaPJ@ (%UN@ϲ'&kn15$M+^fFZ?˒|[jᨭ%;0n鍇%#vbAl9n-}igpM^F|F4~*]PJ@ (%%g<2O){t1cųa5T}b2V,nr#-Aoy/= -:lآ[ 7.h @0 R (%PJ@ Yd-S6>7[>a_<{v[\C՗!F{ Z%4anKd[gOlv exp@oOĔ`чs# +%PJ@ (O@ϲyd)iҗ/>86e0xQ-[@mp3v4/=о(T,~R$cB#ТPJ@ (%UN@ϲ'&knLܮxu+,;$#@cU̗-h5Bv0~$,13-wq}ޱjا}&v->ll lPJ@ (%p гl'pk.?I|72w`j,p&A 01| nx^Bcy\-Yxe)0 B;hbb4C;%8XPJ@ (%L@ϲ'&knO=3 e܋F [i@pxqq+xN3w#qwe-<2f+v(%ڋ݂$;{hL Y8J@ (%PW'=y-o-UJ@ (%PJ@ (#$g5h>»T[(%PJ@ (%.Jz#nM{Dgj/| rV^2o񸯖{ fx#%PJ@ lL@ϲ'&knєx>0`.ܤد6򸯍>}9_ zJ@ (%H@ϲydG훫ϺplmKpZ,8;.܎;ɾ.+(%PJL@ϲ'&knє]N8EfկTk-g{={{h{&rPJ@ (%@'=ykN]'֚5Z|~WwR=}yK![q{廣A (%P,{b)?v>#obZ;[n8A8 L:oa- apZ&ySfZvRΝ$&a#[2Xjvp&+l[ya%%,3Җ^b=kPJ@ (%piгl'pN@5zyb,D* -*-[,U]e58` 3la1Ћe@5zy/1e[jK(dPJ@ (%.Mz=1Ys┍ >^v/2U|> N&M.+ vY4hq}1qKRmYZEY{ yF|¹eq7>vlU+3FcД 4jPJ@ (e,]Vp(k쐭}˰cIA/k64&CGck:˼3h0J@ (% Y6x=O܅$ .ϡ逽 ter@%.+I89Vg@I RȰ y;oWn)I֎ V-jְ$PJ@ (%EI@ϲ'&kn>ewKu*ȐͶoK2\\쳈lL O%f`rgp4|2 ѭUޮlo Z-.B,PJ@ (L@ϲyd[l{?ddWk8F[r@΃&}=eo&dEp#JN%eؕ,K}&6Ȃ2cjľA̖A`ˠg e (%PJb%g5l9B\u̚pUKDLFĮSe{i{iÒhg+ӄ^.PCzH-M3yHZ`, w0d~/:PJ`{z#>}迹gܯCdxf-;6ra;]G4kN2וwo1.kg;_nQչb= GfN0v,I69clkÖի8,{b斫9et㧗K2瓙lëۈ4#y3w-nss]߲qqlxeVs]o?~̜-}f,Ng/&:s˭|a;nqқ3~qJ':YHySE7n{R\IMhoԔݝ!u%гɚ[S6Jo%B Cɤcj rޘ+@2`0 Q蔠12fZ0qҖ\5 @W!`>;t 6/[,C{&`(oKj 8V@<GE Cɤcj 2l\zSB-I>C s$0 {phA%؎hr;Jٗ 82EA2118W k 2J@ (O@ϲyd)!=&&Z<XJXB%,9wl7x%({MJ>xΡ%4` <@w0FL Mx, TT$,&hZ<XJXB;}\C-2 Z¦Rf12JT 5ر?)јs xLKTM,;dlYKPJ Yd-)(F,lkdҰ[9ٲe +UlyÑ8Fhrɻ|9f&;g١iܻca ҕ%5q8JCz-J|yLɚ? oU>[+K2d&e۲/y\cE^}2-J @q@<ʆ1×U~־^b^&`[AfJN Pz#O ׌TO=9˻6-Ao%Xg̛2ϸ]yF4$˙u]g|3S#|G˙r+9p~ow3ي#o+ v}Mˡl9Lfwj`.#mYo|Xұ Z׬O@ϲ'&kn9)oſ>8qV?#w/_t . Nfy-MGڎG3']ΔW.Wƃ.[G4˙܅R0-oOވ,6#MXX[p0 P=#C7ars#.KL4Ke!KV:vAyb1J@ (=yΔ3/C(a~9L&a٨lU|V/k++K%FWc ΰ&cdFՓ"q9Lwe)lgzw>*Lr;+s Jv1J@ \,G ܚMy M. <2fYcK~S-I3T\]%+8&Wan[G[d#˸Gx 8ɻJ2,WdƎJqyTWbx^BϽYjvKjakR'38;va%Tz=1YsKgʶ71 .Ʊ 2fe0%WYc Jv+N;xK3+˭,hBd1W/jٳdI\1Am-*W[aqߍYcL>#ƭZlJƱ 2fe0%WYc J˾qrPV2i>Ybx;M'dD]%sqٲU8?x YaZS^?/١ +%pгl'p{ ؗ|8Nx9΃x.}.1v8r% g% &g5gʶw>87П=ٱRΝpx" t Kz#>ڔoLPJ@ (%PJ Yd-PJ@ (%PJb%g<2O`M *%PJ@ (%eOLܢ)O+%PJ@ (%.Vz#֔}:PJ@ (%PJ Yd-PJ@ (%PJb%g<2O`M *%PJ@ (%eOLܢ)O+%PJ@ (%.Vz#֔}:PJ@ (%PJ Yd-PJ@ (%PJb%g<2O`M *%PJ@ (%eOLܢ)O+%PJ@ (%.Vz#֔}:PJ@ (%PJ Yd-PJ@ (%PJb%g<2O`M *%PJ@ (%eOLܢ)O+%PJ@ (%.Vz#OH_$`M:Ú}9Ox}sSjv8I&]w.h@W%PJ`1n)L?`o6 > %&WaxoK߯=93Xk%DDZPO>g})%P|GroM%)oę5*duiمw;B<=C3+'Sx]PJ@ (V3eۇկozku%Vڞʃ<=QJ (O>Kؿo}PJ@ xG#8s۵Z!ʞ*/خ^t # 'LA߾ןׇ|ܽOS^S__ÿ?G^G|W^3{kgG>_?g?_s_ןs~^@/[?_ğؽ>i o^^SKҧA^T=?C>^3'|g>^Y>l=#>^_4{u{}1߰ח^%/L}=Y/'|m_[_q'}^_}^_O޽ݽ>ez䯯Sw}? ק?>_`L{ûYtg} }7s_tko{}۽o%w v;B}K^Ez^}w|Kg{wkǼ__W|^_n׫~_5_C^_ïWkG^{fz _o7~~_oz{ ?uu߸{{{=-z~^7?o='[6}SmOy^~O}wi~'^O=߱{=cg~=z/{u/~m{=^uw\{\{=?{>c~_޽^{ ezѯ=On׽%ؗw^kz/n3n'__y)_]{=5{7{O{7iᷞ73?=^oڽY_{vrۛwg-o^w?ϟ|s/w=/wv>}^u Eov_ܽ߻^/;}翽]^bwx{^yӟ|c>'ݲOd+UgXfC}7 _BٯBf +8+ : d{_@ޛ-B{e J6/鹗1[9Wd`|/SnH3- hה5ek֔5ekʮFS120ha>pVN0d%4,}Ž[jxt%,YÛ Ac,%^ls#cndq/cV8]P[*>/Y172h1x,c9e{4 )Ox볿GYeYeYdm=ycj;0qy_:)"^r1ZaiD#>&ai{1[ 'q>No.+y-֡Mk sK--%` U d=JL ղVSn$*$w{WK6_fMqĸ~b\?160x>zS |b\~8>(__3Xe[k K>,]ȰY}F48$@ xWt@e֥z04YxD0ϸ֕qk_s(K%ٮgqP Kr> e}1)KAi Kދ2FK ^̕J>9˿Mlw_ylMٚ5e[ =ewKIy``@Y dX!a@2_[%f7W:cWn dތيqˇ5 .klYw6e?yo{USxc#>;iqqqw?y!oZ ]~~&1︼ ,]=hc%c d K%{lɤ3={ARȰli RIF^g.ܕkd2Δ>L2fDZ)d)p]ؔbqք q b%`ç}{S//z?۾<է???}{%w>~MS}t_gϲ> _G?z^pV}yݿ[/<{Q/:h֔)[O^z8W똧Oܾ)?v5j}^04x,c2ZQE -`qY:`2,,5Fvviky|lEc8}#dЄe+ `Їe.} G K43oL'02kP c AޥԸSbCK}!?WMٟsޛUi՝l@B ,H¾,C  ;4}g؃qa :ゎgq[A?+n{Ͻ~'ooyMzso{n?zw}Wv/9a#]ܮPݽ/߇e[ۗwޗMe}|_WfSeSeK-*W@{Mv_TvLJ8mSD#aگmv-Ru;֗׽FrTmө1ق3C$ %a5@1|4`p1tk-b8֟t0~UbX@OOUc 9y?ؾ4>fkpFt**GR^8J9 _Ar-2ӯ3{myi +˙7.Z–7Xp(gl+gGmՅRn{m-7yM^˿Y!lV `*^(l/w<:bײ} uEРυi a3rص6Uo*L{yTϩ~֭nuur_~x7K/떭曼&/lɫo˿j-lo=ZײuZ*]S/Ux+7~2@X `RjVez]]}wރ ._?鏝ZԩUq42}/Gn-Yk2\NGye~҇lʛe|x8dl{쁋W&0e-(IE#`gA01#fN2@(_eᛜS%*[ֲq_vwʾv:[:NOyxSW}N}d*e_yB}UCbSe_BoB܃/1!vaT٥_]?[~|g 2@@@eov΅q{U-T{' h2@@0@QE dzT"1_+dHJU0`mǤ=-]844gJb8`I~*z dF_2@ ZDeW?TcP5Nlݩ#uίǢr0~s1~E d Tف]d 2@zGeݧJsH~ 2@V T=(K d UjdvJIad 2L3* d 0zUv3 2@(i%d 2`*2@8 PefTt2@a*4˴d 2@F_2@ jVVBgD2@ PefTt2@a\y*s'd 2F_2@ JTA-Nǰ Adz#mB?ǖ,Nd 2@Z.2@ = Ԭ˽RoXXLTDwQ-F~_ziVz 2@ -Uv42@0PàSsmYDQM6LئxbkptXZ d Tف]d 2@z_ek/֯=~mkdQM6tB|@lf[ӗdA deF_2@ T"@qj,6#k}#?>-٤# d 2 PefTt2@a]U 7DQ6OYJk8U=6 h2@hM{Q 2@@UC4Q*I&$JNm! 56 d Vc*;0 2@@$ :_|C00ҭY=2Krpx H0 d 0@3d 2@ ԣK/A8!0`Pխ%~G)ڋ3Hxx2@ d*;0 2@@KՌ1302@ dF_2@ [p!2@  PefTt2@a*;z]RJSg_6< [[b#z^U2@ T=(K d @+lJJZ"p2Sg_BK#T4b̥<2&!d 3* d 0T6& 9&_-󥢝$[)2a-{ d Uv42@0@">'~/-|&O0=ʄb+ d̨"d 2U?{GzzcWǞ5UQ?UF@yυ]2@j`*g d Me[Z6bK5L&Up~`8uUj%V0 O,;̙L08-LM9jMrVk14i~2t2@Tف]d 2@zYep =Ipt´Gl}A꜉D `}i@0I/L7B?֗( ϯcI+ q&8{ $ 2 PeL/ d l(J=U[%-p1X_$Fΐml 3Cli[5N.` vkgڱ3 #d ,Tف]d 2@zGe۫V"֭"73?*5U?AJמX~S9y,9$VҤllݚQZCg7g zd&&܄ }.2@ = 3)JJ!?C2e՟ 1#kDَpOhxa20FʿW]u.2nܸK_RN> &Xg?ٶ;3'|z-M7K/Ptx;c3#V;8|U:mڴ믿^k3f fm/P4U?5>9&Ǔ?gG[2:ǖQ& o!?d 4VΜ9s=+?5ל8qe2 [^x!'3'?I⋡jӌ9}? H S;sCЖNXoˏg} $@" -ܢ%-]p}'_ Pe|?qzΨqJgn16Z[m2W=; PegCpK3a,dU60j`-5ې1|W^2e VKſ]z*>Cjc6m'Z#h[+9 1pJ&M`hc[UI֖Xx$O,H[ĖV1[H5S4;_!IH4 ~~HdO:ցd@Uz M2@&]M’Q~Yfjzw駟tIԜ/ '0vXrx/SZ& .jcj SN9EƆ!ΣFBG[m=#M0-Z4~xnfؗ[be ={ '|r^{#XaW( 1l)֦; Ahu>9I2SO=e=sY~O0FZk͛7Ob}i߮ /W X7ߌ 6gs]~.gy& XI@*[f!`NZ3$ J!`obE{GRGq*bap F a``iu ;~TmSUdp 'PZ_ $ 'pJU2@Ȁf*g* 5'ѣݸPڲQGuԠAN;4(G 6ʶb1 Mv;?vmvEA.coD+t1Ěmfd>!ю9RlvZ*^l,$o~5 Ћm* aHЕnr7[/C=6!@cŋKw矇_ -Vpq69:**7JC#9b1fT}$ m5c'xBE#*'.a{ߎCؚm` $i.|O0 |d,$>)A&>v !3_w 1@ (m/n->Kc3P,]_}۟lN5A_hm mRz гNSNܹsG?j=Xf}8ӧOOLm ; N|.BHŦ@=*^y]ۺQ_XTX=*}v_{bيuΘ |wݗmNlR~cH Nh@;xh2@|[v8 JH g̘[9cWz2VMeCxbI]C~ _@c[!k萜 =,|gl̏|#V<1 tTVaYێok#օtZc0id}RUh7Ar?M};valp0p 0xa`p/ˉVu*[f쾡/ٶU{km?Y9rlGb[G=L{׶=e!um۾'xԃ-W-K`ub_F̂'d ֜=B-bY |ZCp %w^U6t" k~l$l@HFlH@0D+("e3BF?3d+h8 ^:O b-{w ɓ[bl|.T@ml씶1Y1.R6f~@ &~0@v}Nkp["kea :C*18 Q X_ [̯;Dw a$&{/IgaJNdp[Hgo8;ᬒ20pn#Uoq,xj= ֛eۮS,Y V Nm9眃NqR# Q1ebHHE+cH+o3Qg#3f!+,}Fq8F (&)mff0Ǖc-Y8*jN8`ގdN>NO?sI|}Ũ`+>%@ V]FFӾ]{h-z<,NHǷ du383Ugf`Vۤ=1IQ9 ߣ@(`=~xlՀ2<婹/g742*H&~18!pHhI !04! d $n#42tđB0T-vYc9},bH%/!# yԜ^CXG8~55CB׻+^K+"1VvI^, Yu ȃg~t ! \îr f_$Pe 03s!BXtmwaOЊ. (m@ek_HPSVPQ8;!9 'ǁMxt<@)i%|:l4'~../ #~d6䫆һ`B2@ d ; 6Ge7k8Z圠*2@? 2@T9Utb#g#XŻVb][/p1К.\U2d~-{M;Q9ʀXY?ξlx`&3P6;|c_g*;8Nv {lLcWr-{ 5]Ω޵l;_pW]pV[m5bli x:n͐y6s$$f,[ O]]mv[ IeN-ӁJ<_ᰞIWx+zNc(u~CO?>~!>TٜF2@eDFvr9x$ eWUe?rws=}gx(?#l~e<w xX8Ud  C +D) ԣNMr;?[~d 6"g=Isgi5;}vV; 4lG d @Pex]U[9,XlYˆBʖ!X{QByCֵ6h#,Ǐ}c6C,% ԯp5o.ʝuן-?C2ch?3񴚝>tk CUvd B ԣZl,4,Sٗ^z)4^Qwyx ?uYXcǺ9ێ~'6$9;0<ǦqN6?Ak;?[~|L=~ ?3'>tk+FUvpNE' dfjVxy:Ѷdt w5 u{N }־Q/ó rSO=8bq|};qF#@CULM!G/~14NTmk[#v~9Jroj N&`U&/88N]M$XGgȷ%&8u~m[`6`$y Z ;< 5l£|@'XXk0O, d`0@SI( -J8Ο?_RwC/#Z6|Yf+xo8n x{p 3\ƀ}[?!aro1@m*;s9F*112TtZO(y&x7}Y$ @>8#|4&8# sbmUgȴylU=ydT~BOHk!E<6TP/;Le~ۇCTٱid y-{qcuYn^o[n̙3z뭱~-~1.la#h= ZTΜ̉=& 0p1X_$FΐoWK:Ն`{jOQvDUݏ=c c!< ιPՙ]C* N&F*0d`0@9" 2@PecwLC cO8 p<?E= _.l`;;v,f3fXrʍ;!x`n}AS4(O>A/'Os(6>w\hp d*ۙu;{]*'J76 ,16e+9cvNN:Q buNؙ0'JWc~L4T?CcnXڈJd3@SI d2@ֳ#{|<0U˖!}id,[Qsrjoã[85އ'52aN 8Vu `r^mǐ~lGEG!َk#Hd`0@i$ d I3ۘ3:X5LX6$Sbv/axt̉O۶9J$G8-L1# fQb`B覄SÊڱ6569{II7Tى d ^I?v&s&3Xo=Q꟝`t?TSHrSUVrvZa wlTJGAz̘1_ɃVڱ/m=*Ϥ9 >s1'7ғ9q}ش?^se˖ᑿwuW yDŋ>1ԢJ(>J2)aE9[6γc]oB "6.Dsi( C /HK&4/?42!ly첦X8d.ڜm٢EjyDV[ڨJ.r"U[ƻ)6mV{-Jrb5g8fK A*rc-^hk*;ԯlsښ9B`Ou{>D2@=TٱiUQ?6CaTFZlj2O'e//gz)V?Ov3<wg?sfA6``tʾKUvX8T٢\a:T 5 CB2Sg_ȨZʨi8I@c*;6*k؞*X>so Hl, ZDW|ܸqP@B.fir)r.xݵ^+~`|=^x7&?~pjS*mm7-t_ۨc ףK-}tQ?[~d ןAsUn6vm#`N2@ش lᄏ<r饗BbLޣ:?c[EeC? A'fJհ*o)^ע~-e8ϖ!}壯g\Mgn]h%o4bI@Nέ5Ο?:|; /hܭ Qv}Hk nF?vs5[c ;묳cӟ4`|~:!KK./Y%?2eUV ?\ˎ}Tٵ@D#D՟-?C>$ٚghYc).I|q2 M`*;6A/ !+;NPη=ctIСXx}"C}q_6x_6uWfiU7^ *ۙH*@bh&׶F<6sVA L$N)M0_l plqH"Pv¥:GN$AUV9$6Vmk$#MZ<3sr!dʎM'd !v{[tm*۟'O0p SQ|#֑*,p bAj&?VHe0+mG:M :Jl?Ӥ38`  ~4&8# sbmUg(jEc'dNέH X/ײctsT)L}S0AC`8sb5jx$ $z 1;֗0Ѥ3`Dr͏=@2h?mS걃GB9.xS |8@T !d`52@VOC X}1/ww_cwm*^<9qs)?EGICg~kZeFƲϣڶ|O۱lڟcc~a sUĄ^$dq PeǦU2@Vec#mmE C;8Q vqXfg`id,[QΩBy,8t&α~|,>|;Mslc C4̱3aN_yC@3@V x4'  bdpb2.7.xA~!A_{|<0U˖!}id,[QΩmǪNuSNkm9ҏIJiFȂũc}xb0X#D9UIU2@ZtV862@]*ş(^ϒ,|[[2&fu~-9jرl+H X_i{;د<ңN;;}^6ȐU<0|hL΄c)I|C04h}cs+,0U>ML,\>pHw/o2VgTCB:O2.gΚٳj bLGMeǚl_Ӽ]S[9敎oZnUmmio/ZUhd?vnm՘-uV`mSPSēHk#ⷊ_!P I讫N*GU~ǖX1d c+;e]˘ t[eaM5ϟSCBN7.皮84kMQi^rή(2X[k2~pjW|d.XlٱXGNf[ml' >2(V[' :j=##F"J0b$& F 4c|XS)]Czrl҅Fw49Uɐd` UvlHT djTQ < 9_1/^>+A? 8 j4IMց|)UvlnUʞb^>y hjB6kZFԋ1jXnB6m!Q/>}We//ײcFLuҬF[cg~UvlZUʮay1>k}Ge׶ϼ/OKDkUKڞko/Zv_̨c~0)2@h&TٱiUvZ/J ^ǒtBoe'ttl/*ZؕOi "d 2ʎͭk(lvq#Z1_)^e.T`S d 2Lc*^***ە>ic/Uvr@i "d 2ʎMk(ll솪gyhA~c~0)2@h&TٱiUvZ/JMMP;_+^*E d0@VQeצQTTT U/E>,v%SeO 2@@3ʎͭzVll솪ھ^Vnp-;v9n洄}2@ PeǦUTٵtU6U6UvCU+c[[т(Pe.T`S d 2L~W?cǎ1bvXt̲zVll솪lܔ5_!*E d00Ug1iҤ}s+V87tW_}Nk(ll솪7柋Dk!]QeO 2@@3*{_|Y-[lذawuRermTTT U:Tn洄}2@ dϷ=2l-ZTLeOϬs46#0iftUʤf,> f6xj6ŏed.LoF6kO+!o9 3yχDz*?ލtF=lO~ dؘCF]o/;j Q36C͛Df03|lMsgV9tU+c.LaF5ld'yv!Rqk~.Θ;xRe*1wﴲP3݌lܳP%g5dz{|p~7/MeJZ>kˡ0|3f6ug_[.F5ѽj="!o1f1ӍmHc PAƌ"w:Kh#D]npcVi  9l>۫skV6*Dڨ4J?Vkֳ͖덝moQR-u-hA~WL\PewAx d 2@r*{'O˗vaxƸLIllDpqי{W93dު=#Q6a8fzesսrU66޸XTs6:7W=u.CQ*czE zk{7uVS(^Q݋=gsw*r eFqP:ܾˎCl77^qܝmX¨q6NW$c3KÏmG(F@ + EdcpST0c-ö ⍗{UUw]f 1WUSY@i7za1^g}Ԍg.^eg̠!]墦cF*2/Uw:Uv1R*D7Cudm.cr8!eUSV6q !-^WZ0%hMpF^n5AQZ69 Lp ./{̘15/ۮeo^ݔNZVU+ײthZ=s-o`GijZѪkeK!+lK5eTSv-Ge߽^\.-;/}ۍRF3|UIZ;EDaeWK%(VM@heHU!WtO?k?zWD۵476?rz1 ]E۵ ZTlČkfh>px^LST6n'U#U~N5ttIZ>:x__G#uk2]i\5W{KTu-h/wܗFlS}ouߗ}x86]*k HgOQeur쯫mO2=!)R5{~q+4eA]刊?r{_ߨܗyiF*2f_Oe,IeC5˾S{du6dc0*Z 9,MS6/^y_~nߵ*Q]Ar_MoU>nƻ}/w.u-mm^# eϙ3O;+[ozGkL>nߛO>o?W;O+d%S0oԬ;z$v7rV mtHH۾9-UJ$>0 b@>FL͙ ۩"amc-*ίGbQ;k9 )c2Ku31W3f6"7gbOfV`녣&7|CΔ̋73c_O5 gV^'}y?*CJSًw13T1ׅfl3{\EA*晣cZW~ʶS+ 9s^V[T1v߾ Gc i|@u=x vhwuK8yTx8jq 1DA =?fflRy_`ofϨìwSf~VZ+b}5p /\0xO/O2tI&-Xj=3P]9+Vw`QnTJݑ$b1VMKBm$9uzlrRO6at*k<; cNxZOl0pr%l񔪲p_Ǐwv~[^ ;lA0l2.'OWkSA9QiLWߋ鈜 ::0<̀ck-O|%RhHcc~mWUY}\&s5z*;6U߉5XSbѹAMc1ϲ ] uQ"4VQ$s `_S7S}Z4rF*?.\g-{]v9s2yA}S`…SNߥ?0BG}TEIS v&c1/-ipIp &3[h82lr}3` TN+ *mn=Q'[<x N@LctIv0ߣ3$l=X?6$}jCHb$OwC2T5l~{d,`X$ h|Gk;r<:\۱Ti8cݔcKTc04*dݳ<*6UvWiJT:G-/|֬Yף~'Ν;N=ٳgRl#G?ylpiByIUeWVtD Mc'*82LI,R{bkKrЩ&`: ⵭=N/,`d<O!C}C[#08Hxd֘ C6!Qц[lKhCZ}CҶFj_<:D1#8~[`*8%C Ff iO0Ax 3b6ZG㑪5|UvlGM]vCɹ]vi :gT5̟疧+Kecq:5nRƞpUV܅}衇kӦMcIֲ9cl{] 1#]4$49hѪhV58xLq0gx*~zr0[ٗ3B dm2;`r)UmTH`8 V- Cc|?Z3w`RM0q :%- =NK˦}9NjhGI44F{N5F)0bخm-]!Ye ʎM󨲩k-/# DGT6q'=rȣ: -͎8 &S,@bh#טv,w~/1@o8y#4La'Q L<`[gxߏ֪N t~dyG %1 82y,,:ѻ[;xg>jd!G ^ZŠʎ񨲩Ƕ-l"T}_ގ^eھ/Ɔsr8 cUT}Ȝip'6`;K`1T'aS#zl_QI<0SȠӏis`Zi=1~ίaڟiǒ~ߓE&vZchؒ?3)Ulll>;ǘu D'5j[=oml[ύH l_CnnJ1El㞺c؜FYh/4eClݧM dڿpܻrʎ]D.G`rN<9}C PeǦU9*jzc8݌\CPv9|{nЮ\qK==듏΢8lId*ԥ{t' z>}o;w4%i(mnzcG?m˭mol}ӥ5mTW0,5>VQ771j;mrګyc>iި`_kϊϛyl9,go}'iQ|]/UWَ>Pqq)͍191=g洇-J9m8*>|vW1fxd:&D4*Fw׎C)ՄQ>tVt~ruʎ]N/dkm‘? f G`rYV!d ccӪUޓ EM?lުJY˾7sם澗̊ij_U[@Pe곛~77LUͳW±]Z|[Mژ+תZ1e{Nei[+uǙGn/ ӢضTO_4htN6 w; gtnMbirT:^>n3!CO"Qƽ*)\:إoQڨma3]*[5(g XK6Um?Pa LJ1 ש/)UT1b}bH$<*V=6 d PeǦV5Zs[u _J'<8m.}jT+~vfeqk4fU c?!^Y}|sܒ#c*Fr2F߄#ek]rG>])!Q}7>v6F*`uxypõ`5쎚&*XUbcPH~ M_j[uUԁ&]v:v!jа%M~x`)#}CִG&d 4شfݱ}'lnyy[$u)SUN^MLU3ά92lJYwTU~\nʾ>\ 1.7aUv?EDߤovT}7mųg?Qi#!Wc.ۮuYKVهu^tVPe.PߑԶ6q&uD&Pc}ਣҶD2Wڪ`ab~$~5vcXOK#LK' d PeǦU9* ;皗^ ̫8l/::mK*]|}ب!RN~rti*/YRyJ{}S-wuWU#%g\*Q%ߗ`|_ j̍L4f1k1/1*{]Ab#OmG{DskGش:Ig5S֪x;7v={Yc~t f Yrs~sȬxYvFoN"%#MqK瑕+ײcLʢ;U;ڠ3q"1Q;!F0C,0j uX_eB}Yp,ON d9 PeǦU9*;ziE܈Vѿ1D6XE/Z7ήl/VٍhpC=9\H&%M K1 UvrUvB6Uuۄc?UQ NЙ13;U1@Q\Ol~ƣcu ~KN? d9 PeǦUTٍMU6Uv  Z(YekNj+Z:\pc#1x>ZŵjEv`SqlItNUň%!t,Z58aWc#` 17}i0m2@@sʎMkT\ZVv 2M0JVsM)K*;v9KewHQ1*dbqЩ¦ B``[39YyN K~irL:UwPխ֮a !Lc2bڗN硇 2ʎMc57A\K%M׼¥$>,v)4KK-'ЩlAgL<'U0ä1&p D`N4'`p:  b*;6ʦʦʦ-8d}J[kٱA]*Ao-4P_sd+ dDc*lllƉk\BU*;v9.qѲ(M3+>;2@Jg*;6ʦʦʦ-8d}:pKA_ʶ*9T8[dbruAX&t&Ft@333FNfb UC t@7Ԋ****qZ2目KkٱA*۟G.10ǐ$9D45$6puU2hc !I(i QԆ:ֱudU, O'SCb0@n UvlZEMMM-ZqF*k/Z:T.JTٱAm*['өڄim}9d]8fX0I3x.өڴAgm|Q.<>H΄a3<9y5VuFזB`r;`?V>qش****qZ2]¥;wG1|ٳgs=O>nhtMy衇b h"/&rUUyS^ޔy2!!# s@ATF ( 2F@EmEQmlX.c>=VksgݻZE+V|_FZR7Jcbwc޼y4ׯ_%z{RϷt}g0KoJb"M%Gc M%@0 $be$e¶>Q&M$ bx*Z2t hgJX)dUb[fd/cR(SALLځ7qRq6Mc=Dz >n.cV >N,_ZnQ~4ڨtR `'\Svs5[Mxĉ{/M:cƌ+W3|:5VuE_=_E ?a؏@Q)732913SJc?07+s9`79XV;ҳ:4wa%PCICuv6BU(^鹚yVnAu],ravebt)F!4$/q#54.oK)kpĦiGljtĦPߨM>JWEvҧO^ޓ'O76۴rL+u+?w}ODꫯ Q(KBR6 ;R6/씲K)eS=MIlz#G,ݵr!S6WQXg?;e~+8OxիW͟断l+> c[F2C0T%iY|JvsVa"g$ CJ %V>Kx+7i+%< c[&ͧd}tt4XNFb^zY;i%I'ԀL )e}N);씲}Æ)eOP6ӛ?T9Q:r!rlz9R6}fR5khsΘ1c>zw\mF>w}7Z6n=JT71C4:1$h(9}$J`d-yl۳ %$x8 c4>>A|u1]>YIJJ^ ^FzN;&@Jپ;SN);uw3勚s+W!l>Hύ7nEu]0|fkO>UN1>d|0{lqzE{+q^yTq]` >1@;JN'-<XLg0Ѩ.!4 /y'&[|5\e` KCйFc0OP_gLOx:GyQUz)S)ĪCǣ1߁SʮGye,\?>sZiJeg>J~˦?Ear4g5em .=Ms=˦O^zo~ꢴ~~-o;o$eycJ)8>LY5x%À?d1/ I%L OՒ[$&}QوTsc:Ϙ.Y. L@pؖQ,S%I4S$v;RN/Kʵ)e]EO)4%ޔaSXueKۄiKٸij1[7Hg<|O͡tE$ @x9J!J>l`v0c50 JZ L'I!N^K 1Jgŭ.e骝lH;@;&ô[oL)4M);씲뛲/1+rK*ZrRp;cځ~*\/ ϹvZjځv lmUJپ\ RvJ)e7e_fW>J\@R]jIّ7J漠gjNH2!F3]kba,FltժJ3H%C{^HMbhC9]"ACJfkc7&fށ}U)eג})eRv}S|MSޣtyBRrPuʶo 0`=q&ЎR4  TA C B8`dH ^e*0SdNl[āR|90o)Uys]+ o;R*l_RO);씲뛲CrOT)e.եlynB,$c:;}l^n T1g"#} SCcQ̜pVj0ǖ <@ kT7T+rU{v\ {\S$pH)w[Rv-iכRvJ)e7e_iʟ}֧o?] *|-)y#0%cע -=C\gIa gd@ɞ1 %a$QO<)rLdF-VltlG WeDn;5죎:jĉ4h^{qFyR/)§RvJMW5?ۜ(]UХײ%@bSCsޏA@~.Uk*?TO;2ΩUWF8>$pf C/p\US$s- VM+J8v14J`0SRV%a{}  lqbؚS$R/)§RvJMיMu_R6}~LKm̌}ƌ5h|w\M%UN>CX*x &Wzf[>FPgd\]qN$W+1ĒFUJY ^RUPb()en;R6Nz;J֙Q3̀.1>œ\\= lt1sq8me>\g4]{YxnSҟ9!cMSYx"׭633]fX9bswfvbufَnTL9sBsa=Xpܲ=v2So| n.>޶nƶ3wv tүzmATo#׽}]- >-7=m?aǰׄ}dUc޺Cgi>s;"_3s}NjIy:3pi2-̰#̜M6}i2ۭϰڵL&|;̜`a=f]}Ȝy3tigdrQYX7363l9btB37tfQtR:SU)>A]A>nIͷ^tT@l K2RJ4}fKƧg)Y8rU K$@#K9s[)}E3w?3cy4\ϕ}^y8_ry fyf4}s3˔n ڙx%HŻ7ܲ xΧ9t#/ _sN= }|$J.F5~ΐ_XL}ɓ2ֽ]wAZ8_ϞE Y.;hKW'9ggVfDm/1;>[y ,w~49p^K֒ }ɱTv3gND_Xn<3 }3 |0e_<_<7,כ/X`1}gex^qAy*ړ| ݒp)+p?0 ygՅj/OcS 9#ad P hl@o.^:Wǣ]1OC_(110n>% OO|7˧QtTC[;HtJP:K8S ULL H)"vOOϕW^ {Sn1>P9y$#&!ޫw"6>7o۲^˖32m:PeCJGI3G0d_C@ ed|hI[{3d7z%#NٸwQ)[f ]>/e&#f>tbK{+枳e;Q dS2M5+P;7߬ů&;y9di_9{^0dLxC(psX=yhaPv~ y=+W.?e7s; S6ߏپ@ɮrZb4ltVcH! Qr8In bg  #[g J>g H_;q*Ah)| l}*)/qWrh˨aV15*O_1f,)G:~ョZzFUrfa79tӽX%lz!^ήC>}Y<7J"3vg2wkF 6}f6Qzs͚}C Z6pgtcBW{ҷH 4D[m>k Qr8In b:OFu9 a;I>0[L",S^Ā5jv}*=X.LjJEq 䤄c42QÀH:j̡rVzLx%'1[l)Nuu9#6V>gzIfb̤6L 6<Ә@:e?w5|>zONIJZ;vyf$ /ۗ'?>ۿ^CDL@Yn3+Yysq+ӮGn#ؓ?O;iu6#gA[E`pwu~L 4-@Umo׫tl&m| {g~[zy&;_ڦwwgɯgRyEkKST^O*+o (}nfc }@+cf OqK>ARv^ :enpk%Sr%`@` h tV>^0Y xR Slsdx$9I3}-ft6uܘړ Ũd+ ms.^Nmesa[Yb@fS$1] Y[l)W>:;;-VMc^^um_^xohk0a@k91=Alugfa2qd l=fo'؍6Y@k耳[zQ]7Fg)b 4O9s̿yp>ڤ!EQ>o6+`8f0 [i.̸5_sʦ=IG&A)S* uf`͇mf-gT"K`k'_{2Oe7zrğFh磥y@gˤ ;+f-P*ޔ}'A8Su-k)bIw.)eRy[*{ړV>=%G)-gV"e}4u×w7JR-T"oT͎Պ)8ef_}n\Sʶ/5e8I25Rt$IJ]KM ^KAkVX3hp( mMbH)w[Wm w?ߩ|~1*낧WMSIgUJP쩋*ߕ/bǼ=y{c^_\wpfr*= a.ag^?RzVM^8>W2{Nc7IkoR ^kˏ {!ǔ7 a*a }?d7𻉧9`շNQ0# P^ӟvˆ`.U77Z'h% m`]m>>Cz0eoX^^OK'_?ۜ8Ǵv֨MÂS6}(GgKA.eow!H;v;`GZ_JځJi[T>G{O_>N3|J%k>[}&'QUPP ܥL -2!^雫|̚c4<,yX!79Կ@{0L)enR.0\ê~i\Lm;SLtU֖Tfsޣ%(l堖dәZ4v5bv԰Rp+1;c43ul(3(j?9$%0Og"c47}nU[c3Y0!^雫|̚c4<,yXI=z8gJپ۪ ,\?Nf\o&3.jj_ۭsy{ \WL[W;} _l9ƆÁF :P311}>Yb91>0-fg8ʵ08)$$%V p%tU.!s5f,ϣ˻z~uۜqәI:W')enR.0\ê~i\Lm;SLtUJپ)SV!S~"A]/-SR󺬮 `h34 |' >FhGl06afL|ɳX:I٥ҫ!>X/I0TeLg(CT LnA ՐHfٞZV%J3WI#JC FUxO ^5KP:05dRR,vd|\P-S>ہ$Ӈ[!d-o$N)Ѹ@\oNf2Cq႘h\&lý)rG*}{[Tʶ/-,C3c1E. }1_>#{32C`vCeS+=;OxV 29zgjۿޙ/韩atF7\r"/5U}U)eaU4sw띩mCta :*lӔ{S6}roo?{q)~UE?FzyFg5M%@ ,v0?%m\PWd4NOPA2Щd;}Т|HF62R}ʘ^h m S$ռrXϓy9sIHճCLOx5tgjX@JvqJو_oz3v 7 D5)eޔDSy 9&؊kl\@m>k Qr8In b:OFu9 a;I>0]x8"(12Υ} lʌ%v0)+O߰9I߃%zWslfգ(IE@jJEq *ap}|'$rᔲ}w|˺G ia|(ǵ|4xKi#76򸿼Og4x4kO)ۦ㦼GەFk=ws\q5@L f^aJQ-Ρ@|әɀ?b4ҍb;ILBWF+El1Uw9Lҙ=7P2AE$ا}J{:y:tHe涭¼T:@\L7j0^5Rq>.t.@j3J14v'aR/zGlk62b\ 4]"6MTp)?(}'cw)եl\}ξ*|hHdh RU;chb`l ! d H 8((Z|.L>Tp @-RUF x0 @GUcJ@C6Q[ U$d;0gd HȹlζL*cYu-N!F*FvFjȼ"yha۰)&NZL@CJ)enRN)Rv1hU *J@C0aUhW5H*)1XJ(J(ĀxC((!؍J@Cq6*R &g5 ee&fgRʖRRvJپW aÔS4ќ(rH)۷ե qRH;@nݷ|uJ=&Aځw lmUJ)eRv:^;hʿ}~1T HVip-q]M ہ}V)eRvJ/e?cʿ}\skپAJ{d g5iԲ1Բ-[Dud޿v lmUJ)eRvK?6_>J?\R]Rv4쮒'r@7.NV󖽇OOMI01A],ɼ@Jپ۪SN); Ӕ(=[pi+M% WcV!>f 4~13h'3 hVRVRvJ)esS]%+l l-Mc*9K#}Z?>j̪!~k1fUEiژc4E@i4~ WR*씲SN)5>Jw))esW٘Y1Dt=-/d=,dr 5#n4~-懵>}V)eRvJ/ey/+8}G=cΜ9^{-?Owyn-&M+yw̟?Æ ;\v-Tm.%e}< ܘI a% Y+K^r)rץx9*I%( e0 wv*cI. 4 ;vnRi %&PVAGRK#ŪJCY.ʳ]j+Yxzv`IJyyU]6[% &r=Fb vV@JپSN);~_ǟEoևz}?|С)k8p)n ߘ8qի PnN>d '@C}_'e{K\uV7fʛ 60{F&c.`ېK1<9UlTU0RF4 |*a+3֠dh()(IIa &Ah'`W)\H)SKil7bTo^M\> dO&=c4RNd"ixCֶ)enRN);씲_K~?L/FϚ5'?˧QMڌ+p 9a=}g8SVH0M|zW m*^ mƄH)wRvJ)eRK/G'S6?1{7c5BJe]+6Ho6 NfϞ}c؏@)[Qrc&M|@T L4HelOscjCi6J`X!RL!SC4WC`8\.$iwZ٤ȹW-5x9Oü.5X!둽f n0͙l~[Vd60wi;l&GyKgy&>=fM!e_s5pGM]ễBn̤eHSUz%^"eoY7Vt8o9!ZtsؤlQn0,W[XOgiB өI7UI GVVR/6*`+M|RIS5ƽFz'V pۤsjea.1؂w lߝ^J)eRvKGWf!eSROUsƍWXA NxÆ *;m'x_)wsKٴgS@Trc&M|8rRT ee  (ZVp$~k؁}v1){sm0cҝ>uŲt̸5g9]d\U8> `{L{ԜTEO͌}͠ɜxGsm&lkzLwY:<=`;`[J5uXe=^|D9uM3}ҩ{?!O~2]yYOXs& i2s˞ڍw﷟9q:yͳ&Ǟ3|섶杗vӏ;wmZqׁ7S9(Mk )秬^l?k{b\… B>CYg?ǍGz@~0EiH;v;R/&e#;/}=lТ}tDqOA|_sRtFO̹'E;4?Vj J -nvzд "A:!o?Es:v':Z`F|sM떲_ߊ7l)=dsbH_V!Ԏ?bdof:)׽>w鋆'P9{`ј4zz΢NީGqm'_4(6e?r10˝tI)w9ؒRv/ڲӘYTzti;R*2eO!5;gNQ7i5'#MoF0xCd8?v1n4RRWT^&S 􃟭j^k cqiRyNbsH9Eֈ~qz3'M~aV^z{.im:?ߕwG[avL2Mm7FODԼ'}z=r=y1 2얗/[;v=_zu% ֦]-xtLo4eߵmuS]1>Ŧ_T{{.)e.l!pGyQgv`L/k\wd$ɫΎN|`oIqV-gkqԾg3!ƬYkhK6}1w e|:&m^Azw>ǐȏw7y_iY?On1۵#-|Kc|Mٿ*l*{4)e.ly Z\Űj W456l r#}K)|[?}su\>>2>jusgP;c?ū!=)Cx%PzT)ye̓a W熉RVN/Eibg?5ؕo\/zrt嘔}K_01 Z6ӯTb|sE)JʞS^@$r&=0Uuʋߠeˈ!|֦΁Rb0eҖ~o$S~}KǼSqEn)3e_=^wțoyq9.fj{>#bǼ0 t̚o8z9{V4Ip)AY.(i}p YbV4؁70cf# pXm%s'$mi5Ծ?;U^ak(%@dIb̫H5dYiL eC PRJƈ2`%ζ8SഒdJپ۪=)8/tÖ"<;Wyr8}/WbE̶̼sz#6}CqƜ׏˷#%q%s~fװ!}9n f٢Mfa0ffUHJ./zŗ/?N)6}ŧٓHxᎅ~{] #a/M%3GBC"3ٗ*C>^mMspE?Ii~pFc׼|-i޾2g;=`#` ‰a_^AM6f9=c%}َKg/>=#ӊD7}婄i> :0{+W>Mn>q}LMg}\lEi y_u!lM\Z^ko쵬<T.:lcuS8oHg[Ѱ/|v6s`:_ٲr$R,If@!d%)c#hP"ТLJ&@- SVŤaLmԐGK31>зH-.qMAM= |X:W 6Q$ |1Up:˩R[Ґ#="ѡaKքyN l} J);l#r!\b5 Ŧ<>j^W|rj^7[ *\ILNm[}jѨ^5$)\'oWb}ū(95tg4>lیռ<,cyH3il͍ <ţ%bnnlBgIfꦎ+S).)enRN);2V+Y!9I!9I]3m^lqk@Ŧ&KyK颐RrPT/ t>>hvrUg-i%I6f=>tsjlR`a[X&5h>C)s6)[c8eL|-sY1ضÑdznH>^0ƍ4>n|}Krا<07b(AdH,I`H+@ ˥] $lmUJ)e9  ȅ \W&&An(6e?]gyKE!l堖{ \P@)X,hU(H H&s;)>ITᬀRU idaChrzJFe%58KBb8HRa0p /Qrv k=\IJJʮc6\C2 `j飪\-0a"üzJ)w[RvJّZ ιL ιLꚩmbt[5(6e?U')yJ銐RrP`v^qIҩ [QeP bNggctVN2FVlҞ1baFgjX`7*&Ӈ\C(LU.%Xj" e HYub$@eJ^%al$j\٢H6k %f @%(jz.9ճaJپ۪SV9r+ " ALL..VPlviҏSEW}RvU;^ o$wh/2MU@tNq"ȵ)enRN);2V+Y!9I!9I]3m^lqk@Ŧ'KTx4.)e.guo8%ox.ҷ?2YĉO;v J);l#r!\b5 Ŧoc pTq*A twl>oSj8ͷ*ҹj{"Uzc=i3z$$N^a7TmU/` 92/`0`"#)en8e\z=U ;>bLʟka|)Rbf/ygI$C'δr.OZI25]a] CU0@Jپۿ둾iGljiGla&*6e49yofӅ{:|-)[^kJ}u8V7cjTF:;WkWm٘H4DI*OkUyEy #ԬaFb+L=&R9@ ࠔXʜ._0㔲})e]]BO) RiJ_/͡FJr?%.*e&ׁR5=O5E!jjnw Jnd%KnżRZA btTh|)e[)SN);l+aJɇ)*.XS6s-kz͝=syI>9$^s뜤"0,B ȹg#Z{DԪQ ki3]|%/{<l5򑽾IJ);씲Sn[Nj}C({8ܤ J1/ bdF20$p Y f^hAxyCc.ٍH axjǓ &6H)w[RvJ)eRWK>^=Ք}W7=eiJ&K+YP*,{U  ,ᬀRU rH;KWb)1C&{%@Y8ضr X5!jC{0#{V*@!P< Q@dIb%C$ BV,5j^4@)12PeЀARv5 zN@J'd.)"#)eRN);씲]~cEyK钑^]r9˺}_{U- 0tA=5uʔ>wNDj(myJX{ԒdUCw.4:͙tjlfȀSy+% z'di ^1u=nlecA.5`,9$L$ `te f(ـ6)&y1 l>I)w[RvJ)eR_/)e.Cʶ/Kg*kpd|HN)d ȼ <95 &7YU)VւN\Zp$ %VΡO_O]r6Q$ |1UY9)2~>0J>zR$M>@l(ܫjŤ쎎uӣ\ÔSN);~XZpyϼKKҐRQ`\3K@]iɬu`R:)>ض6c[բQjsIRb/5NhdاQ SuIHI!J lO4ؽNƧcjnTg{j?+ռnX(I}xlKo|<9<@g _u2K$Pz-5evm2)eRvJ.e[ZǽetH)wi(*ew5_ xʊyrUgLT$mۘ4ͩIقInaԠ1 W|z˩InqPc7/>J g &>%0-X!Sk|&Wu&Zbq$up q;g͚dknn>ꨣ|L1|J)eRvK}WWZR65%[c3,<d1JRRNʰb(`R2U`8+TC)@uE=A)\@^iebՋlU;PQ d^ x9p}na lT%ʪs5v՞ŞQ2 P1ȹbӇM%Iʹ$v+ K=`OU|VJ@ ',KJ!1` H7n;HgESN);~,x2qgiO^X`v^qp$T-2@Ui1C1sx+?̉X#+D|\6iekc m1JزLF3[25Fe#eChT`R%Jc%Rj" eHYub$@eJ^%al5Qi*I(p(a+5/ ^ŠE F0DĀ- e-씲SN)ߥK{W~KtH)w5.e\#5*ad{eϛfL;P۪wOJ Tml;;nq2)~ɒ%󫮺J)eRvJ.eRZy[K+rRHy$܁KܖHR봙d[l){t;4gΜ?ywuה둩mO3ࣺ\KWK,\?~cyF4h@G+n-a } Yilss+&Ǵ4dD b4ha1iYm&TJ;v [@ʮ>=zh4wJv"MՒmpĦꗦ}΍4W#6MPtyϧTexpFjȍup:qJQ8 aRiWmzk|O;v [yc=N;4=e/X=\{ʫ8 &2vjٙG?fuk-4f1_cc[=45| j|ڀ]wçFKݫi2㟨tѹ}ij:Ld3H\Iyջc\:ٵ9% ݴrnCWn>V6p(w|wn3xE7뚟-[L|}h'PˑLpbr1N.|F6w ˏ׫U~n_kcMOqcdحؔ}ci՝=n,OOX]FTy퉹$y=c٢HWC&jAIC@0c ^znݺugj);^9n/x7fΖU }ö]Gdʟ;uk-g13ȘMhtw]# NK7Sx庵~fgjnk9WXEFl;L'}V=Of{=êr=F_aS's76-_A"{mw4wp-T畽?{3^|ˋ.'I;ymFNٲ67`$}82lX9vugt݋ /G)kY1n\1+l @}:3ry O?ߐMחv[yutRbQKʶo|V6Sԣ|z s$)4(&CmQ&&NqzĈx;8 :9k׮4hЩj;{I-P~z)9 `{:}d7_Lܦ#'t\Hه|dˆCVOe^ ;zHRM0/8)[ꁣ-E:1ɴM0KD1fӦyg1n,7m0ϯ(SvG.}őBve˯v[)Mט}e& E)C}Xa%=y~9hN~Ү>cr1iVn`Ѯrʞ~ّCOB;z99:6}Lc"֛߶a}z]+*b:CɬOY+pkyMރ Y| ݒp)ޜT!kH*'SdZӄ4X50lQAN7]֜i_;ŤYfx㍸馛l^s5cƌ}u(ewuur!-ͥB18 #e?h̪c΃1 ȔMo*"1;){܁so8R3 ;e7ˋZ1>ЌPx-#v`44uV^f&Ҵo8nxU1"e/LZ&Spnw^Rfh3lXӞ{|qu`N\]p7L'ǡwwĒ# 6jbїNaM ہ޵aG)m'_? 0@LΟ8Юr^mC>y޵cnڵOMsxვ}bCb &DZKo..z2'=<& wMQ}uD3J, yY! 9hq蹪h$`+m7WX|*H;v_;;;MaJ 8Ph裏{R^BwKqpFir6I73Io7fɚ~ǎ}soeqLLqBtEϔQ3yJӾaT4{XҫT)2W6w.3;1nX-s}Ei:N٭֧;*돟 ! # "EWAD@EYum]ZbiU.iVQMrdޛy3ɼ'/s>{罟r, Yku;7钦 n37=_3ŷbP̽wl%]Mֲ7!ų>AQpqs34V3YUtDWtCnIap+fD"s%uiYJ[QV(]5=T\'ܔ}4Qy]_'Acǎ-,,3'ٳ''Fՙ3g3'N(vj׮322xg?~qڷogAN(%u8.~q/ruS9<::vZ8c4Pѷ0?ny\ץmjӴ ZGj\Oj3ui~ P @UPv^^ުUDn߿ E7rbyxz^='38?yj.էl+L4K+sBIټW<>/M]sa_Q~/NmW<us1QJ&oW(Pqm-Myx\^zkwȅ er#Wquk^ܚ7~g~U5vv<`ĜyxF^';qRgS7^ l4w7iO_x+τ1_9+_4:A'̫/=c{:Mo?<?򕔐{FM鮪m+.yg{l ? w7+-'ffgjۮ/zL~{WÎiL"&8vN?Z/bU>3/Ԩ 5 :;A[a}RdM 7e#|XoSÛ7o.Ng֭74h rl»:ణ}bFQQܹsEf8R6,D{西sԈk6H`GF9x}cAf%s9y9z_ۨVmJLWQh} 6_&/Z''GeM\[l2JeB]|Dq_IJI ksXUY04ފ g_L#? \ e,;=OGlٵvG-׬㙚ճ?ίoxgI&lRyK95?7{n]4#ũeQvJ6% jEΩLvBW}ퟠ rBd@g}p䣊i%K#AM=bAg!@(.fetM\- S 4͉8Y8tAh` 9D^iBġ2bō:qg)ni}d6p5e:1_t͈~zDS6h&c1k|[7>.^jEpq:lm R#F.w*U 8.(N> Gԏd2\|9kFõq=.kj3V՜`X+~6P @_*@p.̸Oe 6Pv(XA|8UZ(m>gZwPɜiCAFs:Mڵ5xZi)n`C;ya凩&D~yƍ4eaxbq_6HQܗ3}vʜlTeߢ6 Mm+9o饗 S6b )jvQM7OE?j]""r&%L3 ث 0"s~4]ФNe]1$P \l j ͑)l6MkA W=e-_!/k FH/r @(@)6*@١"6}xfÅpDl>V>lrIKnW5*WNeGPU PbPmTA#tP 6:Mi)klʮ-n([e_ >P @RPSO=-̛lP6(hq6m~R{(r]6AU >*P @A*C *)99YeA٠plwt\[i9_mte_ >P @RPv||@:qDbb6beA٠숣YҊli%_@F PvU5P @(.e_{W^y۷/;O˖-7onT2YA٠lPvQLi6CZ#Pѥ}1+ P @("H*@ً/0aBttĉ3g}֨deA٠숣]vmt3_@FPvU5P @(.euˍ A٠lP6(;({tn6]Gl(b(WP DU}}qt1cƌ?СCF%8( eGe_+6K^gx_@FJQv BW"̀?2sJP @WPvaac=5R 6mJ e#'JwL7صIҝ|5e]ekZbSM(2ɋ-sLVlP6(}4y](WPѥR^Kjl?;Q?;l9uhB(DUM^G}999jgT2YA٠lPvQ8)}vGmtij+~TE$,>  @(@PPv֭ бc~qSOeffLVlP6(q=ZZ7Q~ mtiewJ6F @(@UP6֭,[}?r\m)h e#Ok|e]BK%eu*V3 7SP @(+Pe({ٲeG1*eA٠숣fƣ6R]RRmţXuL7S zDP*Pe([}QT8( eGe#ok#M|e])Alu[M2dFh P @(U5km6q+3bb()Qm%J5]rݼiܡgDi/bnYtDUkR^]3\Fc>7J_CCXfӆ(yY`"s=bjO?IԘ(()#eq.' !ORuݥҞ\ԪD(lإxbܱIѪM]M3L2bx C1I6`/_Pˠdђ}Z&r؄x&UjJ'"ڐb68'l0Qt_)guGDe#'iʛ0G6F޿$RϮY?Q)1YKhȯ&&%򄫞-ok$gڈn%V9MSb:J`nQ\M3S7P @(W*Ccǎ始O2o({ȘK9 ~bܦĄ?o@?k╵VOj~,nFH0(N(PZ"OB4 3AA1Mi<%>_Ā}\4Z5uCNQr=hiS~uK'6ju]L z[|"u^ gWTAN} -n LAhB_IS緀CJ[E#.I7[ p@Dj:A/bc?Q,=0\f׆H_SRZK8M+mN=gB=Qnw')?;+=t];,n⁰ZE)$e#61ܱCJMW Wkҭa]SٜlS[|M$(eF~̮ !(\+x @((@lqM锞ʯ=sj}e󘵷 ==jբiS.”HJO)͙7^%ӼKյ&)Jo0P?wB ߈x 5q9Pv,oNjٙԩyZt9NS6oϯ#"/jĔf>Ou"Gu<śՃf3 JjtJ) i?e X )Y:*;|:|Fc;h E<OzQr}B;QQ䎦a䉧ǂLU݂:e/YR:PX=MߚLEQ`Ԡ.~w޴ k8@ěMZM=Of'*WVPZlfʾZzdˮmt9e- U8-j.3hS"fjgP}mEP @( T.,,(f͚F%r/1&>Ae-{7zִAD!y]m*-i ^ZѮYLKte,p/-Ղ 0[еlmB#>K.Py{)qv3Uu=5g274)>5fstqE4V&xӚ{6tqcBĺ[jq3˔eg{Z/tޣhSwWҩgu,:Eţ) 69Zv?v5(Pu(|OI P W Pц@?uS Rwi3vd;^b \.VR.mSͼD v([]uS `sx)eQ3fz(Re\lui[۫RlvI~=EM*eKjAjM􍆻M]X,CJv][4ï&ijټK)ۻM|h`@QʾR_~ʮ(2`-P).E @( Tٳ(֯_/J:V(;mTNwF]^-&i-˺cҽ8,}s >jv:KQz74o4۴gf-J~ڮvʠl(:CP @(T Pv||(_I e;uGK]95eGfdO 3t3_+vNn:'c&k:DbxG(9`S U Ѽ}:.΋#a~M }p]Hc%}Nɍ4.~fq/߬}C?tQ}N̥VI.sϸ+=d kSvgF\7;ſQ^Y^fVPyVř錄/ߗG4?3.ʣE] t:9sʛ8M'CwẠkٽZ/qlLP:(6*P92W ;c U@vw0(.D?28OXc2)P&@8MG) 9ƣz6C9PA@Fe(;48"3w~rltR9mƍj3~C9PA@F(msB;q'eqJ:VmeŜlْĘׯ_uVsiР$l~С樽EEEs䀲:0P @(ۨe JğCNNeC㽠lӔ6^FGʿk#|3l>㉊S>i$~s$;wd޻w/oggݢ2ehFʮ#P @eUzlP 5S;[q6mtSV XkuVFeʗsʞ?>d}v>m_`ֲ}QkقE0"PUP @(Pexj*=Cl Qn[&[Gg2se ŝ6X}8m|{c lT5:5:/SVxs & /~`FshV/^,er/&f ]!|G(*6@١p#6C=(; NcpaCl>X:L~ˮ5*O|5?l͚5ȑ#wc=k s2?c|ذa:K/ <1ntlLP:(6@٠ zxy< |8P,eKFүz'yn_ū_AͧPZ}_vzz}.^ ޗ] |G(P@(eZv(6_vN,e7^m$c׊W (XmX5(PlkF\8(]V!:Ag)b}ƣz6ȁP @(l ]6 eu,eו^/ѮՓ^(r5(Pl mDʕA٠Rv="VWzl(J9(P@(6*@ٕilP6(;/](lHoEmt9eP @(`EPQY6"A٠lPvH);O}m]#l(J9(P@(6*@ٕilP6(;Ĕnmv-Oz/ l([ pP @+ *P)W&eCJ5kk|e]@V @(BPQYʮ MeA!l96E @((ۨerelP6(;%}PC̮el(J9(P@(6*@ٕilP6(;~̟?m/ l([ pP @+ ׯ_߬Y]* mDʕA٠Rg®1_/@s@V @(BjN6l`۹:v͛Eqʮ MeA!씯>IߗvGE-:-j8P @(9e7id„ JmH2qP6(RN/I}eR _/@e[)'P 3e:tv;Wy}',}b ulB) LC;|D]˨(e=57-Դ#%Pr:u Gjߗ2kE^4XV"XR)=!{*uDb[1ubV);y:+\3TE5&W*QSryؿ|t5 )x(cKwJ;=%yX/6};)AlXdeVl)5;ǤƦ mC板. ɞY}2@8`scR\>C8烟ߘR#/*1쒘 8P|['zJ:|mOՑ ΟUeP @(`ELO=;v8qb|ws=և)5 \Y(!^;-%:^׷PQ'i#^+KX엯W?DPG'77xVuS_V:љ)I2.%gMh(rS9%Şb- I?W|sskq_/*OU e[)'P 3e8p){ĉJII[=?]7D÷iX}ׄ&V6"Xh*L)׬W7#ޯ7enK\@ 33R1\@73a]0u^Lu}Iʎ@I\FAp ].,[Kns']b3 kg=tA.Pê3/mpMsNzh4q̹{[v*UV<ƍ"XPw>Gn9\culf% `v-ow3sis@٢fP XQ:S6ׁ5k-=cF瞣(7-Emm7%u;7p`k1dgﺳ{hrjFY}Vn}>++ڼoeze8:QdupX%sDQ((S_+҈< RnB.P7#Wu\N9mm7>RS|) &.' J)ەHlrנ!g+|昣qSTT ]-[6y0qm1}E1js߽h{pX7<>b)qm6/xmv/aB6/gk& ]ϕw&Wyfpk&`ngd|V´t6N,:QQ4cyj61w˙ [Dp;N"ήeGy7]|9_/*OU e[)'P 9eϘ1wܹ3gdnR)2nAJӚk)5Kh"V4z5S;xYrȜe 4m %3:Jٟ~L:y ]LkQj<}{?j$C}\]@')%'Ho}L%nPXZ=bjM;IK/P5J E>4SvpQur.P ?)~ rQXm-+3xn.ԔOO֥hu9ߵM=?<`ĄC|肚N>jNM*}: S>_GL)H/y͕q3^+w=4t*5A p@ ^<хפ%^ ;#dzf~-{ SZ!'e&gg}Mul^VDg$ҳLeoZit_oPgNXg}MZP:}PjS#joPvLWo/]jxGӞ5s~R!rAX@>/^Gjݥ*j44w̹+|Ĭ3DZTFjgתF?-z:Axٕ|{̸~όGu*x}ߣ=`usWvf7=7hOYRNTzWoẈM'_Qlqs@V @(BP_=UB us}*eOS >bu~n]erv()ny9[P{+)'6<%.vQ6/g즔l,l~*dͯrVv8E/L3)燾U֞zIW灚AWFt6aZwBg6o*΃7X℻;OQʂ5ChJKWg~FT ~ n8q~OT2jtx MXI1~r~dqҏP)؝0[;e{nYws-Lٱ5KS 1^~S]`]~^:3Ԝ4[+A[qLpXtu/xjo*l1Ώ>ե/r7g.pbampicȩ_.)3u^)_OK۴!_;*0e }mQlޗm@P @(QmTXYlc&ojNNMMkֻoe۾tkFM+\[;!uV";]^gcVfkG5SGu*+=*OEe RKږ(9~%Do+JȝOlMoZ*( VRRMIڝh5k~͖X;QJ$as|GYZDlNQK{bw4cǶs()UTVQsqʩYN ~\RŋD k.]y9?,&9wz{wMzy-2}i ba]pʫt?oxedNNd t-}z޶(PB(VeV)+u(v1 ^NNe c¡wҩ G.l+rP @l ֯h[D"ŵlZ:69D@pZ)gQl([S3P @((6*@٠lP;HV2bpRwO]%^I|Xxr9P @(PmT=A٠lPvZ;MoJeƣ6E @((ۨeA٠l-w_]s(P;ƭȁP @(4 +P6( Yku#{mQl([S3P @((6*@٠lP6([!lׯ$eFe] 8v[)'P @F( e:KtBdzmt)ezP @(`UPQieAZ&5zmt)PlLP @(-@F(]1Be;lӔ6^zzFr=jAٶ $C(l}9UeAFsӒl\< ;ƍeXP @(l)6*@٠lP6(; [ǝ1NOIlx(b㶪 $C(Dl e#]%s]ݠlKZ6%(Pl e#iDvD9^jU-\.8C]UVqqq 6뮻D׮]ڵkƍǏ3վ}={r@ٶ $C(lb e#hlvl+W.YDK٫Wۏ9pBdzsN>;vpȐ!0GשSg̙yuԩ|'= ?q)bQ,A(evlP6(yM'eƣްaut&qrZll,ӷl999K.U_|6nܨ6#7(V]d(PmT쁲A٠lPvQVv˶G١Gy$**jǎ|3gN ĉt˖-:p;[n]EEEsr@(P @[ *=P6( ʎ<"Nٶ(˔֮]{sҤI-['RFpw^(ݻE=eь m@2P @(6@٠lP6(;(!5|Ӟ߷o߅=p@kŢ[~GEeϛ7O4#eXP @(l)6@٠lP6(;(~LklfÆ Ӟ6V/ey9ߗ}jFʶUW  @(eylP6(qMI]Q|5kq~ٚ5kx7;{Ff.9yuӦM/㺱eXP @(l)6*@٠lP6(;({DeQhkqo+f禛nRϟܫ/<}_6x_2P @HW M)opߩb\Q4CpCilakqJud3Rx? #òmP%`-;K|~(aVmT.ZZjB1I'rѤwiL#RJ<=D|̭I>%ŗZ|,\&r' @5̠K㵛P\R+]ZyvtKL,"vT/:I}Am~@ 2)LɅ6ڡl-g/۬>G|kv2 oYg}6i|@Jں+>ܮ^5ӱ麑%P[ʟ"n,{l8xpBTgfƝlZ#ѽ]s('ӍNeGzP ?{<%RM Mw^E|Xl=֭P\<<%RMa_ft=tCbjzMs65a]|q-7,~I]]Фi4}(ܛ}'Q;Xwe#Z 6睿%!7SuND4M).6ʶNPѥ@\pP @("]PQieU֯n0-t 4Fl1'km(!E{)6vHz^>EyޓxVf8\~mz,J!MSG 2e}IBF{%OMJ)@w\&չmk]ۈu:{p%5Np?P @+6*lP \Rcgײ6EzZTM9~3+ ClN@ZMXܗr-iѕCX3MRfʠT7[\b5kz>L-qŔ03x\\FkDoP /:M.wz?ܮ7q\r\' v-%rLXt]dOx^˻da^!m]se] 8{mB(l@FU^%dӕ}=S}Jd b;.q@keSirĖM[)Oϗ5|w@)4c/s( j![(;6OqJ-c*Eӛ+|A@?L|tmžxozPt Y>Oⲩf_WK__jrz@'(7ʞ7Ϟ{y8?ɨ} C1a[lvJ͓u̎ tMKhQ67nDQ'HDĠ&Pvf~x̓EJ|7g+P3Von&Z!o6#P @((6*RJ2ykٿH}B(\@FգU<]%[Dc,ұSiA:2W e\pP˿B : u";f:LK%Z)52Pѥ@#'P @((6*@NvPv(q:aZ7lq*g9|6){D+d۶mt)e"  P eVl-;僲A:ҴsRkQkmt)eGvOPQmTZ"k"gv&._6AP Dl c|P6([GV4s.vYʦ klx_gt>W_}l%%%ݻw3F],ks"2P @(*W6`kj^MvAVZ >[Y6a+dٮƁŹ?ʮ\P @(7TJC%)>&3loو0+r0i}frVg5FImg0:Fz]j#|a(SmTCAaFl>\fÅpDl>V>fw]#%xٮHPѥ@+Wh`4P @j(ۨe+FlmtKOYH4Zklz]j#|a(S/ڠlP6(hq6]vZ2S0FɶoόeWh(NPQaeA:q$5]s e] 8(FP @(*(ۨeA٠ȣ m@Pѥ]"P TK@F( eGe_- ٶe] @ղ6—P @(P9@F( eGe_)Qٮe] @ٕ+20 @(@TmTZA٠lPvQJ_kl] p_vP TFPQaeAG[r·-&&S1wCx4NK68 /$ALOԂh+vB"ѵo$JFHԑMmDn2iaRJ|٪DwID-9IHvLxAR#S_RFqԴ#2tO|cvڊ)1:֣7WFZ܋rS(>qVi=8e4IŽ4MeȚ獔GT(g)Dȶ ([8슖P*P)СC[%쨨۷*eD9=i#5\Dq94g[J_)m~M:L?֝(53'p͏d=Z .$ejYgQg֎qSE=rRt_G91McŊࣤuNQvAuuk~mQqg௹V@[݈% h ʘiZD EΠ ZAD=H('"lfRpE΃LyLc?^2bQnFNufiw~Kxm4;]۠SYNuOH,zr@(ya²Ԃz63kǍ'$O=C>R}גE` qAԠ'r-Pv1-L~M>>Bֲ]s @ׯxLLLLNNWƛnVZqܵkWv222ƍ'UeW _ @(@EΔ}ĉƍ8ǎ[tiJJ޽{ղfQv80&4v@Y՝XJo5E1kUqj3o8e6H~p\#^1WX4p1Y}_s{ʁڠɴZ5 A.aoYDmDqoRv={@McfIG/٪[rбq~c@i#_(^f؜c#J ftkZ:;N5M 5ӐJӮ~fs76[UgazbgO8Rj)v 60\~yfv6m.'Մ!Cp={ԩ3sLWP6y @(@ulj^~E)ȫ0+WTVײ-itT<:)4֊T{עK3yIjqҺS6ٵfKzv/f{T_GЗ7ck==!v߼n\ol,o#w{ 3O 0!kmYkcd+Mj˽4e/!N YO4y-7g BgQKjyz杒qc3/:w7[vG,>= O:KؼQ^Խ}}'}wN>8jC7kDYI;K~w {2 ;)4] LJՙ]:o:Dr55Sv&M&L ΍sG[l9i$n2w}7jג%KxkH(zQL(PΔ._l޶mZZa(QE.%գ>V9>]M35rz^zyӴCyvH){ 7AǝT/kG+= Z5o?wmwz)K>*3 ЉoоcfƵ4mLtw ;ʻ?|zqPH(noQ=@)߼ηD3c͉{_<׶),iǕW/v9X~'uǁo8(6޽XàS 9j}塚hGLjɢ:./VqS,RfV|6oNp4yCLӋ?RZAY 膿(LJ>O𩜥5횫q9S" ǎ[\\+ڼoc Ν+yذa]t9sKķl' DD"e^P @(l)P) ~{ y#fb Q Z(=ɤ)5j^'õr@6o;rVʛ.||[eeO\;=Y :.Nf&^sv}2VZ;`K˿f䷯qV=e#Ȟ[yz.f:Fd-5>DDZZsO?@Ԟ7 wx&@ͷix|p9|/[Uп?nٶO*jкo8߻=t^T7 @ӀYyZR2iQ|vNM[d w1^D hVZgRf"Uy/jvׯ/R~a%w_N[Mw(!VV|^NCYz45Bx8K5m[rSO1&ر﬙>}:ߝs 5Y#N|u۶m+ڼ-;w\m@2P @(P)ۤD Լ}')..Mu'/;35ŗcj胟 YQw!R65񂵀ܔ{p{7m&8AXd毰Q"0nOoT~MuMAF5/*ʵH`-!sQSMHwo>Ѥ4U_>XAffIw~B0rFnNQ6o&9N@fV?eM{JXz_R65Sdݓmm\zEDA@hiJ EPP^"(M4; JOł(O].w\%3OߛU<}oѢYj,B-x^,Uţe8;{Q3\}ѧ(`QxǙ&WC9ič7&wQ?C3+F2-eО&c2Wf,;$$ @&e둡K+˖ aJ捷gU݃{4ky1.򺮶aYe޸Z<㾠glx_vxEX(Y dz: -m3o^i/5g'TKV[4no݂]J+[JR@g}kiR=w}ѓu”ybq*M㶽(O^dxԬXƯYɼ{bPx ?[n2;&l)-pLAv=dݔ6^W(׹ Ô?5 \ݧS&K*{Rli*j*7.`d١GU_}3in/yOHnӃ˘u0%Ŧ`kjmGvsަ=OMIeZ*JM˖at˖lͲ4h wfruYluVmU/;  ,[]2^YH3*o-{];jdyڻȖ::ZYr 39JO7qs {Eį%9J(EF_̳*-N)]MRSJDwS}Z0+.}"[UH<:]423ݲRs 7h٥^a^cJe.nc2c[{;ZMq/Wc>r#t_dP;R9zV)n[kYV3J_> dk5ZUJ{&EJgyvz2ew6YN{=`ok#FTJ*S:Us [ah/==..C@5c٢ƽ#UZwu˜6M^RGUn)|kbTX۩e2c\~ܹS.-rwili|Ƹ)^ܦ3:rHƲ @Nvk}[6YYp2*p2$-4s+ގ}U݂Ė#'7:أ;{_](FE\-[΄:tC+gs΍… k^饕/[L{_v9x_6@uv`I!I*6t6mֲvکO8jMk)ز ĨBS r,;% `m=bX6e@˾pm7)e] X؂ @M6 l,ƲSe;?i7)\ 6`ف(q l Ʋl,;YR?(unr8d+,jXЂ @N6l,ƲSe-\ 60@K6 l,ƲSe[? 60m7> @ `fqecXv 쯔n?VXH %eUX6ec)вP$[afW,ndA}@@*,Ʋhٗ[%C²ͮXvGJ? @.,,²l,Nqp\ nR\;gv> r~f7> @ `f#ecXvl:W&ɥ6`)q l Ʋl,;Zi 60cndA}@@*/-ۏCK.!g2ᗿ;v;.?mps7ZVR"SY+EzJH\R&zEzU*[N%T}5ʘY}\C#wr1tZg̞3(K6Gݦ}S@6pO*͖Ñ!X4}7aպʙ]eˢUSƭV .Sfhؑ3#[MϹCٍ#87 nĸԅUڪOM0jZ_}s^y*onY=TJS؝:ZQ9lUӧ7+tV%ڳv<11"uقeܴVo>65wb3z{lGQ٪ L)[JM LI)ϔ!KBe^[c;RanL%~Y;e)u$[fӔ^XvJ? @&,,Ҳ |#v~,Ołi2mhu]/͑͜[*xgUj>SReQF)JJM8Ѿ뿦,6]:tSN&V36:KRoҤU.`؅7j\%$}o<}PΙTL{Bu궤ek/ؚۛ\8Ő*Eu3Z>:P˨/q꟏EӏN/̲ӅTMߢaޛ>s4s6yRjyIݾ:QqڴyHI*|PtgRAn~noQ;! ul? 5xpzQ`TG - .a7yCTnQhͦ n?_sb/ydzI?T]ټ獵fuǦ|kGTPEI(t$N)˶YP l~a2df2--u:N%t~CEp}"K0,՛֔lY\qwQĹ/-{qܕ' d}B+ܵ *^|炲 'j5e [?ڗGP{NOmv5Fԇ :,,Ҳӡp9=ZƚfyÐXqNz-86US |Vm{{^ZQ?Ov(?is_YQ/~< T*G6OI;SwX=o羸S'?]ֲ|q[\-[?nidFqt&?³ Cۧ޵miƋu]\/YLE_c`L-2Xa %?/T&Cr)t_eVGr3;x9O? P $eE^Zv_S㥾}מ1h/Dtq>ch36dYfnjF(UI6wҞw$'G`(1JtO.}ם*-OPM5̯;Q]Rl/-{ǯ~)jZu,vgЄ9gPsZ$w v/S][]TjUVμ pϙyh2-no,;]y}igj2vm#{iS:<#SFfV:2y~sN_U{LumuTe%.@wxˣeGY=Y%S]ZQ]w=YޢY-tX_U'%b6dV!k?4C%D埭M[E݅]J&_?sR8RRrt-!73i2-rˆlaᐭlc6 CmWyi\0;kFՕxi|G3'(Oq/͚adh[eYTU(kIJvrJa}%9 (\^Ceը95R9ғm5^S, u3xa5׽x5}{_v% ;]kDLT#s&Gt̩ܿ=`,F\;%X-&,e'^2X|5vjT{YȢe[2澂y/sTyHgl|Yȵ݅]J^]D2c0܈-fleٙs|MV.ֲAA&vŐlc+ `mVyiƑ{ C .w: uNnɖq.a.EOmN|^dīIT[Vjñnʁe]l{!@O6 loƦI6wlr(ڽ},DBolynᐭl3>T @&,,²7uM.ɖq.l9i,6:vl%Ʋ.eی,@tXYXe{cv$\;J6wN>ep n7)NF޹sg۶ms̙)Sʕ+/_TXvJ? @&,,DIJ7uM.ɖq.lݲZVᐭa]v-RȊ+o޼y?""l˱l!@N6londk}G&کDzleRJndXv|lْ>}ӧ-S}9@I6lM}]~-oe-8Kl,[c)KJmeo(7w;,XK*/Z˶YP lpƚI6wlr(ڽ},˖]PGr-L^l~z,_|.]@`ف*q lp11֎ {wvy_vZoetٺ@DnI]0J؆έR n0[IT7y-Vc0K ҡ[6Gٳh-V @N 66V# %-|2;Xv>Orn'[vNܲtMO& pXhl9+ϟ_/{r_3Ζܖe˕ @N@Z`Q"mXESOk6$e[x [۬oDzvꠠevR \,[nE~if')e{ o(s&%1=DNcTR jLTLdN1Q*aGn7&:Sy#4![dl=Z%lC1dKӿn(9{"PǠRvlb.y_v9x_|X6 @v `Ʊ+W d 'xOD޵JM9FiTMo,{$@ p(9osN{eI"O>dT#'xODJ0Knz˶@mm!| @? ` e;3MJ-hk.:@lȊUR cS![1cƲay@<M*V@H9Z2Vl/KP #,4bE 'hѢ*Ug̘1o޼rDвe֯_cǎZ5auƍ׼ysS!ܵ qӿepVX m C mW"%رcG|Zlyf9իW80KnРnٶ:)裏ڄʩ]~G)m7ò-~1l @0-"+V\VA {C )V8};=g͚geB=zhSN_]F3dSOmڴI[%;-[/^fc٢ٳg5jԨN:eK˗kMJ,YHZjԙ>}zt邂o1c?]L`6X1l @GXEdŪK ""H"eʔ4h|`|MҨQe˦9%K (0~x9w}WuذagΜ)R8tPϝ;WXXv4iڴi駟ʛD۷o/岡4جY3ɬYG rO~Dϟ/Ҡ4bR.3gN|ʹiӺבmvaw--M(,7KP #,"bU& eƸ CÊwAYbŊ:u{u֕E-CzwP^خ];mQ,;s̺իrRmɢ۶m64iRc˗͊trB)nf hp4nRj$m+e x$e[DVJdYE~#GTP!-OE>2o\&?|cq)1… KF,[fkun2]K.ײ ЫիWݠWoL;=m7)5 ˶=T @H˶X M4#)2~~h 6tqgq,;^˖lauVmČe~u֌X63ܹ J bBX1l @GXEdŪK@-.ۮe-ikb3e*>.-O9۽Ev2\׎gRSF"ڵkR_ƸԑGE1ޥKmzѢjժiSߵ ]"h_F"2-SA-~alc@ lȊUb[c1 @FXv9K@eQf syWcHlc@ TO@ƯEe>8eʔT 1 @FXu m C 'Q݀ $e{%@A ?!e x$eIG7 @? `c *@ `$egPG @O`ư< @ `~ @OXX   `+@X1l @GXDqt1 @FXu m C 'Q݀ $e{%@A ?!e x$eIG7 @? `c *@ `$egPG @O`ư< @ `~ @OXX   `+@X1l @GXDqt1 @FXu m C 'Q݀ $e{%@A ?!e x$eIG7 @? `c *@ `$egPG @O`ư< @ `~ @OXX   `+@X1l @GXDqt1 @FXu m C 'Q݀ $e{%@A ?!e x$eIG7 @? `c *@ `$egPG @O`ư< @ `~ @OXX   `+@X1l @GXDqt1 @FXu m C 'Q݀ $e{%@A ?!e x$eIG7 @? `c *@ `$egPG @O`ư< @ `~ @OXX   `+@X1l @GXDqt1 @FXu m C e h(JI#G xa @Nh? @@Ehٱ| @p#e - @pF֢~B N@1Q@D?{X@lo @ nnS|V Y[n Ѹg`1|}R߅(CHfX6Q @EJ To-,qɀeb.C @ `ԁ @@ݹoCdIzi] ŔK?2M@7$JѝK6t Xݯ7a `M\A@$Zx:lZ׳Dѽ|챦TFb/v/uk\ɨC-y?A:Mϭ[X5nV/ڬx#?B-,G@ H;q Kvx1<,4~QG(%l}\:&ejY*jܥ5=O_?=wibQܜ߄wtY9Fa1~IGy䩧R.:Ju)XVwwa]%,o}D k " @5 0t3.Qfٿ,93j&f+4ZTZDL<I ZvZMlܥZľ'/iXjnR}Ѭ5Jέ~is7 aZfѢؿ͸obŊ<|6n^z]93fC6"Gdl%o}D.Y U @@ݹKpY_pu&;kr|*mq|Iy>.|h^6§L񅻽Q# f`Xo֖DҰ%Ck."FjtF;ؚK\*@ )h'""@ `M@ݹKp"1 Ztӭ[*Zz\'&W1sW^\9m&%Ka?I#eC+N.;B|=Oy.X= ?plȥ{D;s(T8dku?OϾ}t{:^ܴ'g*+.ޣ._Ҿ&c4[ظ }rɉly~+o9ujsT=xs>_Kw@د;Z\éMO\D+X%ƼrqGwa[{휓\.Zy Ո_OO|wK{>T67ƾ`;"N^S6q+v32i^~}?mC~H@9@ kΥgͱk_h];Bv-^r[wE._|pϞ OwmwN_=LgjH˖Q콑;6̎'#NqџDF>rd?bK^}d~F>u?B}RÇŲ_QQPozaZ|58b+N_?m/-Π NpSvEym ҐILh߭wbf}6J{S@U @@ݹZk7YŋϞ0~ΝΝ>۳cǫ-|S{ZRdn\##;+ꝧy,kc}{G]|w҂ӗ矺4N8|rԾq哾_p&{oGOIs Zdƍ%ƼNO/|'Ν裏Vk9'ӮXb˗/P@r $Ν[&*Uk|\/7c ܈}nSqpACk7v[_,[.@~K@;9@ kΥgGS썵ZE4#妽=jӦMrT믿n\/|џnԵ qW2ޣRȏUiex"[-2c\FtИJ:&kE]$Gz+]z.s^dzq?u_Oˑo?j68wrn_iΩύqָĘP/̗/_&+}Z鑚B}=zܵk^oԪU+Wdp';׮ʨq9")Nk2r怅kvEv8j㾣#,ۺ{sWoyw)?ey  U @@ݹ]j`ӨsLիһ>^᯵B-GZ'wӺu]|0ah.h]$NY<6wv.r}K,zyTs"4"O_'u:>^g?Z&1ݾmgkDhն|/˞}:*ش)W(7T尬9#-$wD~J;f&k:zV.oKVbagl?qyiW5`ԅ_~t ?;}3S>;yIɋ`] >'Wʲ KSP ݗaU~u+/LqjE%XWI$];(B,fI"2-wgqMF zctٮ#ZA^x1@~K@;Y_UY @$эUHi<⩪2;lE9ӳo~sW5{-Zm^+Qd{c$wD~J;frPK>m'M:s@QYC:Jfw4O_%sK,ʶXIJ8ay}NxO7t#_}+W#_GOG_Z,3DwX煞/J x?j7VRsMl3ˮkoLoS}Wc.m䃝h7`Xw<  @& Ȕm e۫{ *-knY~S:276^ڿ< Mx"wD~J;i/?6`ޗ]k?q,.=we85G}՟^{-k3w W6ywYiKmرZtFu>֣kU}fڱCw~DR"]ۏ6J*=Ƕ?tW]Êe + U @@XK3p;ݫS83vmw[e,;RbN/{`ͱcsg폚]G8}cv_z-ۼysXXX&@o}pHa"ŲGrDJ,.ef\{ekٲH:K6휿fˌLMz`F!%e5A  fĤFϸD8,g[cntۿۮv1ej,ۅH˖eG?,̒o׻תm?m'opmOyhbn˖eڵKn6uw};M vbGDW^~]Qk2'C0/^SF"ejbhaF!%e{WP  ft3.X]ߏ ulGם-c>˖%VIGz"҈ˌnH˖vE\wgo۳җsնWlyu٦?8h}]G.}q1gse`Ì?lk֡V^Z ITFwE?7n8}Ⱦ}&{Y//Ѳb3wșOzŲ bxAi.  l눂 h$&Zϸ*o e <췝olipcKw;c\k_•|~J c~ZMl܅Oľ-_UsȴM;wq_4Q3rRދ}f ?zv}Wo [nzqMO7y7 :(wȖ-[UOo,[ZD۳^7'$_C$USR۞5Rem =NI/pd $?DD@ H"Ҥ*z%t9x]n[D[;fs7>y}M[E"[ݓn]Λ3D?}Z.Z|Lľ?ܖb˃δt7{#ډ1^VI&>.]Z|yU;\KM7`/˸Wq+W"7%,5m Oh'"*k!@\helY(uYUZ쨨(y_ˆڢ|Z/?-˶fM]w{ORnh!͌<ozY;7_K3b5눧\U>x%cGd%M `v""r @$НKϸD=]zbTl%-Mj洭2[O?~"s]6=ʗWX|qo_l/Z,}g@߸ +ˍ/y"oqv]5*)C6[RJN5k `M?Lk>@pQlN@& $ Q |j4 31 #XvF2 @ $!OX0VHRa&)^;kŖe'2`s@@`=֢:03  @H$ 8/.+~H?L?RRj%`+#Ȑ!@B@B&7r\)))bcq @`>Et5p85G/^e0)@@*& aK ?4>N@!e 6 @(X b D@I, @ Tqh 0C> x$K0MA * H / ` @ @ @ @ @ 9 endstream endobj 8 0 obj 126057 endobj 10 0 obj << /Length 11 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xMPߴȈ)R?QBZdLZ?vi~hfbw.a\p9JDAAf7,xBr{ν?4$X67nS8C883WWI}|ZW$V&̷Ei&Ǻ!CV4=zO3K(?'q;wz~%moij1˳mYCʟd|Aw\M~$>B#p·hק:^RN['W®n3{%d{ )8+6I;r zFsle}䎹 >zk2G/\%k@]4>q`>P Hd@TVŦ׸>[)]Jc_W#rĝB%bC+^^q]hD|K 9MՌ3|7rY马hqGVķK0KAO\+QBUD4 yPLRfs[oSgWg]šsw{8G)[$, endstream endobj 11 0 obj 560 endobj 9 0 obj [ /ICCBased 10 0 R ] endobj 3 0 obj << /Type /Pages /MediaBox [0 0 1314 806] /Count 1 /Kids [ 2 0 R ] >> endobj 12 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 1 0 obj << /Producer (Mac OS X 10.5.8 Quartz PDFContext) /CreationDate (D:20091116081639Z00'00') /ModDate (D:20091116081639Z00'00') >> endobj xref 0 13 0000000000 65535 f 0000127500 00000 n 0000000169 00000 n 0000127366 00000 n 0000000022 00000 n 0000000151 00000 n 0000000274 00000 n 0000000363 00000 n 0000126625 00000 n 0000127330 00000 n 0000126647 00000 n 0000127310 00000 n 0000127450 00000 n trailer << /Size 13 /Root 12 0 R /Info 1 0 R /ID [ <58cb235ae3ee76ade361f26797033340> <58cb235ae3ee76ade361f26797033340> ] >> startxref 127642 %%EOF ipyparallel-7.1.0/docs/source/tutorial/figs/parallel_pi.png000066400000000000000000003421611412533144600240640ustar00rootroot00000000000000PNG  IHDR"&n h\+iCCPICC Profilexϋ@ǿi-.Rݶ )ItmҘUW7x zUO{E?"73&}!3|ͼ73@y |-yfqGpu#M$SWHחK,4ORˌ5wC/$ذu!qZij}owg<1l7b5.:Hǥc}N<]~F<[8y}ñOv/;/9ȍO2S$@$@$@$@$@9ǝۯL-1ߊ@1o#㲊_<ů>n{=۞so;֮;?yM=zꏾ9'KN=E/~]x#Nz1)iQlK! =p1/DO3[>ys O=]O[_rwdC׏dA$@$@$@$@$Q¿?{e$ {iC󭽇^0=cOwt)iz:pŷL6=?{%t9%:֫&;? ׬֔~hzƾm6ɹ{ZM?'Npއ?ƣ_xun?yt 'q &KEq {am>Xs)|m?wr#d#6 {oި-9s;?H᳷LD==c\"ɍ~_XKN6I&9]䀣^$ /NY=LU|ȞenݱO\k%^z.;c_)']vŚω>h= ̖fo1݋fˉZՖ3{>ڷ~}m5CCDw ޏ1}[qOXO}t{ƭ/_x<\'#˿(8Sߵ'm|đһv|+'nAO״nbE\߽=q'm|hw%Z!dS}wO;M?)O_=;6bw^%WuFtl[lyמljmO>5۞vv6oyu'ؒ>LxU6|xK^|q'2MW'׮wӭ[c= *hum%^e]^z҃C8Ocd|8dl{k7ٱHHHHV'Zm^Z |c;W˷Aw?__Gbnr֥[;.d{ =ͧv\     &pꩧ[Ul) oϿ_j);7E$@$@$@$@$700]-ۑ H>Wo[#      YH'g$@$@$@$@$@$캫Z l-y̖K.Ă      x!!;f2O| jh~ߴ]7fHHHHHHV'Tӳ>D"WFs$@$@$@$@$@$PCֺ-5HHHHHHxN %cv[bԓ @I4\      p;E.KGl$@$@$@$@$@$@qzI;Ng=c: HHHHHH MmXWs$@$@$@$@$@$PN s$@$@$@$@$@$@1{lB$@$@$@$@$@$x^] B6{lB$@$@$@$@$@ H^<2slO$@$@$@$@$@$׿i|c%\)Ymo#-,/䷦Q|Vny*O25$@$@$dsw39X5!GvuKysOE/)Q8a#$%K*c4NF$@$@$@$@%=Rj>fヾ-d\vFvU qsU9v }wcD5Y+qdzJ4dr!/ĴHHH`<  7r4_*sۙ~C 6،݋hlU>F!!G* ,cvC,+dT^ 6d&$gxI 5/)hl(hmi&k:2&    h$CB?c[_J:.% ]MUi^J VؙH +HFe5\EaI*xUXXIX5桏:a`ax iXMF2k]83'\ERe62J$j@ ,kQEB͆½D;ZgqPP؎2   XVfWz>| s ( ..6wD6U7&@h˨>}fnBC`Z `W%VAjU) %kKtKuA_>aw.EfRIg-t.՗WݥK   X5f{F&X&_K l3jGμ>hv zYJ!4I+Ss5 -TD:@3QK3.j"_]>phr;.P$vΘMUk%Z%%B`*ȨRqQz؊@30ԒPyZ&:˒LLFT)A= 3 ͉R$   $]-$+:~8Ө cؕGNc/3[A*vm-$#> \PP)i53ąc8K@L /A@|%*HjIF˭ }æX7 1aT%i'L5镒1J4V%W_>ѽAtۈ1 ,׏_V}ª]Fs!j,:aEI/>]fI $΍ԪK46s8R|Iw$]~U.9N%H5̗Hz=jUB1 @@V9O4j2N6kpWWn URl |R3&mN;^NN_U}&t3#6#&vxZ*}'/z<7 @@hq$>ե>棲0)_opF06aڱ ,nG:qpR.gd5vqIF'[{yWx9 l/*XphƖHl'8ǕeZZA+\uv0+v2\h pC%dq 3c+oI7jPU7L_>NZ vATS5K   XV[ W%~s:M@hjJIlWWO4vS&D +sm,.-ZhȂo2JYr{o;&-*Jħs~(| VKƃ  [8KyKp ']wU{M?q8!{{tV|Z.ז[/RL$@$@$0/n' z~|΁^3;/\9dM (y'0مHHH 4и~ f\qZm?`rpٶCƺE5Vϒ ꦅujje~07R6(ZETTQne:%ȣ-KLd6+j6b:tbBMl] wYt2G0Z(m2GB' ߨq)j"M]z' h npkN&NG;fͨ,}`d3Cv )tV)$:O&j:M`8!=`i#&iȜ` H`?pYhRӦ!95(8dkGFU*XgKK&R &h>$݁8eth2o>:=iKm2GeuɌyj)oDi$塃K5tyK0M0S=duafR4MTF]&Gaaf QvYw$K˹J@iRp|u%C;6L:l}%3;MD1j}zF9WGsGgU3 <]%L¾aFDWS嚗02?d(+V-4)_g75jt#u ڡn$>Uu1:F˱j*Mc uU4`)0<1l2yM6B:3I Mi/>l)Q߭>IƑ3oA#<mV;H^L'!̔쫯yJz eCp0Wf.،% }Œuv3ۯ/E6&qۼ![f32%F %%up'܅/\U =LTs5Q1lY24Oi$l3&m7:cj 9G'd*A^qtd5:uF|vrp$1mw+igMB2V]lEu&\R1CZ#NnUXE&** jWia3K[C(:;PϠu+Q:,Am/]fdZ K&5H >) /g[f}`Xl;$]pi*I' ')фU6%V)!sK%M -)Ѽ~-f` n2tiǿ{qk8Z-SU^.n5H *‘U #Q(=fGe߶$4)Scp*ŁB5CmqJ%_K EY}HQ+k;p ,,z1vux+$ѻXzYrV,%XEPLl#.NT1vvљm#1lZV˗Lu9K+v#u&ںD## 0Xtw&/Ur/df-~tX2|Tl5mcm䜕R2UѨyl$%QlWTs4rUW_aܾJf~lArɁv$Jg :K,dDc.vO/˶^˭ikԶ1 ;ffB42bЧee۩j/Pˎjc?ZwZy$ U: .UjL/L^vP;L@!2J K)UqTU 5N`UK j6Kkՠ{>@m(K62.DII`{u[mjR2)G:Rjpk(%\!_yl  hP`5ZXeJ&&mlUغٙ1I 4v:j}\ /I`4Wcb+QhLũR3w]RYoS'ю M pSʣ0(Tm|ELنJIe%ݥǫ$bS0{qbt*&#GF8KWhiK5̾l&:C,XL +qR¡Skw ~ݍ B#}fAFM#Œ0ὨG,!  E!E3,' gB*[ V(#_ufQpGm@O  %&P4_?r\6؅gGvn۸\Lkx.[.lj紱+vte)mE w :rߋ|lD$@$@#ٳcKg s?x/s/8 Niv]i؝?ug80yΫ܅'< ގω ~LΑ %PL/Kz5fVMf2V982Ne sg>KLy*SFe0 $w@-dyBچVh=$rT It2``P٨ |B2L;q^2ԴR -+q]dU)2T>v)kUX&QD@N 䱪IAWPbK PNc 8 TwԢFߛm¨Kyh 0)B| jjEal25a]m4iظUsa2*ws8_U%ݥv&VccdqLl @6=-kݕ[W<ՋjߙըYH$@$@+H`~~E%%f/BOF3rt Û;C]șbU#rN!wf'M0O$@$@$4['/]ubY f {lӸK(Ж}(1L P_- o)嶡OX[ Ɵqr?5aɸLYWnwYЯ .{U]fvm]Cjժb  O' N#z+qI%}`8[8 +۱_a2%2jV}K|27F0Vi56{ˎ{߼%bהDSW$)}_Rm:Ow6>lK>;sfUJ.IlW5% Iwp3־@\F.P6uIw-RIOc~ie%i8:p45Z2o_[EW|ըs4Yh8Yt ..uh2>L&3z\&{l&3&-&>CFNj&;NUY̌5&3&\"  agyǞpuW_%] g3GkfTjVj%NulkP/lva?L$@$@$@$@+EW℘߸s̎m' rϧnNZMڮO)6]yRQhgNXcE%n7ZUVDaZZe cM5 4 G*Fr2H)2hШ yFKaa *,u RN5j@k,\k^ t$0~8.ʝYI%Ai2Z>ds\ wf[ Lhfª0Ve2(-*p&=T$tpB˸K)3ʣ˨I&Z1z*EK Vfc/14$VgD[Gm$ԙK 3k-t^ t$7o{bX4Ҥ2ƺT>IlEglSS]mQ?w.ɜ[z/,Se6-Q|Zg)֡D^{ݦ &&rI{)*I2*(R%.5JgHI BF v_3ɭ̎ @rI)X+ vJKU.AcXTZ -j]#%.#%.#Dﺳ@2a^ Ϊ.#%HԳDvgHHH >{%c%֏jd)%U%Zb7jf[ 6ۗt[ybMA 9nG:$St.]G{1oHJVhXvDUhU2ݚغKTD} gKV2vEOI:Kj$%%gL$@$@$PGcHCIIglIl]ĭ5T[DcH>r;f݂Vm5{a7(HҍH [Y4.ڒLZ]Rb[0&  X}H.~X\~ljw]. Sw9,f_(ᆵE6%eHHHRfWZc}wQE#$.)l!zLjNxvFèHʇ,=Af4lgJ2Y2..hsL9sK2an5K$K kFm cv]j8jG1 ,=ҧC~hK:[e[w@ثJgK;6ZKa&@ 7 .u$\<6xv@"ըƚy ވVKfi54кY}oUm{Y7;s_|hv}Ǽ\%  !0"D%hI4h(9l |QqT~F6!츅=V..uxt7KyHQA4P0i_/Oڪ.}B$@$@$6l9,g.'tWIpyg/~HHH``70p# )k_bs̎m' ruo#nHH@{$@$@$@Bf+uUU g8xwzbQ0v]k:Oj26hwvGh'-ʫC= \=Ͷ0e4U3%0*2̨/>9k⿾UVs1 @/fӀ횔6@4}{nyU2Yź:p B[W_ě>}ݯf#   >B%>(mI}GϏI]zᷣaʹ{)@ ,+>{Y6}"Vj3} VN ܨX1ΗWMWMZVc78WA%l/)4_RgEP5Ѥ|I( [،3%̐R^  >RII*R=Rdf sG\VR^f`"JW]殰R dTN(hI D1 "Ќ iejUQe5:.E%f@`ۉ*LT ey`}l/V Mڸ/$@$@s$pWStIfze%6êh/;eԖ)^lk]ՌSҽdv}vj$A_znL%˵ maIۯTƑ jeH֒ N\m55nVZU'&Doc d)GNU` ΓR6Z7RR2-l#7$K.?KKRϮQΘy$ {HH`Eivݍ1ۜ `<ϛ%*Sp_:t" nvv5ʍ.yNf% @}R]|V-@oGWG%ؗ"u]|DU:Kg.+ (kH.u#6%]uKztσ.*;+ c]dph0]ΨCnr园. [%fHH&i㍶]mOv2M5r`%nuk[ `2n[efg$c{JKCZ Y:eʧJ9ӫ32HH`J`[|OvO3O.c0QtѽXʢM36+PmTXd}YE}3TB)e*-tID]<R4icL5T>$,*dm2l6Λ7*CAɷhM$V f݅ [8K+?t _EHpy$Tg*  $;xZͻ] ,ݻw:i݉rǶ H^4e$@$@Ex.D ,Vgl9`/žݟbzs}?8k g*lZ"i!1~cg   U#`uIj|Ry{k]ܲ^]h9d"   (#e1{vqc}u.c\הEɦȺv)8;칣= @J`[@cM1{9D2m4 3. L'_DsYĻƙIHHHz!uQg-r 4\2uwA`zYg(R./7'/IHHHH}R]/N@a̞t#&ze4+om!@2TA:7gV 0ÐƖf1:t([OJiyuC0.9+*'a>hkQtPD5WIHHHV@ۧ٢_2m=͖OR/|L$ܑDfvAW43dDڌ#?XǺDNj&?T8@`36MJKq ]&ӸjK    %PjVx(38F66^9<y;%Լ:KpKԊF^ V](ڥK] IDATR&L ۅ2$upՄVۉXd&cem;Ohi5JW    |]6hwN}}@Ua0#Vc}TaI_իOH&Hvݥ­^Z[̙yc1 [aFLB̘HHHH}R]hqGU: <{jժRͧf5sI/jHHHHfݩcvɡN4:J&J1lZN u)D= ک&nn6 ffδ r(ym5˱(+&]2 ¼Fsyw-&BɄIEQƪ 0VK2>-ey߰P2aR 1Ltc^ٗ}dB7LZ)1$@$@$@$@KI쥄PPOBnVm2kkKRGl Ԡr 8HmM9%H:2ce֖4V*殰UEkfǍ$@$@$@$@J>.C}=Ӓh+EV]28A2ZM< YD ɰvv^p&R/ɔ1[htdK$Oth2?3WIHHHVf [8K+?t _QHpyȓI}8Yg-UZYH$@$@$@$@$@"{V'mӝ(wl;y^ϱo9$@$@$@$@$@$@huƖcͮ{"81g^~@kwݜ,v}.9 ù86%   1(V3 9fI^8fTg+UuO7h$j#" HHHHlK<1{u0߆r%u?ي} mB {cq6    '%#-D9˘#1lǼٖi/[HHHHz'uHy̮6*#=iyWlO$@$@$@$@$0&%OfLs yX 4u>v)/⒯m9Qk2zn6@Qh+0OJTU5OH&a,u3 ;d*cgz73:cnV&r% HHHH`9}-x]>֏h.%fRIQeU/|z1IvRL*ZD &0VEamcUKifR CAIHHH`9'%r.Z<ƹE?YWiɦBpD +| ۍG&{D5h;KbKH]*[kƙ2TFW3_1LF_j5sJWH+&љDBaɴA>f`b+/PY쥋*Vgzwgk/K4VϘHHHOOQ1~X>PFQqa2ӮaYemN`S7j!%%G tq!k \aͮ1Õu;\*0RRJmA̔{-$pݻukd$,*d&U.HK6LIlh RWJKmM,>' uUց1 ,>ͮqV迊.ϵѶκJE?V)`\&%tB$@$@$@v--܉rǶ+.zIoO&Ɂ }80p#   %-lrS>['g^PэGCT;[/7b2#a-x;a/mw1cmu;z H HHs%ks.l~׷@ѤCͮ<ͮ߷#^ֶ 56yz!wtݭ0~|ۻk.&$` 7TKKƘHH`iv͚~Zw? *_¨=ڎ _܅<a%0+blA$xi~~rF2` h^)%U 5K_ m/ PUd$K03kǰ]~$]MUi: K LE5eRW^cW$U%SS!va,9YTP,ez5vqV-4:$.V5OZ Nz:+-T_h%j P4_etz'%jY%ʭ 4jlh,&RVZnc[yqPQY.1&Xemf~qaE?4~0YAT%ejThPT<$F&}S%|-5&3&ryw)..S%w$yy*ƘH`e ivݭo>fmW.bײDJRS^f\Iew @iK{1)|ݝ\U%%3h BsW2Դ./!MfCsWzjq%՗:;.)WW( 1C fR2 Jh +~{8z-T7\|c IuILhJ}_H {QJ%vĨIuW?z+Wgt^{W7ZWCQ݋vf$@BE׬,}=9?41>JJ=OFm[ȗ]uux{\2eKlg<{6U=B \V>%bv^*Klǯq^$@"Pj5>vetW%\2a2&}6{N*dTQ\..Q͐2:I04LW$ռZNIZhU04L9.K&l]^߇Q ̝mTJJR|>%Ǝ#O ƪ]C(,ʝ$X2|]wCf˷LVmO w[%J4NF5U6>-)) 2ZQ]E FOμ-% 3̶#Eu<hPҫDcT&=:O8I0Xh0(UvV: mL3gKưZa=.i_u.D Ėmqy`)uʺY6_>[d¦vF|Q@$x9v}EO}GnP#o*,UtZ&Ìm,z^D3m z|ӊZw2t 3$Tw =s_B\w9,\,JO 3]kT1Vmm4iqf$l4@b_R& ]&3&KQh2-&>\%X|]w7l9,<%E!]ѭexoYB AއM< ]9IHݻ[;QvrEE/)z&ݷywc#Z ̝.> ˥&7RGg:n" Pr&7%g{'ŏ#! sky΋e 4"83 (VSe~|#     XJ|]w[y̮*     XrIuI8cv1* IHHHHH`iv1HHHHHH` <%Q=QQH$@$@$@$@$@DO6uXE$@$@$@$@$@KN>.Gx.FE! >ͮͮzGvBbnkh{wγwԮ͸>U!γ;cxYOY{Lir_6T?]X+t1{9VmO׮MѴ?QLzĘ]^N>A ʴVbyYTsƕRgj;nUh~ v)OkЧW&Hߒ^n% OT򗼶\*,48lYhUe*%Z۾׫BP7V5Y1 @᧺uIg.sQ( hª0SS +2Ҩ*ZR2s&jΧ]D}Jz5jB0#{t>n^΢W e It=Aiʒ$.?,j%eVXfN YMoo]Ia e$@$@$%?h>NOSm[}*ٮ\u# VDeiVKr*e4(D =Kf.є 4fDGBȢ5љ;zm*J'C \8aTDI;l̶{p≈h7eqHH`;En21?1;A?G^Is%K c/ Ɲʎ^± .6[<2|HݚɼΜb Ƕlf۔xܾ.u7>{t=_x2^K vݿ{3;۫[vEykoޛ;_6os ,1=ۣxImcvjUȻ3Fq_VZe7a: 6}˜N6upQCwT[h'sSOT B}ԄɅ&O>`_9c>-zЫ/gN#ټ_3ƛ$@$@ D1v)jJ4?-/?cKؤ uϋj.Kh^RI/5* RW3# ]]B6{HaK ܬd\2NmZ1fvU}^w88iƮ2&xć}}w޸󎛾qʏ˟˖IϻO3y~|?[7{WNտ_zw} ^o;?ڙƗ^Vgv5oy]u|ϥ>' #v3h4~ ?UCMm5mǶDDjs::B6*фð:@_1שlڶUݗ:I+U.üJ| n bov$]%'d0%*74 `U H@>_uǗG}`=}De2yʶA=s}{{$?N&ן_Ӹ;&3v^ߎyÎ}^I_?⹺IH 3m둻pO >,T&jMܾV qT3dث$3I %ޥ%ݥںjh\x..p$Y(faZGlhM*%Y z]Xj%tO%%] 0~{㿻Loc~]?Ɵ:MuW~"^f7{/{֦di/gpGrfHHH>ǖr%VOK>Q5ՒY&c]*OFUQ8ּ.rPDVpJ|RhmfJJRq9lix俽ѭHH`y  O3Ox^CQɎJ4x[m* :_ lj IDAT=IX8klx֟N5zteh)˿O}^v| >v^Eo6;}|^o^\+y_=6M^Ͽ7ǧǴ`H$@$cyɾã{I񘝚m~^x$@$@'Gv*؏~{d'Gw?a*'G}{>ie9<Ƀ˟09x2?%g^(M|~˿`Og}-|yGY`{$:/]oϽ!b@$@$xʏVX;f\z/  1x~@mذW.½%oONWOy$@$@$=V(DGjw̎Z0I$@$@$:_۽ђN48xrя 9XF%]! Xzjmիy]U8X7?[?+hi0ް4 2 ?> 2cHH` .-)@%K?6lcšMɜ2GUgZR--ԼH%UX*S&. l; j 4Zޥ]83$@$@$@$@$@$0-qB zOSNci4*2@|\#d8]*+LT٨ G<S@$@$̹&! _!sxdcr&-ǝܩ[ 9tUZh5c'{dg%Wk.iS1AߌKn s!1HHH`H]}k*Vr 5:…p^2% wW:NO/׾C$@+E@؟y䑕57K$@$@!_Y1;{{]n GQaHHH`̇>fˉ>\nnJmm7/IHHHHH` }ӝQWȧ9rK6E @1۞4vg­J*Uq^*VW+F[e8$XVnV+ v5&m5h     XP?\ɪ=i|IjUJ\/R۬?(ʬLp9 C?T\j\T,zdø\"SbRFYѤ[c@$@$@$@$@$@N㋾O$@$@$@$@$@$@s$P4|gH>y>\%     q8$ bz-γ3kH %PC8lF:2&      %Pt̎ҙ30I$@$@$@$@$@$@"Pyt;TZʢyXzgOvI$@$@$@$@$@$@ Mf˱S_UM%#̩|Y[we9 x~amInX|kWNhcL*"vH\      hw4z>&Gs ;G{"5% hqn< #;Ij'бd̓ @1;>nqz ( h`<$@$@$@$@$@$@H@'C {R&#KRAxi%֍Ge@$2SI$@$@$@$@$@$Hi6N8LYU+|@sʧ$o{3DF춖$@$@$@$@$@$@ P4[WqܡIxA @26 h~=Ph]$3#aF*%Sݚ$@$@$@$@$@$@!P4nbwڬ3a , م;DKfUFRҸFH61HHHHH#0ۧ6XI:|fl O2 o JvV&zW$@$@$@$@$@=C {-塝7-AHHHHƌmwѐ_^)W/$@$@$@$@$@K>' $@$@$@$@$ 499QzyXK$UȧL\>vvhm;-\u^a$ eNÌ RIZ+vI\jvN#VV 33$@$@$@$@$@$EOq&py,2TEKɌIKa0ӽc3LiM$'X{-) 5&emBh}$    XnO3 WޞgTnUKl/(SZ+S>Oˋ!%\f恦6 1I^^.Wlr&q:ZIlQ~%     l=qƶ=|X9VOQ>l6LVGu[%LR,fp-Ri3fcHHHHO#>>"}3[ZN qpŜ:o9"    U 01GCi { yWwm0z]F#(?|y  9O~^׿xy˿˪'ꫯ.)?s#z}o}??$0xֳ%?&t_ 5 %8a5ԝg Xr'K/}_^ݻwrFaST2$qrԮgg_WKR~@]I[n袋~[?xW3/efwƻb;vFyP/_y=WW@1[?qB?ӼHOv1V /C7ή"yZbWOvHVB"d*SFe0ѠQcX@08UXv.a9&$@$@ Jgl裏Fj;rz{sM7I駟SW~| ' jG>Cɣ׽u{7Q~XV'?, .@eKFj嫼??~Qz/=3T/#5\}O59tI~gI[x+_J S#8B]w%?S_yx+U'餓N?t=]N5qDph" gg'|sɃdf͚/^b/{vT6p?~q^pvlOY0H^fÁYPJ^c@]r|B'N)e'1zؖݫJj$7ZϜ+5;bp6B-W& €0P ~C6mxj ʒC@Bx4=z4J\+̙s뭷fٳ' g}nS ,@D00.\PU8DEzco,**B=sWcAu۶mы>د_:/AɧaTh!.b %+~Sx@oasΨ?QrpЋ_`E1V_+HC!%^ve(QYE)BW5JqH@XxP8b=3JG+Woy׼yse9F~IУEuc?bifC֭ Ɓ }J8So?P D9@K;,N=. %M~qGy (3t/OlOO(2xsW2qu(=DQ =9M|EH1y,z zz]}zu Kq(FJf([LŰe_#8 "FGF@8lz2 €0 d,sPHt Ib|Vs̤g" EȀPB e0 1bM@8ѳ٨aZE=14yG1MACS(ڱE,&>`FBX?Aװ9LFPXB wŏ^f}G/RB24BYvmՋ2ua*T5!ϗ^z (bׯ_QsWfg?Gž'N^XV֬YS 2T+13o<=eQw
~`DӧO# evڮ.<^ZZʋ 0XN.K\KJq =~suy 5^pq|l2ZftܯQWD"#\8d'R0TB=4 @0` 44]Ҏ* cF=J:svJ,a@"gK|Qļq"p,FYjlbČmߢ 2wA6lJ*A:l0LTC\6n܈0ӨL޽;f!c{Ŋzsp4&fQ*M=~aeFmsU%Bt | a֢E c1鼺?n >yүD6.wP4;3b2P(h]U"'xA% v,EE!J]Nyu-2]4F<a@*Pa`hoj +`Qr9plB XxV5x"eC%4'M,ƻ^ SH c~8sz93,&g}1Kgssʖ<$^aK6xg({X4-Q:<ϝ^W^zANf pur. #/)Xe` C ΂Ļ? 4K2lb]+ht6uFTT6{X]l8 .$: dե(w YX:ޫdFnˁac'* Їfؠ0Oۈ €0Q yi , Focʘ6s2f1m-Y*W_unL,ۨ0Q,vSK0P!.a,Nx(QbwnL<q?sȇCcQa|}K1mlK}iĄ? Sh(þnXW_ma(TGtl3@UB)Tx숎QcQ1s5'~jÆ 8p4ZȀ}4KF N0yM8Ǯl %(x N#3Qi?بj'd9y^R?8^cw* O2%W_}ºhlcdl{ |uQGLY TGy*5Hx]P{q6. <3V!m;`yJjlS 3Tg98!U(CkhQcYWSjh%mW~DxqqG; XM3vu IDAT!kbMx`Lfe^f^]FrH3ve9Wf*Da)ƒxӓdF~| - +{aGUUsU$̼!Tņ5?xr IrՅȰ @} Ԏ x86+#x80Ќ&eŽx`-a#ͤ (4)h0v-qL;vfeu80 d/x'gZILKXd 3.#մ]l$z= Fnw9>9% €0 UXH4nc@^׉k(QN|ɨ+<`G@([ >֭>ǦJ駟b/}YJq8Kד&&:e;T;a@a@a@HXwmj\cۡ1ٳ'Gi+'Lϩa?K,2ǂpw}76[ `?c oGlٲ! X4K ttXx.$ga@a@a xCK05kk0W13k,W >豀4lp w}"/8ޫa-: a&G "+cQJ$腙n^42c&q),sb}"Cߴpz/˺?6=t8Ʊ2P*=O4̰apt=k4װ1448FDM|R7HgWG ^GAqP( >yJ0 €0 w}'Oj"cUl9m.}Dyծ]=z4Ƣ޽{ϝ;G1WܣjQ|ԨQm۶]x1EG-P/ix^Rcu[o%eb)w2o7r[O6$acWaB7A_`w|bP!v,ro$0ZPN@, UX΃5l޶4cGqqa3 d"r€0 €0j _س NTD4z8?5wMƒxĞwqzsX08 \], 53@6pvqIU3a_e4e*=[&8e( 5۸.l'0VɳjB6zL*rmt_w@8%+Ybh{閉db89$B +΁]<͸]0pqD8vpb# €0 39gxsxju7@vl6E Rx6EK1c^JߦMT۷1ˍjts{v,v8G:`'61ϞqXǹ;0%߁H^f4t&234h&Vzeo۸ Re7){P3h#ķZa{{o& W]wUiPKc.;VgX3^x!FiT-ZǶ]xȑ#11LPosPH^fcequK9À ԬZ~x8O4j €0 NyV%ޭC(Z_4~%7mo?֭[sW4 e'FmY:sT9GYyHe(pYe?(^€0 €0 Y@+p 2{.99L3((1=S =}Y1IRa@a H_]uQv2Nni߻ݶ0nt3l׎@ph;w%\b%+dW,u3utp <=[%8̇L$tؿ׶YdF떉l8€0 82ގal;tUW5j7+̞xӧcelԣG/X4+СC1Xiv ?*#x!-ܒi'(zƌx\kBN`kMv%[8+}e6ݪ~([Xd<Ϻe dzyf^J;ʈ韀'9.byB%2׻$_)| >vOTƘ/ @* 7nڴi<O| )U[\\`ʕ+Gqm ڔ*):US$*9s7  NR{ոhuܓ1*cHto|&\lwrgw"RB\0= - €0:k֬Yx1^0֢E 6uD܎;ffn)f+ 8E0?_ubJ^Xs މ'v)klfQIO^#Ȁlӗʄ$L` /siK!O#D@8D9ջo4ܳ~rʈdT1n; +e"wx/_;C ^MODPԿ1w߽xk /PZZK<3PX(\X{lh0ͥ?bb5L'θ<\e\€0P [5v͚5<@}=U\*rrѣG_s5H(WmVFQԷo_,Ǻ/2UwWno;sg\veK.ғ@5`IGq-đ:2~h K[Zrr{$$Pb €0 AP̛͞=[ 6ͽBtҤIXK1PtM?m\bxSB%Q˖-N: |G<]XX]>SA?'s DV/!,.€0os-!a îx}W6¢.]d`S?~1c=34H&;vgPeSNpB39砹j޽4i޵kנ l>l1 O৓VZer)VYV-*s%l'H1*䧓.9=lTA`l  R(Y746n)+ ] u>^]dՄ^73lжUz辎22€0 €0 @re 9ρnr mqCNs\IqFqhJrJ1L'5ɡl3T-A)/#;X €0 €0 @l;2evlq;[iy,NP|l*_4崆Q\a 3(++Ûa dn%a@2yKTPԗ #{A^-!5# x7Jɥ$҈߅m:t^|6}6DaNb<ֆPC:pWq3yep>VѰA04 H_TR)dFm6:uFnmڱHPmD B\Q\o \CO3ʳg.RJd,za@a@`fx6[8evқ*9372D8F2E@I͒$ 61{UMR&$uǑ^a@jPj>sh4C[Cڝ$Cm(nۭd\8Ke3 !p -n€0 @F1PK:a2; kg+A+h;2a@a@a@<hNN%bw?MmBC4/(ULyg'5>xHx63 mׄrN=22a@a@Hu1 +%k Gi2*jG?KX~y>|<:گƣshɡ,E_?E mr,`~>>~5hog`˳N\ݮo4oZ/\u˞;W]~r_iԮu> Zw=u:މ#?iمԮ.z,:o+QMQnySs67|zT!o~Q}jBt4+Ar>SqDU#Vj|}D-DB4H4s!H+`<>~ڵc/&-Ӹк)|$}2]1ݱ귣?M']踻Sigsffa>|Ż;2[.a@a@<P5v͚5 ߮}ּ+]SWI)уJOYBӊ쑢tԼ9UFJs?I ЅSn#=z?ZE݂j=r_osbVQXZ^~~ K%-*ཀྵE-OwKsVߨA j?3( 󩸀xR,܃ZԦjtg/W̅^:EmjĨO<]t͛UVtMe~,OCK#H4%?qDq]>%:NS?֯o^Q 5@fVxF؇N79fDBmxO@!e\€0 €0 x3pqǍ;Oho޼f]g-mY)ނ;kFիcjӚ2C*~gGz PKG0VeSm9'}(l#Ln\Fh8pڵ]tƍo=X12Nl5AVU`ֵ7knE];vUtRQiRڼf{կ~y=G*fi]SQW5vVu".M IDAT΋}z6 춈ۇJ=i]c!b3mhySXV;Pi ĩUh5[^7.0lO?aPbI~q&DX#귇VVi,^u*[;a#)kAűz-A XUڣ }3N OVzON/7uhsi2s_AO]/@Ww˿> fhׂch_ћHώ(=$0 D (fF tIxSy"$e ړF?W^8q]w݅52/CZ҆OyMcKīMQHT>)8ԡv3aX~% h/:5jӟU浛V^R+J2–h#p?/א:EԲ}t4{u׺:r*hN1,OkG]ФtLjӸq=r^RluV6 zlμ.C9KCM+4oD%jxDo."6h"WNmjٔ>j[b`݁^{>Vcس-pxL^KZXq--MͺƐP0;z>=J:b{>1G$ nGw.OHeJ.neb0D< VTfJϲm.ķ2S%]rL@ 6N$x+%%HeNM{]7rS[Wr97zJ A薆'"xRJ1g!4{qa6cM4Tt%i ma Ma@ldo,+pO}\UNiٳ1_}饗27tQGucpp~ 4{۴i`86<ݤI5G֪U+Qo/\P3gWfWGؑdë́qHI51Fh*c3|ݛ U2D7Q,u.CA)kXo8GWȪWebX @2Th!TC + !lM.2zzWi KO*Q9s@XY%g)9hV%€0 0 pLرcU/汱8/F⨥Qcqx"Bx/[8v?(֫W5o޼KJ{SY\fgHΑ~7 6ʿr"n|A9r(1YH3s<.0TFFLfeĪTLG2+5 W h-cj\%Q0 @V0_-М+dlׯ_N:xGC=*z?qĂ h@c+mhL݅# 6fC贽"רF,aV&Pa ;@fV1q CVM]ɖnukl2U{qoe Q1;=R:QV|+PlF ˃5LR6lF0r]J^Tھ)j<( [Tz&('f"4 `PD)y%J5S!Yqo^0@ASi(X7 Aa ((0,Y4,wS =XxګsݰZimw6P7aa,&L,B"1Ln:̳S..6!TTƞJ'џXRGe/.vׅD)49ٲ 쩉i F,ņ"0 @2R*x;2d(!( )8@04[OgN*`ӦڋjIhܩVS{l{k)M*t+tc#㐯"Z0ۃvFZC8ۂO3b]"Z0}磺AQv.{)s|w.cwk"1{;D+dfVюR2̪RODF €0 @<]Rfl, €0 €0 @2P^N=b4JnK[SF# €0 €0 €']J&JƓX €0 €0 €0 Q sRf) €0 €0 €0`3+1쀄\0 €0 €0 Y@l+{d1',ev 0 €0 €0 @zhϤ9a@a@a@h|KY 2O$ €0 €0 @3PPf 4Y4H3a@a@a gyxp#'҄̎Na@a@a 32N)S&€0 €0 €0 dyOCLف)a@a@a@h<_>RfLa@a@a@>½K?RfL<a@a@a@: (N!g3Q',evԌ 0 €0 €0 @^4hϥs=̂5IHkert/Qz|Nr!$Q" €0 Axi6=9\Bk6Q۽~XAMk^tr])J_4o\i{V>X4sيOsX"K?~׋f.߸f_S{oѤۿXŲ-QۺydS_Z͝v)e*V=pقlܴ~jGK;/ w3iem5e]v jm=J%њGiET"D/"Es4xf~EkRٯ1,wizjޔ8u긽7iK.z`š?l]7Ow[8EݺvCKk{EIE Uv!CM9󣇿Yx}5kT/7|ͺe0ԪQOeVhRKN]bƓVSV:# ev@\1a@a`'g^uԕmtxzґ.c^oH5yӺ-/7qC^فlw ԨWtР=>d 6vu;ߠm)"ԮWp וZ}vo:lPZ h\Nw]IzQ%Bhۯ$v#^U(#^]tN>h6a^4?TޛTXVAǔ8吁9>xa3\ uy0Ԭsˌ'~GذcǶdB~ڃN-_ay71ȚǯFui狻wb4 2;x(yovpΜaql;x`*wWGeS4&y9Q4D>*`}f" €0 :w쨥㩯GXԿ22}ؼܩOXT䎽A7g/zOwf ;N:cgK*36yՊ4ߦB=SLpR:u͙EQ.U4p}P wG;?b~@hA^nh{oާMk\au߭hܩjl([jܤ.g( S?í{yRӏE e.>`5-~|n{VkG:r!~K€0 €0 T!_,mG4sqE#]64kFvL~_ӫʇwoթ#NO WDZcd2ZaNx>vt?:?IzН l_zrNcK欜/eeswUH/+_PNa>IC:fg8T{*LYPȿ€c5 €0-7Q퐐Wm%iO4k4lm_vX}w/Գyj+ѝӣoQ~~l7E㞉a0 K)Q%ք˖*G/'S]?xr?oC"Ƶu{Xw½7ew_jy|{?+l[ +C7nJw/2{ J/ìbcQ4Whl` ʘA DMO8gX6@6zUbMlXA> q( ݍahLWA€0 @i24w`ߵ{'#m;lஓ.m{Rn<ǎ hz;GVVS s=w? y8:4+!ߠݺ>aVNEbfw֨!7޳^v,*ε57Ų78zEݯ"b^0?Qn6[(ߺR1 cAPJL^u:a0awqb Sӈ=C4a80S]I} RQ8hxK0 @2PVN>86n^HguЋi@^KSD6޺iMl܊հh-./+߲i+j̵pظv{A큈[6ocٴ U+-nѾƖe[g-+͛bxuquz+B5ͻ2\~TsXH+u1 Tv͏+`6mM7T:} =<{}3ƾJw9p@)Yܴ,Ob$W߶S٥=եUd@X4+?8]unŗ߫Ahy|U/.[~_ul]vݪ{Ơ̮}Y !c4Y4hKKSQMw y*7x&f h<2Eųp%a@<΍ 35={p]AY-'xTgSbhyy7ov=1y IDATDža=~?4gpnU^yyWx 0;\h|S(9I,2/⅛nFu^iǾX\11,w~Gj^S6lEqh6:_}FGT@mbx׵S*ƛ=3y_%>qB5ܺ~oN]z>s?ixꑷtߴv~Ŧuaug^[v*܂]ُXrUX(WXXO`W-~^ [<#˴YfgX%ըpvTdQ/ @F0pvg>oI_i#8#>6=eKm0ny1Q5<>qlyل1|OgӉ1ˈ쁄};񞭎@# jZO]qNz|l}Zgix[n jlV#Y^fG2=2*/ 6TMH5u]>p#9薩錕z €0 €0 fD"^;yYu\nXg(ULlpJazنQfv{ €0 €0 g Vf3O2{6b%32Qet*Ӌ(pe[Gd|x*F6<"J΀ lv͠4@ (4oΜX%0 €0 €0PY `xEù5ґgr'SɄctp> +J3(a@a@(#|[젔4>,29z%Bqmfh LQ~بlF#a4ۻh- N \t%E΀p!0 €0 D@l wM $\l\=j{m+) j .MOptWJ' ؀&Q63 ld[cF)m3h lĶ30 €0 €0=x0;65*fd:A/0Gum|l2`&]qac |= wՍ]lt{O-3z =;#m4Fn lfbKL*:60 €0 @1!^}B̙3lҤI/uԁ~Μ9?PZZ:`OQtAʞ4iҝwމ)ntOdɒ (lN^f{E\*xe^JdbfW&PGvS/OZ腞5lJ 64|p8=66˶dODQlK^YR@%)€0 ԩt<8(c\8䷂;T:{ʯIg8#ݗ82776RluTGl;:pTl?G=xokxQGA?o޼mrڵC… 7mڄ5 k֬9wܝfRXD7 Q񴁋ҫo?Kl)(4O@J{\pOuJϽ D{4V"=nqp 1pO#'aN;Y€0 €0 d Vfo^;ʕ+p5JѣGC3طz+~䆲qJ5jwII 4h[h]j!l1<ueWYC谺?.Wv,K[%ݝ%F50 G2 اK7l{FpapB t)%!X}b)(݀=i>l)P*QMX6* {qa@a@ ߙGش; Z_veXYhEwҥgϞ0֭4裏PfCo?khT.6j!X7#S *6{RN chj\6IGBM@FP:BQYEbƹ$0  Xf";IhsVrF&a@a O{Ǫ e}dzQZ5 _uX O^!oѢEQQٳvEaM6lB2[C} Qt1!s\`HcPأKrtG>ZY 9'ie69zh olBrqdPpP*?zUTuDQ,a@a@VcB;q=jժ|r,fc+>}/OcK_|œN:i:^܅؇O>رnC߿aÆASX{2}o{ .6̗c\I]lRWyI+A'1A]p\l=ТN:#3nJ>Qᤒ €0 €0`3QOtcvްa{cqȑ#{%^.]Z~JMScq8 ᄃz >3Q  z8㌭[8q1)CM G"]8t f}\AGJ,W׸2.G6N}TXٓäc7r{,qs^a@a@Rb+C,횶ÄvJ9lR%6I .8.6u #@P]. .6-#7)TRtRp!'ȌKΉjjՅ8QbKG3_4€0 €0 @E!*eȊY*{, €0 9@fo'BJ\JyN:wÆmj(PT.6.\pg(ODg\F 뙒 kq TPtTxF2 7p".$U-PL*89^̶1 TSa@@l6;28 =ߨy(e6 4L ųb JgvgP|5B8a`#6JlAo` aVhd($ոmd>YBN4N$, €0 xuv#xa4B'/̸Tz|z]Yc6F,Sf4^tq.G638ΰn){"%zBgGCpı͔=ȍ 7,Q,60cIe#\R{@GafvdDdu23PcIa'3 ^6Aa@e 'i ko7UHx:ur&PФplMR^04_? 9 O{gK/ÝxO|]b €0=X1#ƃى0խ[^8~L\lޤ8[0q"'ʊlG^]Hfw/vI>@.8.6Qphk*) u! nݺʕ+Gqm5*,g04D'/=o%ͳ,8[%ހa3Yc+Tq#Aϗ'- bBI%gL X>8 `S|Ї e$XA= ]o ƶ"JR!@n0p駫ԩS_~(sc\2 a@ymخiv^c#O^fY{A}Yi÷}ZI!.6K=.>4$z !h,]lB.W6Bظr12i3h#zIς(3ft10Dߎ󩠘FQF@K_i61}C{:t}6̧jԥ>#.]G?ͤҐ)A7T\M΢Fuc>ߤo~rޚ>z mMO/ /KmF]XԨLe囶ToݸU"fhu]4>uT:u W|~͝N'v#y/yjdvzcyK{Fߦ_.M珟z}:կQmeev)ۼuS 0E7ӦWLuPQT-~t-{~;`Zjq/5b&tS444jhV2L+n h@:눰14IQQX2ozZ:ԍhGDOCe,Dͷ\|l! 'A+;åMjO"$e гe"6}Ond=apB *%u%0fXc6a|o0eD[)W/ueo_\e`ڴi_,*cqZ^>>ΛYOz|:Mju0~X["0퀈R.z)>H7#Qtm4典˶#4%E4r-]Eg=D)77ҁ{/oxN/)cڼtwg囁wvrJGi#կy:۳c^4v]^>~dn7L{ՍT-ޥ향+~>떅oj5EX̓i)ZXRt3wt5Szar L/Y3/;N/^m׿"6Z.y)?~HWiG uzE i_RLcSVt%u 3|hBnjߜ^N'N^=1(K#K}ZΚDߢ)c?̡ Jk|oS2-#|eA=>l6&KsKjc'uɖi M ߆>p\qIqJG(8*K_"4Ёb]l8^4DL܈kdhFWڢaQvNPcs=~{۶^SaqLVc٦Y^跭ߨɱ^ǏҌ(_v}:"bOǐ!/K~lkh+"گ][Vwnvɱ_M7[֭Y 9Eq;dS~qK>#F-++O{CQz ]|mȶ \Tww=g;N]o譚tjY&Wi[q&MxL0&J_nʸЃ*θ\\y rOo$nJDnGxV  L>}ɏ=XօSiVLEMRєt@ۢi8x0۴ag?jᆱ{qŸ̾}7 |oBZ]wl`pu;S=+k}/N[jܷ/Cd{Zsg݄g=r8wJw4>ZiGI2 ңH>O{moĄmۯZ a^eMa/p~ShT&,?y.nOF͠A4;&e*ޤP,  M0䞝(tODr _0uuYvGՙe<&9ٲ MQJ`&:=ӗz"臇Hb5af +P(JsQ@GͰJX:~ ?/E >CfpPg",4H-4v{KV"f砲MgiB #"F sSᡦLHN6E T?E"%ͅQ<>M03j;.2dt.; K*Ub!!Sa`^`ⴓJ 9aL`Lr`\Jzrufg{cd$Qdʰ_|[275vn{(͑6-(ڥ2.9SMMt. IDAT0cXgt| MNf63FVjm`BJ\h͔uo ^rnjǞV0a:}ovlu;w/E?Z6{gǛ?pƓi"e{{E xΌv8Ǐ!9oHs =πQ51s<fӤg, kO>h};G `*<fIK$%㧆vM_ln;4]śK|H)giʩ F_aPzKQ#w;@ M0޲낛Hɥ}Leȃ>z)KK` ƯMr`DWybWȮ軈8LKƄulhPճГ7 2FмhFd?SC!=90]}xoG.gXIJqGFLi;Eޯ+ 2l3_j=1Y3m!Ok9b-UHO~f1,lޥFaEJ*ĺwhs:#¾Oؾ+/k^u? zŘ?}쁵:o.7GFm^vç?V#lH׎R̾cTIG.D쇡QN{u|޶kg1KC/}!t.z΃?e 1vx'1F)4YmӾ󊥡%ޟ>ɔӲR0 .`XҠ<- ^q / lFݧO?h$@B2$pb`vٛzs49&OQwMuu$&[&3n_C}7 ER vK q|{_ WAE`ν`z%m;% $']NW>ݹ7c?| ~:Sa3wR ${/VBqk:YHI.~滟zsNpԵ>Bk:tuo@JHNH<-{?_l̪T/=[\u5`?&@ t|c፰1b[e9(E\¹T@02|*f?x_Ζ|%nNxlʆIp~xm^1whU¬YҜν⿬w"ȥ=r5eÓX=#t<F]m5lhģ`XV9v1ϩY(PЖ倻+X1[z~xp_gTw*MJ-C>5wˮ(7chzds"c1pbwf|כXwE |QmjoFCGlb>?#sN-^3B aIy|a tf03\]F1$&;~0.%,I=&<&V, 'ơTѥNf+{(1bB_yHw槂 r3UR yt+ .lfnw(ʉ~(Ni!~^$㐂LxL0D3(N)H,:GH0.;?*^˶n1x=DwZb`错=z͞| HK^~FOҩУ.b&`"& !;s'ZW`=JFԑT&_,2Y!Ka$`qe/yol4v;Ř`XV8WNΛKLtk޹WfQ%|91L44D)7IK\ʗDM3>$ň@be5b=|u }$J-+e`Icc.aӯa C?KHX=nr$ׄ{۾ /9'9^gpN BW)܎_ʽbe$2 FvK=4R!gi4(MW#XEaWz.W˖mGq*MbΈ`"&b+ @R 4|u۝ߛ0-"mJ%[) +TM2!9ds%Pi t6FY64PH︈ːm$RzOHsCVZb mbהJB0.Ji0y޹fWzv4e3Ԯ5\C3:lΩ ʦtv!i#` FN6+ +*eS]h5DTJS-ģsRW2b\Si *'v٥跋13DUv_ SiZbJ|lRj_)y>'"Ho3#,$tRgÒ9ZZ:)es)),ΉLz-iҴ&` PJSWlDMs`XV<*{y*1\{}hiyx}f_a)ދf}r=E{ٗ; A fwz֬YiiQ=B2]|@uRX=#3赍R2:.`bO=slq+ + Pw͛Kx v ˦ |`J>*I>Rf`XV`^a׋Z!Crx9UxJaRĮx_2 U0z}іc=(PHp+ + xT p{Vxۥb9Iy&4C=r dQK1ETIl1)?+ @q*o:^%<._\<` x+x+XV`X2@ha] |okJx!~SX˭r\+ + @P n1a„s۷/??e˖ 8ɓ'ڵ^z :D͛}+VTPw`0+̞=;'' Rvm9QMʞ2( bdBtXBU1ĠG)D! [0xXf^~V .K #zurOM.в`9l;OI"N C"CxCJ.@B! ҫ!~}F%kvIA h zh:W(FA(+ + aMGePB;KbŊy#M|zGСη^{VZH;v];v:thzz^o_~ի}݇YF8C%A£!!]+Z:eے2ЩY &0Lā@g=a4ٕA)M8%Ʋ$@KelSz=NKC%GC;8M0r૯DiuOD+ @)V'v.{Eh|e16U[n8FG{9s^lf8X}W̘1͜9+ĩxRRҍ7޸a2tF1l8K /\\h啋AL(0ʯH!SaaTᒿeʄ~tR/q*0&h XX#bE# bpG0 ~(()x( &W`W~`d6<TF.iIi ЏK @Υ4K!bP."QЏp!0 ܚrITgܲs"V`XV(^-?7?糳5jO"qs-֬Y͵k6l0%%EtgUm۶5ד7hCڵkG$ڈ<ͦᙜp4Q"l;GzvݣGy(DHGW~:FI#r2@C`gC Tα1K=D(3GXȜh`j*(MAN`ьxx R4(D:V#֣g= ҔUDS!Q!dXV`X@^b /<;/Ƚ(Y-++_>|qp]R% 8M/!Rmf,Ƴ4LE,ǒX\9ehي#`#`KT,*oI5] xk~ + @DqO?Ç#TrwWI20ĵk6:q5rTl>tqhbץP\S$Dv5+0WyRCʲcԆlVҎÒVO<*xԬ+ +Vp;0vuXƛ5kzjݩS'l>tPjj*6_n]qk7ުo5i8^~}͉2钡|zѝJTB#r- ^ǠGq:7Qs,iB38Gސqh]Y+>tbRAS;d׻p + S# p$!vK(2gߏ⻻O>[t)I{Ϟ={ԩ3qD\޺u+>]W\pԩS\|8>Ym۶ |F^8iOݔBɑüfyP–C`x\2́ٹƂ0(`'J]j\%JFaM/!,vQr=+S ~`$CCNG c둳1eX|0zL0QAcn8||Q> 0#4}tmpxCŦP_Xcn>zOv/BVVk St͑7e)L-ǁ]A?zR.{u[7|R༽.{/B$P%Tm9$%5CQj_fn~}ݛ;&O4Ɣ/y߮\TYnjmѪKռ#7]ՔMYG2V7֋& Ŏ퓩{t:V]*W 6:6eȳO8k#Lp}ov04>z{_b|5.JOB h?38mya_wn|͸֍~GYfl*F܌mw.*fG(Mʮ batʘhl2Ld󆱌fhr!#6MC9ʲ`%c^<2R`>֣0xҳzL0:7Kxc(V^:iti9׍|cwVַ+\ۏS[;o/;NWh}Cp90{$r1<S}?< |}'m~p-V r`˷p0;N; _ 􇿾tuC!1 9HXq1ti0`CX/ʗUyC;FpЖ[>̇׮۩Z[CjZrnΑG4aP!?s?wɠj8F^{|'ll<LH𴚝2B*ivIȒ6 㧸缬+ 7A/ʮԺaOgg!;6ۿj`,~ 9YP5o}FA>h>yE]镠USU{[ 8i]sC~H̀68Rޚe6EQN]#3f#o}wз9Y봮6ݭעk"4=N( ^ IDAT;e6eYDZ/Be} k!dBa>^5{uJg|2D-Yg.3z{[ Zu H#?鈄|qʿW?U>j췲eVi16@g:o%8_$ǯt.x.i ]`hXlUhB#qWrKÑ!'ANRXs,΅yoIxJ1Ҹ\p e!0Q0ጤ^%ʲo.K6zXd+W𢋢,{pUqݹw{#{$VHY+>'^ giaBb9Z+w@`Gn?R"k]J=w' P!;to`EpP1b_*W{~j};,~#w톝O/ IW6={gӡ/+[ѯz]foLMwH#;a;p4h;+t{PTO:W: =?($_FRuF;tuΒ6-I9WըpS6.݅{ƕqҪ^)b J$tQ@SFPjݡzO15O}qx \0Fʍ[[%^o_wgVZ#i:5_TyS85Mg\vAY c*<Svjײ0&_<&SCPG0*=T|8IĀ輠lL-؅ՙw7 *8/"? A#NE:>Yw+{;PTIvʶ,mC@zuיyu?w9+y'xW6bom/k޶Iݛ{/C?>i|kk_{3&~V6jЏ 4/?Im?X8%|y-!;+=[W|YӛO߹1t~Ino>iϾ,vn=m[[Km~||_{-&Ʒ[p BrRKeGsfrMGG%8:rL>%DIii0gRBsOv͙]Z1=HH ]nNR5Nh?*4(?m9N43H|чcRS2 . :/9%Y9=1ݯn|9rRv}*f\Kzך;FHǎgD= Wg8Zg3%:lܽeg~q_ЁԴz1#:& _%3Ϲ1jo9wz3}ȳsKk#>))xheiּ28Ͷ"ot( ?FQl;D.Ԡ7\rN#SA*09#ƒ F',IYl9bt*5K¦(<Ů<b ~a-U "u!JEt uN`g Eʖ@"wˣLr`D<4LHTY=h[n[vE# Y)P-Z H=6[BW;lR!kG=wRRgh;,Y+S]Q:!fʐ7C}{#4k:frK 漹T.W~o ,ޒ+TJSp:-avUYuABũ4-k6X*MKu5e`(tmzf2st* v[R.[]e"KCxM2z8|("G$T0H,GKuE<=GA"}fn-H+O)Β /9'9_ό;Leܿ7[ճfJK_V=rzߢ!n9QbbbL*fkuAʓieLcBWr1`XV`ʼyazxնeymL[T1^ .FܦrHwD*KۂK>yue:ܥzXZ9EqcUTjU&b\2W0xXasyLr`_+U]W0`^AaB%߫nθ<EFrф:"ʀְ2JCv>6$J/{F6<ηNn+7F`7 ?i<*ٖUXb!cn'W狖{عP(upYLH8Ō^v5Ў3~;rÁ؅~ٖi-NeetfXV(' 3 n`Xfȫb|FOI^.˜~bL)R@vS/ƒ䱋e?)@2G7 T`&z.=0)1O/^َ>/3* NSC f$fљuɲ_ijf=88/}`XVxlV, MWZOpOtk0Jxȕ:K{yFA uUn Kaz.DODh7~BUMJxb Nb`XV*m5ײ=*v9jWP `Ka"QjЛr.Wґ FЩ Vc`X,k7'JjsB=8$"F0PA*TJ/PIq3b^=%hh+B=`Jw.d [ (`X0@)PQ 2^ ^#F'&zCI${ C,3䱌e'+ @T 4/=-^.FƲDNNJi: VG`ݩ{Sq*M˚M0*JS/ƒěNL1 i1VG*N)N&۔Tvʶ%1Ĵ,~=hȶہP,r0E9uY&t1?T&0Pc(\Q2Xx02ȠJ!g(C~Ȝl+ Btϫ_>!?^`.*m^TxDj~_F&a+"2&N~܄ #< FE Ѱ#G\sF"QBЏp!HdpF΢D%v79Jeb-EJ+N粕G -cK~ xDRd{sG*DKxHm"SuȥBa •6)Ra4 'EӁ39+ @IVpuN,#=8NXN۾}{ӧϫY=+e@+k'q73J}6^ 3t3ʋd>Kqo6T f͊⿬׳ήaz93#WW,3RpPbQ](<]R&, o]R&dg}l\ؘLq~8XVElW iCb1itbBpҥM"DR4P 6aIyycd;4F-WM +@ >k*| =.(ƿ5=Kty^~#al>|;ٝ4g4 80?ԁvJ mma?Hf_eM3BuͬLfڹ+br\yێb?&^9?WOiG+WE&ӳ.9y36qs3e/v 4sٳ@K hsLd1'@sk))MyG8Sv(`ڮ!Bߣ&?ɃHs$22QzᱫD뽔׎8]+ qP஻‹E.5j޽'lYo߾ /вו; _m݁'x+\VoW;CfQ3x! ⭃%H}~pdф{u\BJ⚉_uWܼ 9N pK9 B!H7"¿.~W7.19) ~k YO~޳Ў^ЬSUc5NWqSo9_ .4ɚ{a WS /XO nj7R=mbN#[ap;(KRfH@*$?5kw;NAaZr޼6%JW4m ѐţ:5E}ڦn|αYiniBy5N ]HLb1:7zXszKG4ԇ‰С8`aXV??޴iS=z4^޹s]tRogϞo^nVq壭">i|_,Q% \to1FޘjDf |sհeáOhhѹe T ɍU!*DHmFi+-JkTS4}qmܼD 2j΄4(*ږ͹mZBnn >5,W^8qF/v;6-[ I=wO7pyߡM:*PW7\رzW4_Nh6J2$gع`Uekz4> CF'-5&?ngg7|Klan; wQ~mR7U.&E=@Xξ0+%)TTg5hC{I1eB WN$cVή&+ @q)khҤիUnNUxSN~\Ǿˆ ֱcGYrX;"V{se@46^UDC&7?+A-rϱ>;emKg֛ m]<CS,0]mfΖCܷt𔮳}hE߅=o(n@VI 4o1y}.P/Pa}r{ *2߷3wno[˾g-L>&x %,|K^niWoa|! o.ݵse*uw=8 @gh[D~Sص]\$csC 5,X Mġ6roI[lIIIԩz2|0f̘kÛo޼߉bŊ *ݻ _y|DNN?dȐڵB)viy3)2Li.ũ4 1QrjsG*HQ o#!ZMP =uPQ;sE(vC~!u/}OO_dܩ/yr݋sֽiy Bra/ 5q!>aW?aLLkN G vef_߿C=THwƍӦMw6h`̙/W~7~g̘Q~ Km7)zɌ!#%=|z1HB# 72ۉ&!Pĩb;Cn( +gnf<ē<3o۶ U"paʕ&o/<vў=[}4j˝cqV2 0㣹sѺC}q7zӗdsuI^F>9vQ } P1O=)1k^ǡ{zt"s[fϐУѶ~8-`#~cyW_Չy$Zݻ?ѾKz? -- 6,Œu]H뻸 {8_ׯw8k,Z_]>Ѡ+6ԽsBJܺ~ ;m-G'+c*ؾz4p<}[2rv- s3™ꪫDwzqu'8p̋e˖j6:q+x7.RZBGga58 YV`KN;m8[޹s' mu'AcHi)-!? BuC![~ y7?!oE@E8{ k݂ 4hW<WB"?{Nם&qu@&iVYG=I n񖞳ȁϮ3(_:mj>)U,Di<]uc(tsm-Pn-Юs[ 3JbtuȁBF} ] ?b)bѩ\4.#~V>-ױUvk6lؐjѢκdV?­7ţ~N*+xSG nz.=ɕ<53=J|ݿwAy_R!-Xr%tmj[8fT#bj0)SGJ)8ͮXGXTS5U)mHH އMbSHJfJZdW j3--ev`޼)?Zq/1^S23uC ='uun%MMzEUJ:?7CR"8)6;WalWr8z>^|^ h 0+UDΊCϽC'.}!w}Bj#jɄMHHye9v™`bMjFNK|jey nq׿S,+g+ @;wn=>=zg;>۶G'F?%>kcmyUpڎUx={ _ד4&'/>oކ/$^2}|t3>IjvE}Ċ)m܀{ z{k1M7h|'V}IIpSO=#͛7G7Sp -MKKõkj N 'd6"Op"Oh ^QVxp\~a(BY- M^*zW.U8ei6>'Zi9`X_k\b@޺g#p`cVO?_|o|To|"q_3Ϛ5kCӰ Pn]j#IХ8^~˹Jy'15  UY@38>}nj#o\f%Pڍ"9 Ư O0&"ħˤH_0ѓ0L&d3їe' c`XV > u]" Yx8 + %C/P6y>?fÎq#f'+ @ITdC͚5 _ӊfZ/rM+ Lo bV]"iv:y0+ +`i/eEl|kk7D`X@~h=\g^V4O͵b$+ + b5jtחp+ D@$mivNyD+ +'eHdW-HCY|37|pUq8z8gp'qΘz\oq`8Toޛ/Կ'OS} + + @|z<>)Yx]`XV`XV`X6;I_X/汲+ U۷+C= + 9o{^S!ivD+ +P \wuJ7p&+ e[Q 0f̱11z\?6/~r&V`XV(FmְaC7{޼y/Rzz^[pjV`C\v4ϳ,_40>24e-3ȶ%';YV`XV*#G_/?cp*呱+`^ai/2xiѦw1)YV`XVD+ЪUѣGرSNX(^1V+XV[4j /}|,{bV`XV0RwVVVFF 7܀K.=S" @Q']l߿ZetZKy)ۋ + /_~˽^q@AHĽe/7T) T>t8ή.c.bXV`Xү /@s?+ ^Bm/wB%9Snڭ]L#YV`XV & 4mtݺu7 iۏ!$O'_rsߛvnKh8N?ՆÝ9un k\),sݲ՗ft=`@ƢmhozW%"L^sawwGl:c*$f2+_#C{_ /L_0{3E㑕~O`4.,)fXV`Xr@>}dff={$;&F 2Δw+yphАxZo\rU$=<-*<8_fxTܩ?" Ṽ#z0޵yηs_Fڼ[;Tm];7 l_ٰ0B;V!.um8y{F:w4{ yHɀ}#\jCfʯ_Q!#m߆?rRjz@ עk"4=N;e6G|~|~|R3E_8H /BƅP)N|~HʐPd [`f|\3GK=@=#|G ơ, 3MɅ* ?XC.7Un*آu)"Så+ @S y_~H_,͹ m jƹ^չ;(w7='4iҋ(4,X@D`,yQDEQDzW"! ޳nY"MgΝ{ι7}r{ůi{OEx/Z#8|\N&Y])*1x5Wrq a͖`_@LkQEH<{A^l~$Ow}|6x-_;7nޠMk1>hw.?:x:J5拹Rk>#"16ؽX78MG`lQ8 IDAT;vYYnYK01(~%e:ճ` O y:YaA@7-&cx$G_pe/!?E"##9JY]źtb9wNhFo;8\Ԭ߱nO\ϔc2HHH:tW_}5hPyߋsx av}D {3c7KR._ۖ|)B*j>#ljU>sі#rR\zʊҵ~^p5'QK`x,[f$ Au<`7p]mlEZh9+["; m|!V}_u1Uk y!lAIaOoYχ>iwS/4U] Tț`Z{&n$x(*!.(P3»rI(X +fU$3[0'=+&OYqm+aֵo{~`u3 ,?g&Sn_CX gbaKd%3-%j*^qlfVEvUvQ9.\%   M/ˎ\b]vo/?YTnwwU+YSEvݢ rpy;N`xņ1 Ww#f إIK!K iľiH$Vsj_zgpm$i|6]XnͦY}li-@9{jN6R$m 4B'IN.͔_uo E*ʟyQt0@$@$@$P ,[aÆӸLbMj͡,[eIk>Ɇt">^Ю4n>xm7WWtka:{n']憲͙ٶ,YF:}^ Kt ,Zj?^͋ngmDV DsysPŨ H=?ơRWx*C! x)O׭{ٔ~ӵO<}|_-핓ykuGɓHToNp|g֢}8 gUrNg'|8qJrxÓ8;!AEAuzvً~կ?AjbT>3gmveIzud&``gm8)'zgv~89%VF :;v L2eŊ[]ly8izt2jQ,c5½}ۼK3E=Ř#;nWQ=t'MSn./Ks~E n%&+ .LlJyq|V7YCاRvF@Dm# f2Z<˸5]]:)9>f[3Snm+<~o'_GdLɛl)oZ)u,RcPFNW43 O%B:e s-Ƅՙب}&<WB XkGqx8 <-|-c/.a=gc]RTĶf0Y%eN7[=3J @[^X/QUmue=B[7]kCnZ4n)DKr܂}͕ާ۱[kfZf--|uCrjlB}!ZX%4;^ DpC"z&J_ՔKΈHHHHHJ=i\>4͝Ɲx0f;"$@$@$@%@NN_mnvv.-OtjgD7[-1'  (IfΜY˱ -#`QS &J_٥rF$@$@$@$@$@$P[C֛۶oօ ]IHHHHHpͶ\o޼yɒ%Oظq҂ -[اO.]xqƌGz fk7s]fMffw /T /0/%Li l'/zԨQo7L2eڵ&M7oގ;dzWV|>L\ ᇭ[Κ5K*UᨳDf @hNk͚5ܹsŊMFGGɖذaK>|``Aĩ #=tvYtKIHHHHHHǖjamv-O>t萤߿?&&FZdLժU𰍯VZuKyjj˗k׮mk|rxK"zQ:qmvIK# J@WӸ"C ymK9rd5diii>>>Q{{{%{ ~9~     9I.Nv%]Z7UtR/W~YY{+k.f[/-%B7AHHH#paWMbPg(,sT\=A2^gd3b.xwAuFYX䬨r1IIbS/VDtE Or(ӯ~s!*se :-H]v"nv͚5evVV\:u,Ŵeɓ'e-k%$9     -Ы= IJ%;;;hT"E*!o۶ͶտgnԨQhhD%|}%]\|o0{%ڢjΜ9ٸvf[Ł ٸv~{Ou:}'?{Y-cfeʔȶHIwO.N{1`.y1bĺot]:#gA$@$@$@$@$@7"`f^ RZK]6%ϟnv鸏 ܈-슬1p+E"@7H؉HHHHHJ4l՛sZ=dt4J$@$@$@$@$@J@'멋c*4^*OKnuHHHHHJ@cҸl'QHHHHHJ[}NH r K7@J     pfmnH7[=3J @I# Y'[7N+iSK7O$@$@$@$@$@@tz[U۷~*v tw#      X٪Ťq'vEHHHHHH0j7N00nLHHHؿܹsz}ӦMNZjƉ v:YuIQHHH p^{mĉ[6LgΜ=Y N&Ag2EZ?wO9#   m̙3o߾c:PV-mR st,mPHHHJ'NH{zz=,6Dqxe lB&B:h\l˜bjEyr?{8E[sNc?o:Q7!-W f U$܁̹< ߶Ō,%Vv{wMYp,2!րK~GH}M*m۲uϬV3{NsR ȬZ=}GK\&lJ6>vɀ'v롗∷Og~"lgSg}ceD9Ǵ5'>}]wihMm}Wc_&e椄jP{\U:'<"wc4\NHHHy_~yT?ѕn>~o 9&:;X&:6=ZZofL퇗]=`AaÐ7\ T̼'oQfh_ f \{gܻ5~,90Y'[/(Ztj7z3CoC<3\ZuOĞN$0ٛ:Baͤu Tm)tx*ꎙLz'&fU?DHHHnV{UVtC9}Wxar؜_{+>7F(C[ʠhV +umhǖbpCDSR6?͔eb[(TEʩ&vGR_Tڵ)i=Zfrm1A]`&&7ODъ-E VRz(: M*Iъ-E&g-82N}m3c[ )fΧ5ƍ[N||\^I-:77Ig^Bߛd*W뚎n#> !M+I%ǖzD^>Oh?E~S9ǖzʇKuo2}!^Qu5Ni tF   OL2yJd^WÕ[s{[~%-O*1f):S PZ˝8i?s ͔qI?57N1JƸD6GOy#7fICn%LqcMш BRϚi[ bL<.Su|EaN:͔qI3'Cp!G jR,ii}r.1.?vf֭\9A4⮜J&Ŝ%Q|{{5e41'JiٗvHfHƸ_-=^lKf䏇`T4WدE;D8~8q@r_exMđ=%\ÝƯ{nH7xxHHH6&лw5kĘ~IH4iҤ<R`"~B6*ⓑ?2J6%5A :`qQ(twaӈ>M#ħGVqPDȚ(>HujN@p|&#A cqiEzt>z/8!q-Db`|.@H :AN(w!Q8!sBTҜyrQiy_Q9i)fͩ`6;TYag!@7 \?ܭ[1cs۶mz~Vrؽ]E dMx=R2RROo%.&Lހ[]YrmX[N2rN\D=ޱpju>$3F <=^V{X-6V&@g숶z: d:ȡEM{ܬ| C h6<>"BG~^u-оKgZzFޞ uC}69qΒg>5p|BA'e ;;a;U'\aV&{rBޠ۳5k䔀%|Uǝ#qx.dSqjQC{/ ;h=bs4}kcg$2"j.jN2Ƴäl sR@ ژ|%dgD];!?0Acyf'bȇ.ˎH{8-v W+#;ߵGv)[;.n?/Uks tk 2q{EMl)W,{ -UנtU^9' ӅP|o,ܜvS F$c.^}Zi6}vJOgL.(uC|kn.hDˆϴR6> -XK:?~꿠IIF@pDz*Y.&     doJufvP1ߕnv ܔ΢ t03i".$v!     N"llyW=Ztc     (LPQkFHg!F}r2EV     (MtȡՋ5Q.}3"    IDAT y9HA˷9s$@$@$@$@$@#yzYfVfgF      (qiv>vEHHHHHHP[EԚ(}f{ @A1z͛,Yr錌7lݻwFzÆ kٲŋg̘qQ//x@.fܹs׬Yyw *T(8{/#IHHHHHL@ W3j(Giii XhѪUė4iRLLt0LJժU/_gpB?uYf-[J*"b=f;> @)%8q\4ެYΝ;WXёQZn{)SÇ%=|p ASmG qss{/쨳Df @ض@sgzIxrrrDDJZj~.ժUKn6OMM|rڵmO.oID\] n"@$@$@$@$@$@7#  XTKbb믿,BLc5l8^zzzgI.w'     "nuC)./dKMWbvq[]eͶ4:^[=KznvI? ܜa7ӓ7t҄ Zjs;׬Y3:::++Se,Ŵeɓ'å]%µ%q$@$@$@$@$@$P$8qKwvvKLJETė=zlcK{FBCC _.6^z-X@Vh9sf 6]*?.7S      p"x4tO~~w0_zuJw-;ӧOZ=z~: 8PVn1BbzH.      4av 4q\xZ 4N%ms \)k)^:NfY |ѹ2P /" TB7Q!YD_Uf2#3 ('je W&h{r?s0 4-#h0 4bHL#*N[((nD_ȳhBfF-AD|aAA'l<3 }suVW9ob6x}l1,5}ݤvΕؗ?IYݒ^%w3 '#:.Ϣٔi{5N®.'-4q{KayTLA` @\'M)щ3ɒ7oo33.LՀo[O<ˁj|z%@7[-1'   xh~et?sQaDoO=_ZtvtV`.4ˉv'^ꇁIg~ j3&$Y&ص=_Dp/0*Fh`8Ub,ۇ&p%ǡRba4n:֮dgv߆'5 Z}re6BS|{DJm_    98% W-[JNf+uMK _Ґ@<ԇgƹ:yxxA=ϠLY٫ebx'^?ǶXইUڵ-99G 4_Sվ3sr.[j:-99 _./;A* bWc˩Gn#Z9t-ûPsfƠ;Â(Ю\mTO [N\;pP!<:woʗh>R&cK=:ۅCڒU,4^\c-vnmѓ fN-Ev2(tkjT[|Fn揥HFG;;87ulӤt+>!8^Ϡ{q41¥된r/0[c(3S;i|Sa}G$/bJN |nQ_N9^ޓ}uΚ^XטY}p;Gm,y{;qnY $Cg 6i$0-Kə[$q:b\ٟAK7%N曩7$l    m H@x+ZS闯O.``h0}FہN' >^QM ?Cl|9 OZfouq :澯{861VA1_ģgQ VtFJV뉟$p2bKJ]ӷӝӿrV.1v5z,On6G6 b1Bw"/cq\P@^Y kBw`W95 onIxR$b6-rnZ'h:d5{S`e[\;XN5kLkubM@GUu[>t}`Q4s%]IUo>ӅfW|nm)7[9qS44 @=Ϛ \܍ spj Ͽr-ۈybDkɛ0TLNpbƦya1'1ZE>Lao9 Trj;yR\egcOh#ȯ8֢=}/ʿjMPmY96RU+lc¼eGH87s)^%9虤wQY]cDd hԒ <mY )E,K'\b96o_'ȩ1-{ gkgZl ڃ[9qω!PHHHJ+!^S- c X= ~Wn- ,ޡҏvFՁx3+`#tp;Lp!>k>V[?伉k%Nк?xа Bʸvx-ˋGR|^ VfWj-j1٨nlxPz3L^ɒmp=yq܌q3 A~&<WbOmU25GllsLecu Pl_G٦?Ⱦc/HJй]S\l{'OKZw&\/RkObqҠqʢk7[K) Nȁ] m6 믤r76+[ڋ#YEa)N{ m3<cEizٙ,]vPq>AGVW?b4$JHHHHHT/yca4[%fN7[=3J @I#3AŬ^Dןnv黧 $ k-7=N7Et΄-$@$@$@$@$@$P0in)"M;$@$@$@$@$@$GaG\RM.w&     avgvBDŘJgW٥rV$@$@$@$@$@$p qόn5tB7H؉HHHHHJ6ջٲ9lNn HHHHHtjll(A$@$@$@$@$@% zVWoI0](C$@$@$@$@$@! }f7:$oD7fxHHH # vM^ 5>Y7xVc7SVWpnOf%z Wi_ey. Ca1.UL4\Ga7ۑƭ;tHHHHHd339VKLvEHHHHHHqey¤qtF     (i4^\wnvq      H([JvFctF     (i _mQ/RҸh?^3F     b+t3 ?K)7IN4N@ 0.[l^NfN@ 0B/gl.a\npfTI$@$@$@$@$@6ll&;qf;"$@$@$@$@$@$Pufi!     h6f;qf;"$@$@$@$@$@$PXr lIjIN@ mA >>>ڷo_NNNDDȑ#իw[̜$(K),nx= 3RSS,XbŊO4ɽ0H C,j VKIHHnϟСC2eĔe' @# DyCrFLW IHHn[޼ysNWZՠAuψQ# T4[S25 թ-؜F!hi-<=ƚnآw ]ɈM}‹Nƕ[_eX{)EܬZ,"L{'Ǧ/ JѦZ \ ilsp /4j9L8`5 X 4pB`$CkX`0噙g5ⓧq೼c?undRs3kYF^|BM)9ׇ (1||,>cDxv]4~c[\&cBl8 `F_ C  )bx 6EcdNoɈJkلuS{RPz@x;G⨋KVoVό$@$@$@t|߹sU{ϳ0&g& 5Ō;ShUk.Dg$FUݑ֤ר^L&ɊPrbBL)ͻ!;1_>ifٔalvqQC/~q%.֌ĬIQ0z s{am*'M7Z,|j ޠ{@*` Dȫ,D2@<Ǩ# C3[F7sHA2zi6[G=V&OvG"54[cr/rcEJ1u n"yL+Qx#[+ CNEw$TE|luѣW^m6lx󩧞]x^z7w\{{)hR!qj$@$@$@/VVmʕ֭{G}kH/(u״ORNJ?xBCyYq= Q.VNţ4 lJN0f.*xK5ѻ"μ. e'e&6Fjxz -MPLrJ^\n+ӷ6$Ya}%5,4-'~@ eM`J'3: *殿oh东cz?<!!_henCx)ר\9ZBY!M>p7`]45Q;Dз }qJ`kR.D~-"2qgyA _(+uXrXh[6C,P8 #<"K,Z:9r>#W^=hРW^yedzժU/_gI… (,}tK=HHH4 wĉ Y-+>|tvI8YWaDkH8t'\tvajUSVyќ&m\MEsM8gCvG/**_ctMJ8iWasP ʔ&=8|W"kLutMFdS"W8]Ӯ-s۷,fxUTرc&M~wשSgڵRMf>\zVPAnu%B2ٵ)Tu}BwۻcE/\ڛ~5; .Nƞhyl 1~عKT;:4Z]LߋXqx x;KLvI~8Y'ؠ73J,.bEsr@+|*,:~<8sLڵiHI ]vEg."  p^~(YL('yWXX ?.~Ix( &vȸw|$Pdu=v%19hp /-UUW.ԭH҇e|xr_H)I:/]gx{UjoY,~eyH7|7{&7R.a0QPu#K썮TĿxNR_-0@bؕthMnŝ O+~d@]¬A)otŌ]VD^h>xrz!а]=g-X{gNVbBTeIX&c\{\9Z,Җ8zbbQG Lg@ӗb xn!V67 B$i{$]=S.l IDATAꄩ8 o9]/OrGP_~W_t۶m_~䴴4PvLOOӿ=Kenv $23}tYgf͚/KԔ̋ߍ:cص Cڗ*['WAJEӑY ӻ ss&Ƿd\L\]d%vĐֲBҶH1𯵝f6ߵjh;bݗő0[ wGmXd1xh_nKx k]^1p|>ԩe706o1 mSkwxbwZ>~! C[Sj5l5}|$+(w}2jfhò~L<.ݸ R(t]֥Y6kˁo7@=@ xiJxՅxO!d(Q1VƒBˆ/RFd[~}%銱F#CHc>-ev}ȶ%(DlHݱk$^AZVVvGI[,wevJh\Yi<&0Z&DlȲiWP!pN1# Қ ž 6zXml؟HHHHHJщ{X @#l'DJGL7[cTG$@$@$@$@$@F9ߗ]LW{7f%$@$@$@$@$@$P Xߛ]g^C]iHHHHHl *QHHHHHJѩq=Ui٪QHHHHHJXs΢l؟HHHHHJ 88[9qf;"$@$@$@$xv^ݯf+Ёxq4'j*fuMvG1[8jfX.H;#-;#LFW,LH}@[Daovm4WNHHHHHH@٪pY;Vό$@$@$@$@$@$p{(R”IFr$@$@$@$@$@$p0;3B0-BHHHHHJ ӿn..ҴC$@$@$@$@$@('Th ڨGOsJ" } 檢#wŘO7Q@2JŁ/Gw lϸ˶͋ ]3y>Mߟ ^1

"); var item = new ClusterItem(element, cluster, this, this.options); element.data("item", item); this.element.append(element); } }; var ClusterItem = function (element, cluster, cluster_list, options) { this.element = $(element); this.cluster_list = cluster_list; this.base_url = options.base_url || utils.get_body_data("baseUrl"); this.notebook_path = options.notebook_path || utils.get_body_data("notebookPath"); this.update_state(cluster); this.style(); }; ClusterItem.prototype.style = function () { this.element.addClass("list_item").addClass("row"); }; ClusterItem.prototype.update_state = function (cluster) { this.cluster = cluster; if (cluster.controller && cluster.controller.state) { this.state_running(); } else { this.state_stopped(); } }; ClusterItem.prototype.state_stopped = function () { var that = this; var profile_col = $("
") .addClass("profile_col col-xs-2") .text(this.cluster.cluster.profile); var cluster_id_col = $("
") .addClass("cluster_id_col col-xs-2") .text(this.cluster.cluster.cluster_id); var status_col = $("
") .addClass("status_col col-xs-3") .text("stopped"); var engines_col = $("
").addClass("engine_col col-xs-3"); var input = $("") .attr("type", "number") .attr("min", 1) .attr("style", "width: 100%") .addClass("engine_num_input form-control"); engines_col.append(input); var start_button = $("', " ", "
", "
", '
', '
', '
profile
', '
cluster id
', '
status
', '
# of engines
', '
action
', "
", "
", "
", ].join("\n") ); function load() { if (!IPython.notebook_list) return; var base_url = IPython.notebook_list.base_url; // hide the deprecated clusters tab $("#tabs").find('[href="#clusters"]').hide(); $("head").append( $("") .attr("rel", "stylesheet") .attr("type", "text/css") .attr("href", base_url + "nbextensions/ipyparallel/clusterlist.css") ); $(".tab-content").append(cluster_html); $("#tabs").append( $("
  • ").append( $("") .attr("href", "#ipyclusters") .attr("data-toggle", "tab") .text("IPython Clusters") .click(function (e) { window.history.pushState(null, null, "#ipyclusters"); }) ) ); var cluster_list = new clusterlist.ClusterList("#cluster_list", { base_url: IPython.notebook_list.base_url, notebook_path: IPython.notebook_list.notebook_path, }); cluster_list.load_list(); if (document.location.hash === "#ipyclusters") { // focus clusters tab once it exists // since it won't have been created when the browser first handles the document hash $("[href='#ipyclusters']").tab("show"); } } return { load_ipython_extension: load, }; }); ipyparallel-7.1.0/ipyparallel/serialize/000077500000000000000000000000001412533144600203455ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/serialize/__init__.py000066400000000000000000000013071412533144600224570ustar00rootroot00000000000000from .canning import can from .canning import can_map from .canning import Reference from .canning import uncan from .canning import uncan_map from .canning import use_cloudpickle from .canning import use_dill from .canning import use_pickle from .serialize import deserialize_object from .serialize import pack_apply_message from .serialize import PrePickled from .serialize import serialize_object from .serialize import unpack_apply_message __all__ = ( 'Reference', 'PrePickled', 'can_map', 'uncan_map', 'can', 'uncan', 'use_dill', 'use_cloudpickle', 'use_pickle', 'serialize_object', 'deserialize_object', 'pack_apply_message', 'unpack_apply_message', ) ipyparallel-7.1.0/ipyparallel/serialize/canning.py000066400000000000000000000350411412533144600223370ustar00rootroot00000000000000# encoding: utf-8 """Pickle-related utilities..""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import copy import functools import sys from types import FunctionType from traitlets import import_item from traitlets.log import get_logger from . import codeutil # noqa This registers a hook when it's imported def _get_cell_type(a=None): """the type of a closure cell doesn't seem to be importable, so just create one """ def inner(): return a return type(inner.__closure__[0]) cell_type = _get_cell_type() # ------------------------------------------------------------------------------- # Functions # ------------------------------------------------------------------------------- def interactive(f): """decorator for making functions appear as interactively defined. This results in the function being linked to the user_ns as globals() instead of the module globals(). """ # build new FunctionType, so it can have the right globals # interactive functions never have closures, that's kind of the point if isinstance(f, FunctionType): mainmod = __import__('__main__') f = FunctionType( f.__code__, mainmod.__dict__, f.__name__, f.__defaults__, ) # associate with __main__ for uncanning f.__module__ = '__main__' return f def use_dill(): """use dill to expand serialization support adds support for object methods and closures to serialization. """ import dill from . import serialize serialize.pickle = dill # disable special function handling, let dill take care of it can_map.pop(FunctionType, None) def use_cloudpickle(): """use cloudpickle to expand serialization support adds support for object methods and closures to serialization. """ import cloudpickle from . import serialize serialize.pickle = cloudpickle # disable special function handling, let cloudpickle take care of it can_map.pop(FunctionType, None) def use_pickle(): """revert to using stdlib pickle Reverts custom serialization enabled by use_dill|cloudpickle. """ from . import serialize serialize.pickle = serialize._stdlib_pickle # restore special function handling can_map[FunctionType] = _original_can_map[FunctionType] # ------------------------------------------------------------------------------- # Classes # ------------------------------------------------------------------------------- class CannedObject: def __init__(self, obj, keys=[], hook=None): """can an object for safe pickling Parameters ---------- obj The object to be canned keys : list (optional) list of attribute names that will be explicitly canned / uncanned hook : callable (optional) An optional extra callable, which can do additional processing of the uncanned object. large data may be offloaded into the buffers list, used for zero-copy transfers. """ self.keys = keys self.obj = copy.copy(obj) self.hook = can(hook) for key in keys: setattr(self.obj, key, can(getattr(obj, key))) self.buffers = [] def get_object(self, g=None): if g is None: g = {} obj = self.obj for key in self.keys: setattr(obj, key, uncan(getattr(obj, key), g)) if self.hook: self.hook = uncan(self.hook, g) self.hook(obj, g) return self.obj class Reference(CannedObject): """object for wrapping a remote reference by name.""" def __init__(self, name): if not isinstance(name, str): raise TypeError("illegal name: %r" % name) self.name = name self.buffers = [] def __repr__(self): return "" % self.name def get_object(self, g=None): if g is None: g = {} return eval(self.name, g) class CannedCell(CannedObject): """Can a closure cell""" def __init__(self, cell): self.cell_contents = can(cell.cell_contents) def get_object(self, g=None): cell_contents = uncan(self.cell_contents, g) def inner(): return cell_contents return inner.__closure__[0] class CannedFunction(CannedObject): def __init__(self, f): self._check_type(f) self.code = f.__code__ if f.__defaults__: self.defaults = [can(fd) for fd in f.__defaults__] else: self.defaults = None if f.__kwdefaults__: self.kwdefaults = can_dict(f.__kwdefaults__) else: self.kwdefaults = None if f.__annotations__: self.annotations = can_dict(f.__annotations__) else: self.annotations = None closure = f.__closure__ if closure: self.closure = tuple(can(cell) for cell in closure) else: self.closure = None self.module = f.__module__ or '__main__' self.__name__ = f.__name__ self.buffers = [] def _check_type(self, obj): assert isinstance(obj, FunctionType), "Not a function type" def get_object(self, g=None): # try to load function back into its module: if not self.module.startswith('__'): __import__(self.module) g = sys.modules[self.module].__dict__ if g is None: g = {} if self.defaults: defaults = tuple(uncan(cfd, g) for cfd in self.defaults) else: defaults = None if self.kwdefaults: kwdefaults = uncan_dict(self.kwdefaults) else: kwdefaults = None if self.annotations: annotations = uncan_dict(self.annotations) else: annotations = {} if self.closure: closure = tuple(uncan(cell, g) for cell in self.closure) else: closure = None newFunc = FunctionType(self.code, g, self.__name__, defaults, closure) if kwdefaults: newFunc.__kwdefaults__ = kwdefaults if annotations: newFunc.__annotations__ = annotations return newFunc class CannedPartial(CannedObject): def __init__(self, f): self._check_type(f) self.func = can(f.func) self.args = [can(a) for a in f.args] self.keywords = {k: can(v) for k, v in f.keywords.items()} self.buffers = [] self.arg_buffer_counts = [] self.keyword_buffer_counts = {} # consolidate buffers for canned_arg in self.args: if not isinstance(canned_arg, CannedObject): self.arg_buffer_counts.append(0) continue self.arg_buffer_counts.append(len(canned_arg.buffers)) self.buffers.extend(canned_arg.buffers) canned_arg.buffers = [] for key in sorted(self.keywords): canned_kwarg = self.keywords[key] if not isinstance(canned_kwarg, CannedObject): continue self.keyword_buffer_counts[key] = len(canned_kwarg.buffers) self.buffers.extend(canned_kwarg.buffers) canned_kwarg.buffers = [] def _check_type(self, obj): if not isinstance(obj, functools.partial): raise ValueError("Not a functools.partial: %r" % obj) def get_object(self, g=None): if g is None: g = {} if self.buffers: # reconstitute buffers for canned_arg, buf_count in zip(self.args, self.arg_buffer_counts): if not buf_count: continue canned_arg.buffers = self.buffers[:buf_count] self.buffers = self.buffers[buf_count:] for key in sorted(self.keyword_buffer_counts): buf_count = self.keyword_buffer_counts[key] canned_kwarg = self.keywords[key] canned_kwarg.buffers = self.buffers[:buf_count] self.buffers = self.buffers[buf_count:] assert len(self.buffers) == 0 args = [uncan(a, g) for a in self.args] keywords = {k: uncan(v, g) for k, v in self.keywords.items()} func = uncan(self.func, g) return functools.partial(func, *args, **keywords) class CannedClass(CannedObject): def __init__(self, cls): self._check_type(cls) self.name = cls.__name__ self.old_style = not isinstance(cls, type) self._canned_dict = {} for k, v in cls.__dict__.items(): if k not in ('__weakref__', '__dict__'): self._canned_dict[k] = can(v) if self.old_style: mro = [] else: mro = cls.mro() self.parents = [can(c) for c in mro[1:]] self.buffers = [] def _check_type(self, obj): assert isinstance(obj, type), "Not a class type" def get_object(self, g=None): parents = tuple(uncan(p, g) for p in self.parents) return type(self.name, parents, uncan_dict(self._canned_dict, g=g)) class CannedArray(CannedObject): def __init__(self, obj): from numpy import ascontiguousarray self.shape = obj.shape self.dtype = obj.dtype.descr if obj.dtype.fields else obj.dtype.str self.pickled = False if sum(obj.shape) == 0: self.pickled = True elif obj.dtype == 'O': # can't handle object dtype with buffer approach self.pickled = True elif obj.dtype.fields and any( dt == 'O' for dt, sz in obj.dtype.fields.values() ): self.pickled = True if self.pickled: # just pickle it from . import serialize self.buffers = [serialize.pickle.dumps(obj, serialize.PICKLE_PROTOCOL)] else: # ensure contiguous obj = ascontiguousarray(obj, dtype=None) self.buffers = [memoryview(obj)] def get_object(self, g=None): from numpy import frombuffer data = self.buffers[0] if self.pickled: from . import serialize # we just pickled it return serialize.pickle.loads(data) else: return frombuffer(data, dtype=self.dtype).reshape(self.shape) class CannedBytes(CannedObject): def __init__(self, obj): self.buffers = [obj] def get_object(self, g=None): data = self.buffers[0] return self.wrap(data) @staticmethod def wrap(data): if isinstance(data, bytes): return data else: return memoryview(data).tobytes() class CannedMemoryView(CannedBytes): wrap = memoryview CannedBuffer = CannedMemoryView # ------------------------------------------------------------------------------- # Functions # ------------------------------------------------------------------------------- def _import_mapping(mapping, original=None): """import any string-keys in a type mapping""" log = get_logger() log.debug("Importing canning map") for key, value in list(mapping.items()): if isinstance(key, str): try: cls = import_item(key) except Exception: if original and key not in original: # only message on user-added classes log.error("canning class not importable: %r", key, exc_info=True) mapping.pop(key) else: mapping[cls] = mapping.pop(key) def istype(obj, check): """like isinstance(obj, check), but strict This won't catch subclasses. """ if isinstance(check, tuple): for cls in check: if type(obj) is cls: return True return False else: return type(obj) is check def can(obj): """prepare an object for pickling""" import_needed = False for cls, canner in can_map.items(): if isinstance(cls, str): import_needed = True break elif istype(obj, cls): return canner(obj) if import_needed: # perform can_map imports, then try again # this will usually only happen once _import_mapping(can_map, _original_can_map) return can(obj) return obj def can_class(obj): if isinstance(obj, type) and obj.__module__ == '__main__': return CannedClass(obj) else: return obj def can_dict(obj): """can the *values* of a dict""" if istype(obj, dict): newobj = {} for k, v in obj.items(): newobj[k] = can(v) return newobj else: return obj sequence_types = (list, tuple, set) def can_sequence(obj): """can the elements of a sequence""" if istype(obj, sequence_types): t = type(obj) return t([can(i) for i in obj]) else: return obj def uncan(obj, g=None): """invert canning""" import_needed = False for cls, uncanner in uncan_map.items(): if isinstance(cls, str): import_needed = True break elif isinstance(obj, cls): return uncanner(obj, g) if import_needed: # perform uncan_map imports, then try again # this will usually only happen once _import_mapping(uncan_map, _original_uncan_map) return uncan(obj, g) return obj def uncan_dict(obj, g=None): if istype(obj, dict): newobj = {} for k, v in obj.items(): newobj[k] = uncan(v, g) return newobj else: return obj def uncan_sequence(obj, g=None): if istype(obj, sequence_types): t = type(obj) return t([uncan(i, g) for i in obj]) else: return obj def _uncan_dependent_hook(dep, g=None): dep.check_dependency() def can_dependent(obj): return CannedObject(obj, keys=('f', 'df'), hook=_uncan_dependent_hook) # ------------------------------------------------------------------------------- # API dictionaries # ------------------------------------------------------------------------------- # These dicts can be extended for custom serialization of new objects can_map = { 'numpy.ndarray': CannedArray, FunctionType: CannedFunction, functools.partial: CannedPartial, bytes: CannedBytes, memoryview: CannedMemoryView, cell_type: CannedCell, type: can_class, 'ipyparallel.dependent': can_dependent, } uncan_map = { CannedObject: lambda obj, g: obj.get_object(g), dict: uncan_dict, } # for use in _import_mapping: _original_can_map = can_map.copy() _original_uncan_map = uncan_map.copy() ipyparallel-7.1.0/ipyparallel/serialize/codeutil.py000066400000000000000000000024371412533144600225350ustar00rootroot00000000000000# encoding: utf-8 """Utilities to enable code objects to be pickled. Any process that import this module will be able to pickle code objects. This includes the func_code attribute of any function. Once unpickled, new functions can be built using new.function(code, globals()). Eventually we need to automate all of this so that functions themselves can be pickled. Reference: A. Tremols, P Cogolo, "Python Cookbook," p 302-305 """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import sys import types try: import copyreg # Py 3 except ImportError: import copy_reg as copyreg # Py 2 def code_ctor(*args): return types.CodeType(*args) def reduce_code(co): args = [ co.co_argcount, co.co_nlocals, co.co_stacksize, co.co_flags, co.co_code, co.co_consts, co.co_names, co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars, co.co_cellvars, ] if sys.version_info[0] >= 3: args.insert(1, co.co_kwonlyargcount) if sys.version_info > (3, 8): args.insert(1, co.co_posonlyargcount) return code_ctor, tuple(args) copyreg.pickle(types.CodeType, reduce_code) ipyparallel-7.1.0/ipyparallel/serialize/serialize.py000066400000000000000000000145701412533144600227150ustar00rootroot00000000000000"""serialization utilities for apply messages""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. try: import cPickle pickle = cPickle except: cPickle = None import pickle _stdlib_pickle = pickle try: PICKLE_PROTOCOL = pickle.DEFAULT_PROTOCOL except AttributeError: PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL from itertools import chain from .canning import ( can, uncan, can_sequence, uncan_sequence, CannedObject, istype, sequence_types, ) from jupyter_client.session import MAX_ITEMS, MAX_BYTES # ----------------------------------------------------------------------------- # Serialization Functions # ----------------------------------------------------------------------------- class PrePickled: """Wrapper for a pre-pickled object Used for pre-emptively pickling re-used objects to avoid pickling the same object several times. """ def __init__(self, obj): self.buffers = serialize_object(obj) def _nbytes(buf): """Return byte-size of a memoryview or buffer""" if isinstance(buf, memoryview): return buf.nbytes else: # not a memoryview, raw bytes return len(buf) def _extract_buffers(obj, threshold=MAX_BYTES): """extract buffers larger than a certain threshold""" buffers = [] if isinstance(obj, CannedObject) and obj.buffers: for i, buf in enumerate(obj.buffers): nbytes = _nbytes(buf) if nbytes > threshold: # buffer larger than threshold, prevent pickling obj.buffers[i] = None buffers.append(buf) # buffer too small for separate send, coerce to bytes # because pickling buffer objects just results in broken pointers elif isinstance(buf, memoryview): obj.buffers[i] = buf.tobytes() return buffers def _restore_buffers(obj, buffers): """restore buffers extracted by _extract_buffers""" if isinstance(obj, CannedObject) and obj.buffers: for i, buf in enumerate(obj.buffers): if buf is None: obj.buffers[i] = buffers.pop(0) def serialize_object(obj, buffer_threshold=MAX_BYTES, item_threshold=MAX_ITEMS): """Serialize an object into a list of sendable buffers. Parameters ---------- obj : object The object to be serialized buffer_threshold : int The threshold (in bytes) for pulling out data buffers to avoid pickling them. item_threshold : int The maximum number of items over which canning will iterate. Containers (lists, dicts) larger than this will be pickled without introspection. Returns ------- [bufs] : list of buffers representing the serialized object. """ if isinstance(obj, PrePickled): return obj.buffers[:] buffers = [] if istype(obj, sequence_types) and len(obj) < item_threshold: cobj = can_sequence(obj) for c in cobj: buffers.extend(_extract_buffers(c, buffer_threshold)) elif istype(obj, dict) and len(obj) < item_threshold: cobj = {} for k in sorted(obj): c = can(obj[k]) buffers.extend(_extract_buffers(c, buffer_threshold)) cobj[k] = c else: cobj = can(obj) buffers.extend(_extract_buffers(cobj, buffer_threshold)) buffers.insert(0, pickle.dumps(cobj, PICKLE_PROTOCOL)) return buffers def deserialize_object(buffers, g=None): """reconstruct an object serialized by serialize_object from data buffers. Parameters ---------- buffers : list of buffers/bytes g : globals to be used when uncanning Returns ------- (newobj, bufs) : unpacked object, and the list of remaining unused buffers. """ bufs = list(buffers) pobj = bufs.pop(0) canned = pickle.loads(pobj) if istype(canned, sequence_types) and len(canned) < MAX_ITEMS: for c in canned: _restore_buffers(c, bufs) newobj = uncan_sequence(canned, g) elif istype(canned, dict) and len(canned) < MAX_ITEMS: newobj = {} for k in sorted(canned): c = canned[k] _restore_buffers(c, bufs) newobj[k] = uncan(c, g) else: _restore_buffers(canned, bufs) newobj = uncan(canned, g) return newobj, bufs def pack_apply_message( f, args, kwargs, buffer_threshold=MAX_BYTES, item_threshold=MAX_ITEMS ): """pack up a function, args, and kwargs to be sent over the wire Each element of args/kwargs will be canned for special treatment, but inspection will not go any deeper than that. Any object whose data is larger than `threshold` will not have their data copied (only numpy arrays and bytes/buffers support zero-copy) Message will be a list of bytes/buffers of the format: [ cf, pinfo, , ] With length at least two + len(args) + len(kwargs) """ arg_bufs = list( chain.from_iterable( serialize_object(arg, buffer_threshold, item_threshold) for arg in args ) ) kw_keys = sorted(kwargs.keys()) kwarg_bufs = list( chain.from_iterable( serialize_object(kwargs[key], buffer_threshold, item_threshold) for key in kw_keys ) ) info = dict(nargs=len(args), narg_bufs=len(arg_bufs), kw_keys=kw_keys) msg = serialize_object(f) msg.append(pickle.dumps(info, PICKLE_PROTOCOL)) msg.extend(arg_bufs) msg.extend(kwarg_bufs) return msg def unpack_apply_message(bufs, g=None, copy=True): """unpack f,args,kwargs from buffers packed by pack_apply_message() Returns: original f,args,kwargs""" bufs = list(bufs) # allow us to pop assert len(bufs) >= 2, "not enough buffers!" f, bufs = deserialize_object(bufs, g) pinfo = bufs.pop(0) info = pickle.loads(pinfo) arg_bufs, kwarg_bufs = bufs[: info['narg_bufs']], bufs[info['narg_bufs'] :] args = [] for i in range(info['nargs']): arg, arg_bufs = deserialize_object(arg_bufs, g) args.append(arg) args = tuple(args) assert not arg_bufs, "Shouldn't be any arg bufs left over" kwargs = {} for key in info['kw_keys']: kwarg, kwarg_bufs = deserialize_object(kwarg_bufs, g) kwargs[key] = kwarg assert not kwarg_bufs, "Shouldn't be any kwarg bufs left over" return f, args, kwargs ipyparallel-7.1.0/ipyparallel/tests/000077500000000000000000000000001412533144600175205ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/tests/__init__.py000066400000000000000000000104761412533144600216410ustar00rootroot00000000000000"""toplevel setup/teardown for parallel tests.""" from __future__ import print_function import asyncio import os import time from subprocess import Popen from IPython.paths import get_ipython_dir from ipyparallel import Client from ipyparallel import error from ipyparallel.cluster.launcher import ipcontroller_cmd_argv from ipyparallel.cluster.launcher import ipengine_cmd_argv from ipyparallel.cluster.launcher import LocalProcessLauncher from ipyparallel.cluster.launcher import ProcessStateError from ipyparallel.cluster.launcher import SIGKILL # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. # globals launchers = [] # Launcher class class TestProcessLauncher(LocalProcessLauncher): """subclass LocalProcessLauncher, to prevent extra sockets and threads being created on Windows""" def start(self): if self.state == 'before': # Store stdout & stderr to show with failing tests. # This is defined in IPython.testing.iptest self.process = Popen(self.args, env=os.environ, cwd=self.work_dir) self.notify_start(self.process.pid) self.poll = self.process.poll else: s = 'The process was already started and has state: %r' % self.state raise ProcessStateError(s) # global setup/teardown def setup(): # show tracebacks for RemoteErrors class RemoteErrorWithTB(error.RemoteError): def __str__(self): s = super(RemoteErrorWithTB, self).__str__() return '\n'.join([s, self.traceback or '']) error.RemoteError = RemoteErrorWithTB cluster_dir = os.path.join(get_ipython_dir(), 'profile_iptest') engine_json = os.path.join(cluster_dir, 'security', 'ipcontroller-engine.json') client_json = os.path.join(cluster_dir, 'security', 'ipcontroller-client.json') for json in (engine_json, client_json): if os.path.exists(json): os.remove(json) cp = TestProcessLauncher() cp.cmd_and_args = ipcontroller_cmd_argv + [ '--profile=iptest', '--log-level=10', '--ping=250', '--dictdb', ] if os.environ.get("IPP_CONTROLLER_IP"): cp.cmd_and_args.append(f"--ip={os.environ['IPP_CONTROLLER_IP']}") cp.start() launchers.append(cp) tic = time.time() while not os.path.exists(engine_json) or not os.path.exists(client_json): if cp.poll() is not None: raise RuntimeError("The test controller exited with status %s" % cp.poll()) elif time.time() - tic > 15: raise RuntimeError("Timeout waiting for the test controller to start.") time.sleep(0.1) add_engines(1) def add_engines(n=1, profile='iptest', total=False): """add a number of engines to a given profile. If total is True, then already running engines are counted, and only the additional engines necessary (if any) are started. """ rc = Client(profile=profile) base = len(rc) if total: n = max(n - base, 0) eps = [] for i in range(n): ep = TestProcessLauncher() ep.cmd_and_args = ipengine_cmd_argv + [ '--profile=%s' % profile, '--InteractiveShell.colors=nocolor', '--log-level=10', ] ep.start() launchers.append(ep) eps.append(ep) tic = time.time() while len(rc) < base + n: if any([ep.poll() is not None for ep in eps]): raise RuntimeError("A test engine failed to start.") elif time.time() - tic > 15: raise RuntimeError("Timeout waiting for engines to connect.") time.sleep(0.1) rc.close() return eps def teardown(): try: time.sleep(1) except KeyboardInterrupt: return while launchers: p = launchers.pop() if p.poll() is None: try: f = p.stop() if f: asyncio.run(f) except Exception as e: print(e) pass if p.poll() is None: try: time.sleep(0.25) except KeyboardInterrupt: return if p.poll() is None: try: print('cleaning up test process...') p.signal(SIGKILL) except: print("couldn't shutdown process: ", p) ipyparallel-7.1.0/ipyparallel/tests/_test_startup_crash.py000066400000000000000000000001321412533144600241460ustar00rootroot00000000000000import os import time time.sleep(int(os.environ.get("CRASH_DELAY") or "1")) os._exit(1) ipyparallel-7.1.0/ipyparallel/tests/clienttest.py000066400000000000000000000130731412533144600222540ustar00rootroot00000000000000"""base class for parallel client tests""" from __future__ import print_function import os import signal import sys import time from contextlib import contextmanager import pytest import zmq from decorator import decorator from zmq.tests import BaseZMQTestCase from ipyparallel import Client from ipyparallel import error from ipyparallel.tests import add_engines from ipyparallel.tests import launchers # simple tasks for use in apply tests def segfault(): """this will segfault""" import ctypes ctypes.memset(-1, 0, 1) def crash(): """Ungracefully exit the process""" os._exit(1) def conditional_crash(condition): """Ungracefully exit the process""" if condition: crash() def wait(n): """sleep for a time""" import time time.sleep(n) return n def raiser(eclass): """raise an exception""" raise eclass() def generate_output(): """function for testing output publishes two outputs of each type, and returns a rich displayable object. """ from IPython.core.display import display, HTML, Math print("stdout") print("stderr", file=sys.stderr) display(HTML("HTML")) print("stdout2") print("stderr2", file=sys.stderr) display(Math(r"\alpha=\beta")) return Math("42") # test decorator for skipping tests when libraries are unavailable def skip_without(*names): """skip a test if some names are not importable""" @decorator def skip_without_names(f, *args, **kwargs): """decorator to skip tests in the absence of numpy, etc.""" for name in names: try: __import__(name) except ImportError: pytest.skip("Test requires %s" % name) return f(*args, **kwargs) return skip_without_names @contextmanager def raises_remote(etype): if isinstance(etype, str): # allow Exception or 'Exception' expected_ename = etype else: expected_ename = etype.__name__ try: try: yield except error.CompositeError as e: e.raise_exception() except error.RemoteError as e: assert ( expected_ename == e.ename ), f"Should have raised {expected_ename}, but raised {e.ename}" else: pytest.fail("should have raised a RemoteError") # ------------------------------------------------------------------------------- # Classes # ------------------------------------------------------------------------------- @pytest.mark.usefixtures("cluster") class ClusterTestCase(BaseZMQTestCase): timeout = 10 engine_count = 2 def add_engines(self, n=1, block=True): """add multiple engines to our cluster""" self.engines.extend(add_engines(n)) if block: self.wait_on_engines() def minimum_engines(self, n=1, block=True): """add engines until there are at least n connected""" self.engines.extend(add_engines(n, total=True)) if block: self.wait_on_engines() def wait_on_engines(self, timeout=5): """wait for our engines to connect.""" n = len(self.engines) + self.base_engine_count self.client.wait_for_engines(n, timeout=timeout) assert not len(self.client.ids) < n, "waiting for engines timed out" def client_wait(self, client, jobs=None, timeout=-1): """my wait wrapper, sets a default finite timeout to avoid hangs""" if timeout is None or timeout < 0: timeout = self.timeout return Client.wait(client, jobs, timeout) def connect_client(self): """connect a client with my Context, and track its sockets for cleanup""" c = Client(profile='iptest', context=self.context) c.wait = lambda *a, **kw: self.client_wait(c, *a, **kw) for name in filter(lambda n: n.endswith('socket'), dir(c)): s = getattr(c, name) s.setsockopt(zmq.LINGER, 0) self.sockets.append(s) return c def assertRaisesRemote(self, etype, f, *args, **kwargs): with raises_remote(etype): f(*args, **kwargs) def _wait_for(self, f, timeout=10): """wait for a condition""" tic = time.time() while time.time() <= tic + timeout: if f(): return time.sleep(0.1) if not f(): print("Warning: Awaited condition never arrived") test_timeout = 30 def setUp(self): BaseZMQTestCase.setUp(self) if hasattr(signal, 'SIGALRM'): # use sigalarm for test timeout def _sigalarm(sig, frame): raise TimeoutError( f"test did not finish in {self.test_timeout} seconds" ) signal.signal(signal.SIGALRM, _sigalarm) signal.alarm(self.test_timeout) add_engines(self.engine_count, total=True) self.client = self.connect_client() # start every test with clean engine namespaces: self.client.clear(block=True) self.base_engine_count = len(self.client.ids) self.engines = [] def tearDown(self): self.client[:].use_pickle() # self.client.clear(block=True) # close fds: for e in filter(lambda e: e.poll() is not None, launchers): launchers.remove(e) # allow flushing of incoming messages to prevent crash on socket close self.client.wait(timeout=2) self.client.close() BaseZMQTestCase.tearDown(self) if hasattr(signal, 'SIGALRM'): signal.alarm(0) signal.signal(signal.SIGALRM, signal.SIG_DFL) ipyparallel-7.1.0/ipyparallel/tests/conftest.py000066400000000000000000000124001412533144600217140ustar00rootroot00000000000000"""pytest fixtures""" import inspect import logging import os import sys from contextlib import contextmanager from subprocess import check_call from subprocess import check_output from tempfile import TemporaryDirectory from unittest import mock import IPython.paths import pytest import zmq from IPython.core.profiledir import ProfileDir from IPython.terminal.interactiveshell import TerminalInteractiveShell from IPython.testing.tools import default_config from traitlets.config import Config import ipyparallel as ipp from . import setup from . import teardown @contextmanager def temporary_ipython_dir(): # FIXME: cleanup has issues on Windows # this is *probably* a real bug of holding open files, # but it is preventing feedback about test failures td_obj = TemporaryDirectory(suffix=".ipython") td = td_obj.name with mock.patch.dict(os.environ, {"IPYTHONDIR": td}): assert IPython.paths.get_ipython_dir() == td pd = ProfileDir.create_profile_dir_by_name(td, name="default") # configure fast heartbeats for quicker tests with small numbers of local engines with open(os.path.join(pd.location, "ipcontroller_config.py"), "w") as f: f.write("c.HeartMonitor.period = 200") try: yield td finally: try: td_obj.cleanup() except Exception as e: print(f"Failed to cleanup {td}: {e}", file=sys.stderr) @pytest.fixture(autouse=True, scope="module") def ipython_dir(request): with temporary_ipython_dir() as ipython_dir: yield ipython_dir def pytest_collection_modifyitems(items): """This function is automatically run by pytest passing all collected test functions. We use it to add asyncio marker to all async tests and assert we don't use test functions that are async generators which wouldn't make sense. """ for item in items: if inspect.iscoroutinefunction(item.obj): item.add_marker('asyncio') assert not inspect.isasyncgenfunction(item.obj) @pytest.fixture(scope="module") def cluster(request, ipython_dir): """Setup IPython parallel cluster""" setup() try: yield finally: teardown() @pytest.fixture(scope='module') def ipython(ipython_dir): config = default_config() config.TerminalInteractiveShell.simple_prompt = True shell = TerminalInteractiveShell.instance(config=config) yield shell TerminalInteractiveShell.clear_instance() @pytest.fixture() def ipython_interactive(request, ipython): """Activate IPython's builtin hooks for the duration of the test scope. """ with ipython.builtin_trap: yield ipython @pytest.fixture(autouse=True) def Context(): ctx = zmq.Context.instance() try: yield ctx finally: ctx.destroy() @pytest.fixture def Cluster(request, ipython_dir, io_loop): """Fixture for instantiating Clusters""" def ClusterConstructor(**kwargs): log = logging.getLogger(__file__) log.setLevel(logging.DEBUG) log.handlers = [logging.StreamHandler(sys.stdout)] kwargs['log'] = log engine_launcher_class = kwargs.get("engine_launcher_class") cfg = kwargs.setdefault("config", Config()) cfg.EngineLauncher.engine_args = ['--log-level=10'] cfg.ControllerLauncher.controller_args = ['--log-level=10'] kwargs.setdefault("controller_args", ['--ping=250']) kwargs.setdefault("load_profile", False) c = ipp.Cluster(**kwargs) if not kwargs['load_profile']: assert c.config == cfg request.addfinalizer(c.stop_cluster_sync) return c yield ClusterConstructor @pytest.fixture(scope="session") def ssh_dir(request): """Start the ssh service with docker-compose Fixture returns the directory """ repo_root = os.path.abspath(os.path.join(ipp.__file__, os.pardir, os.pardir)) ci_directory = os.environ.get("CI_DIR", os.path.join(repo_root, 'ci')) ssh_dir = os.path.join(ci_directory, "ssh") # only run ssh test if service was started before try: out = check_output(['docker-compose', 'ps', '-q'], cwd=ssh_dir) except Exception: pytest.skip("Needs docker compose") else: if not out.strip(): pytest.skip("ssh service not running") # below is necessary for building/starting service as part of fixture # currently we use whether the service is already started to decide whether to run the tests # # build image # check_call(["docker-compose", "build"], cwd=ssh_dir) # # launch service # check_call(["docker-compose", "up", "-d"], cwd=ssh_dir) # # shutdown service when we exit # request.addfinalizer(lambda: check_call(["docker-compose", "down"], cwd=ssh_dir)) return ssh_dir @pytest.fixture def ssh_key(tmpdir, ssh_dir): key_file = tmpdir.join("id_rsa") check_call( # this should be `docker compose cp sshd:...` # but docker-compose 1.x doesn't support `cp` yet [ 'docker', 'cp', 'ssh_sshd_1:/home/ciuser/.ssh/id_rsa', key_file, ], cwd=ssh_dir, ) os.chmod(key_file, 0o600) with key_file.open('r') as f: assert 'PRIVATE KEY' in f.readline() return str(key_file) ipyparallel-7.1.0/ipyparallel/tests/test_apps.py000066400000000000000000000202601412533144600220740ustar00rootroot00000000000000"""Test CLI application behavior""" import glob import json import os import sys import time import types from subprocess import check_call from subprocess import check_output from subprocess import Popen from unittest.mock import create_autospec from unittest.mock import MagicMock from unittest.mock import patch import pytest import zmq from ipykernel import iostream from ipykernel import kernelapp from ipykernel.ipkernel import IPythonKernel from IPython.core.profiledir import ProfileDir from tornado import ioloop from zmq.eventloop import zmqstream import ipyparallel as ipp import ipyparallel.engine.app def _get_output(cmd): out = check_output(cmd) if isinstance(out, bytes): out = out.decode('utf8', 'replace') return out @pytest.mark.parametrize( "submod", [ "cluster", "engine", "controller", ], ) def test_version(submod): out = _get_output([sys.executable, '-m', 'ipyparallel.%s' % submod, '--version']) assert out.strip() == ipyparallel.__version__ @pytest.mark.parametrize( "submod", [ "cluster", "engine", "controller", ], ) def test_help_all(submod): out = _get_output([sys.executable, '-m', 'ipyparallel.%s' % submod, '--help-all']) @pytest.mark.parametrize( "subcommand", [ "list", "engines", "start", "stop", "clean", ], ) def test_ipcluster_help_all(subcommand): out = _get_output( [sys.executable, '-m', 'ipyparallel.cluster', subcommand, '--help-all'] ) def bind_kernel(engineapp): app = MagicMock(spec=kernelapp.IPKernelApp) with patch.object( ipyparallel.engine.app, 'IPKernelApp', autospec=True ) as MockKernelApp: MockKernelApp.return_value = app app.shell_port = app.iopub_port = app.stdin_port = 0 app._bind_socket = types.MethodType(kernelapp.IPKernelApp._bind_socket, app) if hasattr(kernelapp.IPKernelApp, '_try_bind_socket'): app._try_bind_socket = types.MethodType( kernelapp.IPKernelApp._try_bind_socket, app, ) app.transport = 'tcp' app.ip = '127.0.0.1' app.init_heartbeat.return_value = None engineapp.bind_kernel() def test_bind_kernel(request): ctx = zmq.Context.instance() request.addfinalizer(ctx.destroy) class MockIPEngineApp(ipyparallel.engine.app.IPEngine): kernel = None context = ctx app = MockIPEngineApp() app.kernel_app = None app.kernel = MagicMock(spec=IPythonKernel) def socket_spec(): spec = create_autospec(zmq.Socket, instance=True) spec.FD = 2 return spec app.kernel.shell_streams = [ zmqstream.ZMQStream( socket=socket_spec(), io_loop=create_autospec(spec=ioloop.IOLoop, spec_set=True, instance=True), ) ] app.kernel.control_stream = zmqstream.ZMQStream( socket=socket_spec(), io_loop=create_autospec(spec=ioloop.IOLoop, spec_set=True, instance=True), ) # testing the case iopub_socket is not replaced with IOPubThread iopub_socket = socket_spec() app.kernel.iopub_socket = iopub_socket assert isinstance(app.kernel.iopub_socket, zmq.Socket) bind_kernel(app) assert ( app.kernel.iopub_socket.bind_to_random_port.called and app.kernel.iopub_socket.bind_to_random_port.call_count == 1 ) # testing the case iopub_socket is replaced with IOPubThread class TestIOPubThread(iostream.IOPubThread): socket = None iopub_socket.reset_mock() app.kernel_app = None app.kernel.iopub_socket = create_autospec( spec=TestIOPubThread, spec_set=True, instance=True ) app.kernel.iopub_socket.socket = iopub_socket assert isinstance(app.kernel.iopub_socket, iostream.IOPubThread) bind_kernel(app) assert ( app.kernel.iopub_socket.socket.bind_to_random_port.called and app.kernel.iopub_socket.socket.bind_to_random_port.call_count == 1 ) def ipcluster_list(*args): return check_output( [sys.executable, "-m", "ipyparallel.cluster", "list"] + list(args) ).decode("utf8", "replace") def test_ipcluster_list(Cluster): # no clusters out = ipcluster_list() assert len(out.splitlines()) == 1 out = ipcluster_list("-o", "json") assert json.loads(out) == [] with Cluster(n=2) as rc: out = ipcluster_list() head, *rest = out.strip().splitlines() assert len(rest) == 1 assert rest[0].split() == [ "default", rc.cluster.cluster_id, "True", "2", "Local", ] cluster_list = json.loads(ipcluster_list("-o", "json")) assert len(cluster_list) == 1 assert cluster_list[0]['cluster']['cluster_id'] == rc.cluster.cluster_id # after exit, back to no clusters out = ipcluster_list() assert len(out.splitlines()) == 1 out = ipcluster_list("-o", "json") assert json.loads(out) == [] @pytest.mark.parametrize("daemonize", (False, True)) def test_ipcluster_start_stop(request, ipython_dir, daemonize): default_profile = ProfileDir.find_profile_dir_by_name(ipython_dir) default_profile_dir = default_profile.location # cleanup the security directory to avoid leaking files from one test to the next def cleanup_security(): for f in glob.glob(os.path.join(default_profile.security_dir, "*.json")): print(f"Cleaning up {f}") try: os.remove(f) except Exception as e: print(f"Error removing {f}: {e}") request.addfinalizer(cleanup_security) n = 2 start_args = ["-n", str(n)] if daemonize: start_args.append("--daemonize") start = Popen( [sys.executable, "-m", "ipyparallel.cluster", "start", "--debug"] + start_args ) request.addfinalizer(start.terminate) if daemonize: # if daemonize, should exit after starting start.wait(30) else: # wait for file to appear # only need this if not daemonize cluster_file = ipp.Cluster( profile_dir=default_profile_dir, cluster_id="" ).cluster_file for i in range(100): if os.path.isfile(cluster_file) or start.poll() is not None: break else: time.sleep(0.1) assert os.path.isfile(cluster_file) # list should show a file out = ipcluster_list() assert len(out.splitlines()) == 2 # cluster running, try to connect with default args cluster = ipp.Cluster.from_file(log_level=10) try: with cluster.connect_client_sync() as rc: rc.wait_for_engines(n=2, timeout=60) rc[:].apply_async(os.getpid).get(timeout=10) except Exception: print("controller output") print(cluster.controller.get_output()) print("engine output") for engine_set in cluster.engines.values(): print(engine_set.get_output()) raise # stop with ipcluster stop check_call([sys.executable, "-m", "ipyparallel.cluster", "stop"]) # start should exit when this happens start.wait(30) # and ipcluster list should return empty out = ipcluster_list() assert len(out.splitlines()) == 1 # stop all succeeds even if there's nothing to stop check_call([sys.executable, "-m", "ipyparallel.cluster", "stop", "--all"]) def test_ipcluster_clean(ipython_dir): default_profile = ProfileDir.find_profile_dir_by_name(ipython_dir) default_profile_dir = default_profile.location log_file = os.path.join(default_profile.log_dir, "test.log") with open(log_file, "w") as f: f.write("logsssss") cluster_file = os.path.join(default_profile.security_dir, "cluster-abc.json") c = ipp.Cluster() with open(cluster_file, 'w') as f: json.dump(c.to_dict(), f) connection_file = os.path.join( default_profile.security_dir, "ipcontroller-client.json" ) with open(connection_file, 'w') as f: f.write("{}") check_call([sys.executable, "-m", "ipyparallel.cluster", "clean"]) assert not os.path.exists(log_file) assert not os.path.exists(cluster_file) assert not os.path.exists(connection_file) ipyparallel-7.1.0/ipyparallel/tests/test_async.py000066400000000000000000000016501412533144600222500ustar00rootroot00000000000000"""tests for async utilities""" import pytest from ipyparallel._async import AsyncFirst class A(AsyncFirst): a = 5 def sync_method(self): return 'sync' async def async_method(self): return 'async' def test_async_first_dir(): a = A() attrs = sorted(dir(a)) real_attrs = [a for a in attrs if not a.startswith("_")] assert real_attrs == ['a', 'async_method', 'async_method_sync', 'sync_method'] def test_getattr(): a = A() assert a.a == 5 with pytest.raises(AttributeError): a.a_sync with pytest.raises(AttributeError): a.some_other_sync with pytest.raises(AttributeError): a.sync_method_sync def test_sync_no_asyncio(): a = A() assert a.async_method_sync() == 'async' assert a._async_thread is None async def test_sync_asyncio(): a = A() assert a.async_method_sync() == 'async' assert a._async_thread is not None ipyparallel-7.1.0/ipyparallel/tests/test_asyncresult.py000066400000000000000000000334371412533144600235170ustar00rootroot00000000000000"""Tests for asyncresult.py""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import os import time from IPython.utils.io import capture_output import ipyparallel as ipp from .clienttest import ClusterTestCase from .clienttest import raises_remote from ipyparallel import error def wait(n): import time time.sleep(n) return n def echo(x): return x class AsyncResultTest(ClusterTestCase): def test_single_result_view(self): """various one-target views get the right value for single_result""" eid = self.client.ids[-1] ar = self.client[eid].apply_async(lambda: 42) self.assertEqual(ar.get(), 42) ar = self.client[[eid]].apply_async(lambda: 42) self.assertEqual(ar.get(), [42]) ar = self.client[-1:].apply_async(lambda: 42) self.assertEqual(ar.get(), [42]) def test_get_after_done(self): ar = self.client[-1].apply_async(lambda: 42) ar.wait() self.assertTrue(ar.ready()) self.assertEqual(ar.get(), 42) self.assertEqual(ar.get(), 42) def test_get_before_done(self): ar = self.client[-1].apply_async(wait, 0.1) self.assertRaises(TimeoutError, ar.get, 0) ar.wait(0) self.assertFalse(ar.ready()) self.assertEqual(ar.get(), 0.1) def test_get_after_error(self): ar = self.client[-1].apply_async(lambda: 1 / 0) ar.wait(10) self.assertRaisesRemote(ZeroDivisionError, ar.get) self.assertRaisesRemote(ZeroDivisionError, ar.get) self.assertRaisesRemote(ZeroDivisionError, ar.get_dict) def test_get_dict(self): n = len(self.client) ar = self.client[:].apply_async(lambda: 5) self.assertEqual(ar.get(), [5] * n) d = ar.get_dict() self.assertEqual(sorted(d.keys()), sorted(self.client.ids)) for eid, r in d.items(): self.assertEqual(r, 5) def test_get_dict_single(self): view = self.client[-1] for v in (list(range(5)), 5, ('abc', 'def'), 'string'): ar = view.apply_async(echo, v) self.assertEqual(ar.get(), v) d = ar.get_dict() self.assertEqual(d, {view.targets: v}) def test_get_dict_bad(self): v = self.client.load_balanced_view() amr = v.map_async(lambda x: x, range(len(self.client) * 2)) self.assertRaises(ValueError, amr.get_dict) def test_iter_amr(self): ar = self.client.load_balanced_view().map_async(wait, [0.125] * 5) for r in ar: self.assertEqual(r, 0.125) def test_iter_multi_result_ar(self): ar = self.client[:].apply(wait, 0.125) for r in ar: self.assertEqual(r, 0.125) def test_iter_error(self): amr = self.client[:].map_async(lambda x: 1 / (x - 2), range(5)) # iterating through failing AMR should raise RemoteError self.assertRaisesRemote(ZeroDivisionError, list, amr) # so should get self.assertRaisesRemote(ZeroDivisionError, amr.get) amr.wait(10) # test iteration again after everything is local self.assertRaisesRemote(ZeroDivisionError, list, amr) def test_getattr(self): ar = self.client[:].apply_async(wait, 0.5) self.assertEqual(ar.engine_id, [None] * len(ar)) self.assertRaises(AttributeError, lambda: ar._foo) self.assertRaises(AttributeError, lambda: ar.__length_hint__()) self.assertRaises(AttributeError, lambda: ar.foo) self.assertFalse(hasattr(ar, '__length_hint__')) self.assertFalse(hasattr(ar, 'foo')) self.assertTrue(hasattr(ar, 'engine_id')) ar.get(5) self.assertRaises(AttributeError, lambda: ar._foo) self.assertRaises(AttributeError, lambda: ar.__length_hint__()) self.assertRaises(AttributeError, lambda: ar.foo) self.assertTrue(isinstance(ar.engine_id, list)) self.assertEqual(ar.engine_id, ar['engine_id']) self.assertFalse(hasattr(ar, '__length_hint__')) self.assertFalse(hasattr(ar, 'foo')) self.assertTrue(hasattr(ar, 'engine_id')) def test_getitem(self): ar = self.client[:].apply_async(wait, 0.5) self.assertEqual(ar['engine_id'], [None] * len(ar)) self.assertRaises(KeyError, lambda: ar['foo']) ar.get(5) self.assertRaises(KeyError, lambda: ar['foo']) self.assertTrue(isinstance(ar['engine_id'], list)) self.assertEqual(ar.engine_id, ar['engine_id']) def test_single_result(self): ar = self.client[-1].apply_async(wait, 0.5) self.assertRaises(KeyError, lambda: ar['foo']) self.assertEqual(ar['engine_id'], None) self.assertTrue(ar.get(5) == 0.5) self.assertTrue(isinstance(ar['engine_id'], int)) self.assertTrue(isinstance(ar.engine_id, int)) self.assertEqual(ar.engine_id, ar['engine_id']) def test_abort(self): e = self.client[-1] ar = e.execute('import time; time.sleep(1)', block=False) ar2 = e.apply_async(lambda: 2) ar2.abort() self.assertRaises(error.TaskAborted, ar2.get) ar.get() def test_len(self): v = self.client.load_balanced_view() ar = v.map_async(lambda x: x, list(range(10))) self.assertEqual(len(ar), 10) ar = v.apply_async(lambda x: x, list(range(10))) self.assertEqual(len(ar), 1) ar = self.client[:].apply_async(lambda x: x, list(range(10))) self.assertEqual(len(ar), len(self.client.ids)) def test_wall_time_single(self): v = self.client.load_balanced_view() ar = v.apply_async(time.sleep, 0.25) self.assertRaises(TimeoutError, getattr, ar, 'wall_time') ar.get(2) self.assertTrue(ar.wall_time < 1.0) self.assertTrue(ar.wall_time > 0.2) def test_wall_time_multi(self): self.minimum_engines(4) v = self.client[:] ar = v.apply_async(time.sleep, 0.25) self.assertRaises(TimeoutError, getattr, ar, 'wall_time') ar.get(2) self.assertTrue(ar.wall_time < 1.0) self.assertTrue(ar.wall_time > 0.2) def test_serial_time_single(self): v = self.client.load_balanced_view() ar = v.apply_async(time.sleep, 0.25) self.assertRaises(TimeoutError, getattr, ar, 'serial_time') ar.get(2) self.assertTrue(ar.serial_time < 1.0) self.assertTrue(ar.serial_time > 0.2) def test_serial_time_multi(self): self.minimum_engines(4) v = self.client[:] ar = v.apply_async(time.sleep, 0.25) self.assertRaises(TimeoutError, getattr, ar, 'serial_time') ar.get(2) self.assertTrue(ar.serial_time < 2.0) self.assertTrue(ar.serial_time > 0.8) def test_elapsed_single(self): v = self.client.load_balanced_view() ar = v.apply_async(time.sleep, 0.25) while not ar.ready(): time.sleep(0.01) self.assertTrue(ar.elapsed < 1) self.assertTrue(ar.elapsed < 1) ar.get(2) def test_elapsed_multi(self): v = self.client[:] ar = v.apply_async(time.sleep, 0.25) while not ar.ready(): time.sleep(0.01) self.assertLess(ar.elapsed, 1) self.assertLess(ar.elapsed, 1) ar.get(2) def test_hubresult_timestamps(self): self.minimum_engines(4) v = self.client[:] ar = v.apply_async(time.sleep, 0.25) ar.get(2) rc2 = ipp.Client(profile='iptest') # must have try/finally to close second Client, otherwise # will have dangling sockets causing problems try: time.sleep(0.25) hr = rc2.get_result(ar.msg_ids) self.assertTrue(hr.elapsed > 0.0, "got bad elapsed: %s" % hr.elapsed) hr.get(1) self.assertTrue( hr.wall_time < ar.wall_time + 0.2, "got bad wall_time: %s > %s" % (hr.wall_time, ar.wall_time), ) self.assertEqual(hr.serial_time, ar.serial_time) finally: rc2.close() def test_display_empty_streams_single(self): """empty stdout/err are not displayed (single result)""" self.minimum_engines(1) v = self.client[-1] ar = v.execute("print (5555)") ar.get(5) with capture_output() as io: ar.display_outputs() self.assertEqual(io.stderr, '') self.assertEqual('5555\n', io.stdout) ar = v.execute("a=5") ar.get(5) with capture_output() as io: ar.display_outputs() self.assertEqual(io.stderr, '') self.assertEqual(io.stdout, '') def test_display_empty_streams_type(self): """empty stdout/err are not displayed (groupby type)""" self.minimum_engines(1) v = self.client[:] ar = v.execute("print (5555)") ar.get(5) with capture_output() as io: ar.display_outputs() self.assertEqual(io.stderr, '') self.assertEqual(io.stdout.count('5555'), len(v), io.stdout) self.assertFalse('\n\n' in io.stdout, io.stdout) self.assertEqual(io.stdout.count('[stdout:'), len(v), io.stdout) ar = v.execute("a=5") ar.get(5) with capture_output() as io: ar.display_outputs() self.assertEqual(io.stderr, '') self.assertEqual(io.stdout, '') def test_display_empty_streams_engine(self): """empty stdout/err are not displayed (groupby engine)""" self.minimum_engines(1) v = self.client[:] ar = v.execute("print (5555)") ar.get(5) with capture_output() as io: ar.display_outputs('engine') self.assertEqual(io.stderr, '') self.assertEqual(io.stdout.count('5555'), len(v), io.stdout) self.assertFalse('\n\n' in io.stdout, io.stdout) self.assertEqual(io.stdout.count('[stdout:'), len(v), io.stdout) ar = v.execute("a=5") ar.get(5) with capture_output() as io: ar.display_outputs('engine') self.assertEqual(io.stderr, '') self.assertEqual(io.stdout, '') def test_await_data(self): """asking for ar.data flushes outputs""" self.minimum_engines(1) v = self.client[-1] ar = v.execute( '\n'.join( [ "import time", "from ipyparallel.datapub import publish_data", "for i in range(5):", " publish_data(dict(i=i))", " time.sleep(0.1)", ] ), block=False, ) found = set() tic = time.time() # timeout after 10s while time.time() <= tic + 10: if ar.data: i = ar.data['i'] found.add(i) if i == 4: break time.sleep(0.05) ar.get(5) assert 4 in found assert len(found) > 1, ( "should have seen data multiple times, but got: %s" % found ) def test_not_single_result(self): save_build = self.client._build_targets def single_engine(*a, **kw): idents, targets = save_build(*a, **kw) return idents[:1], targets[:1] ids = single_engine('all')[1] self.client._build_targets = single_engine for targets in ('all', None, ids): dv = self.client.direct_view(targets=targets) ar = dv.apply_async(lambda: 5) self.assertEqual(ar.get(10), [5]) self.client._build_targets = save_build def test_owner_pop(self): self.minimum_engines(1) view = self.client[-1] ar = view.apply_async(lambda: 1) ar.get() ar.wait_for_output() msg_id = ar.msg_ids[0] self.assertNotIn(msg_id, self.client.results) self.assertNotIn(msg_id, self.client.metadata) def test_dir(self): """dir(AsyncResult)""" view = self.client[-1] ar = view.apply_async(lambda: 1) ar.get() d = dir(ar) self.assertIn('stdout', d) self.assertIn('get', d) def test_wait_for_send(self): view = self.client[-1] view.track = True with self.assertRaises(TimeoutError): # this test can fail if the send happens too quickly # e.g. the IO thread takes control for too long, # so run the test a few times for i in range(3): if i > 0: print("Retrying test_wait_for_send") data = os.urandom(10 * 1024 * 1024) ar = view.apply_async(lambda x: x, data) ar.wait_for_send(0) ar.wait_for_send(10) def test_return_exceptions(self): view = self.client.load_balanced_view() def fail_on_2(n): if n == 2: raise ValueError("two!") return n amr = view.map(fail_on_2, range(4), return_exceptions=True) assert amr._return_exceptions rlist = list(amr) error = rlist[2] assert isinstance(error, ipp.RemoteError) expected = [0, 1, error, 3] assert list(amr) == expected assert amr.get() == expected def test_return_exceptions_postmortem(self): self.minimum_engines(3) dv = self.client[:] bad_id = dv.targets[1] dv.scatter("rank", dv.targets, flatten=True) def fail_on_bad_id(rank, bad_id): if rank == bad_id: raise ValueError(f"{rank} is bad!") return rank ar = dv.apply_async(fail_on_bad_id, ipp.Reference('rank'), bad_id) with raises_remote(ValueError): ar.get() result = ar.get(return_exceptions=True) assert result[:1] == dv.targets[:1] assert result[2:] == dv.targets[2:] assert isinstance(result[1], ipp.RemoteError) ipyparallel-7.1.0/ipyparallel/tests/test_canning.py000066400000000000000000000051651412533144600225550ustar00rootroot00000000000000import os import pickle from binascii import b2a_hex from functools import partial from ipyparallel.serialize import canning from ipyparallel.serialize.canning import can from ipyparallel.serialize.canning import uncan def interactive(f): f.__module__ = '__main__' return f def dumps(obj): return pickle.dumps(can(obj)) def loads(obj): return uncan(pickle.loads(obj)) def roundtrip(obj): return loads(dumps(obj)) def test_no_closure(): @interactive def foo(): a = 5 return a pfoo = dumps(foo) bar = loads(pfoo) assert foo() == bar() def test_generator_closure(): # this only creates a closure on Python 3 @interactive def foo(): i = 'i' r = [i for j in (1, 2)] return r pfoo = dumps(foo) bar = loads(pfoo) assert foo() == bar() def test_nested_closure(): @interactive def foo(): i = 'i' def g(): return i return g() pfoo = dumps(foo) bar = loads(pfoo) assert foo() == bar() def test_closure(): i = 'i' @interactive def foo(): return i pfoo = dumps(foo) bar = loads(pfoo) assert foo() == bar() def test_uncan_bytes_buffer(): data = b'data' canned = can(data) canned.buffers = [memoryview(buf) for buf in canned.buffers] out = uncan(canned) assert out == data def test_can_partial(): def foo(x, y, z): return x * y * z partial_foo = partial(foo, 2, y=5) canned = can(partial_foo) assert isinstance(canned, canning.CannedPartial) dumped = pickle.dumps(canned) loaded = pickle.loads(dumped) pfoo2 = uncan(loaded) assert pfoo2(z=3) == partial_foo(z=3) def test_can_partial_buffers(): def foo(arg1, arg2, kwarg1, kwarg2): return '%s%s%s%s' % (arg1, arg2[:32], b2a_hex(kwarg1.tobytes()[:32]), kwarg2) buf1 = os.urandom(1024 * 1024) buf2 = memoryview(os.urandom(1024 * 1024)) partial_foo = partial(foo, 5, buf1, kwarg1=buf2, kwarg2=10) canned = can(partial_foo) assert len(canned.buffers) == 2 assert isinstance(canned, canning.CannedPartial) buffers, canned.buffers = canned.buffers, [] dumped = pickle.dumps(canned) loaded = pickle.loads(dumped) loaded.buffers = buffers pfoo2 = uncan(loaded) assert pfoo2() == partial_foo() def test_keyword_only_arguments(): def compute(a, *, b=3): return a * b assert compute(2) == 6 compute_2 = roundtrip(compute) assert compute_2(2) == 6 def test_annotations(): def f(a: int): return a f2 = roundtrip(f) assert f2.__annotations__ == f.__annotations__ ipyparallel-7.1.0/ipyparallel/tests/test_client.py000066400000000000000000000604461412533144600224210ustar00rootroot00000000000000"""Tests for parallel client.py""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import division import os import signal import socket import sys import time from concurrent.futures import Future from datetime import datetime from threading import Thread from unittest import mock import pytest import tornado from IPython import get_ipython from .clienttest import ClusterTestCase from .clienttest import raises_remote from .clienttest import skip_without from .clienttest import wait from ipyparallel import AsyncHubResult from ipyparallel import DirectView from ipyparallel import error from ipyparallel import Reference from ipyparallel.client import client as clientmod from ipyparallel.util import utc @pytest.mark.usefixtures('ipython') class TestClient(ClusterTestCase): engine_count = 4 def test_curve(self): if os.environ.get("IPP_ENABLE_CURVE") == "1": assert not self.client.session.key assert self.client.curve_serverkey assert self.client.curve_secretkey else: assert self.client.session.key def test_ids(self): n = len(self.client.ids) self.add_engines(2) self.assertEqual(len(self.client.ids), n + 2) def test_iter(self): self.minimum_engines(4) engine_ids = [view.targets for view in self.client] self.assertEqual(engine_ids, self.client.ids) def test_view_indexing(self): """test index access for views""" self.minimum_engines(4) targets = self.client._build_targets('all')[-1] v = self.client[:] self.assertEqual(v.targets, targets) t = self.client.ids[2] v = self.client[t] self.assertTrue(isinstance(v, DirectView)) self.assertEqual(v.targets, t) t = self.client.ids[2:4] v = self.client[t] self.assertTrue(isinstance(v, DirectView)) self.assertEqual(v.targets, t) v = self.client[::2] self.assertTrue(isinstance(v, DirectView)) self.assertEqual(v.targets, targets[::2]) v = self.client[1::3] self.assertTrue(isinstance(v, DirectView)) self.assertEqual(v.targets, targets[1::3]) v = self.client[:-3] self.assertTrue(isinstance(v, DirectView)) self.assertEqual(v.targets, targets[:-3]) v = self.client[-1] self.assertTrue(isinstance(v, DirectView)) self.assertEqual(v.targets, targets[-1]) self.assertRaises(TypeError, lambda: self.client[None]) def test_outstanding(self): self.minimum_engines(1) e = self.client[-1] engine_id = self.client._engines[e.targets] ar = e.apply_async(time.sleep, 0.5) msg_id = ar.msg_ids[0] # verify that msg id assert msg_id in self.client.outstanding assert msg_id in self.client._outstanding_dict[engine_id] ar.get() assert msg_id not in self.client.outstanding assert msg_id not in self.client._outstanding_dict[engine_id] def test_lbview_targets(self): """test load_balanced_view targets""" v = self.client.load_balanced_view() self.assertEqual(v.targets, None) v = self.client.load_balanced_view(-1) self.assertEqual(v.targets, [self.client.ids[-1]]) v = self.client.load_balanced_view('all') self.assertEqual(v.targets, None) def test_dview_targets(self): """test direct_view targets""" v = self.client.direct_view() self.assertEqual(v.targets, 'all') v = self.client.direct_view('all') self.assertEqual(v.targets, 'all') v = self.client.direct_view(-1) self.assertEqual(v.targets, self.client.ids[-1]) def test_lazy_all_targets(self): """test lazy evaluation of rc.direct_view('all')""" v = self.client.direct_view() self.assertEqual(v.targets, 'all') def double(x): return x * 2 seq = list(range(100)) ref = [double(x) for x in seq] # add some engines, which should be used self.add_engines(1) n1 = len(self.client.ids) # simple apply r = v.apply_sync(lambda: 1) self.assertEqual(r, [1] * n1) # map goes through remotefunction r = v.map_sync(double, seq) self.assertEqual(r, ref) # add a couple more engines, and try again self.add_engines(2) n2 = len(self.client.ids) self.assertNotEqual(n2, n1) # apply r = v.apply_sync(lambda: 1) self.assertEqual(r, [1] * n2) # map r = v.map_sync(double, seq) self.assertEqual(r, ref) def test_targets(self): """test various valid targets arguments""" build = self.client._build_targets ids = self.client.ids idents, targets = build(None) self.assertEqual(ids, targets) def test_clear(self): """test clear behavior""" self.minimum_engines(2) v = self.client[:] v.block = True v.push(dict(a=5)) v.pull('a') id0 = self.client.ids[-1] self.client.clear(targets=id0, block=True) a = self.client[:-1].get('a') self.assertRaisesRemote(NameError, self.client[id0].get, 'a') self.client.clear(block=True) for i in self.client.ids: self.assertRaisesRemote(NameError, self.client[i].get, 'a') def test_get_result(self): """test getting results from the Hub.""" c = clientmod.Client(profile='iptest') t = c.ids[-1] ar = c[t].apply_async(wait, 1) # give the monitor time to notice the message time.sleep(0.25) ahr = self.client.get_result(ar.msg_ids[0], owner=False) self.assertIsInstance(ahr, AsyncHubResult) self.assertEqual(ahr.get(), ar.get()) ar2 = self.client.get_result(ar.msg_ids[0]) self.assertNotIsInstance(ar2, AsyncHubResult) self.assertEqual(ahr.get(), ar2.get()) ar3 = self.client.get_result(ar2) self.assertEqual(ar3.msg_ids, ar2.msg_ids) ar3 = self.client.get_result([ar2]) self.assertEqual(ar3.msg_ids, ar2.msg_ids) c.close() def test_get_execute_result(self): """test getting execute results from the Hub.""" c = clientmod.Client(profile='iptest') t = c.ids[-1] cell = '\n'.join(['import time', 'time.sleep(0.25)', '5']) ar = c[t].execute("import time; time.sleep(1)", silent=False) # give the monitor time to notice the message time.sleep(0.25) ahr = self.client.get_result(ar.msg_ids[0], owner=False) self.assertIsInstance(ahr, AsyncHubResult) self.assertEqual(ahr.get().execute_result, ar.get().execute_result) ar2 = self.client.get_result(ar.msg_ids[0]) self.assertNotIsInstance(ar2, AsyncHubResult) self.assertEqual(ahr.get(), ar2.get()) c.close() def test_ids_list(self): """test client.ids""" ids = self.client.ids self.assertEqual(ids, self.client._ids) self.assertFalse(ids is self.client._ids) ids.remove(ids[-1]) self.assertNotEqual(ids, self.client._ids) def test_queue_status(self): ids = self.client.ids id0 = ids[0] qs = self.client.queue_status(targets=id0) self.assertTrue(isinstance(qs, dict)) self.assertEqual(sorted(qs.keys()), ['completed', 'queue', 'tasks']) allqs = self.client.queue_status() self.assertTrue(isinstance(allqs, dict)) intkeys = list(allqs.keys()) intkeys.remove('unassigned') print("intkeys", intkeys) intkeys = sorted(intkeys) ids = self.client.ids print("client.ids", ids) ids = sorted(self.client.ids) self.assertEqual(intkeys, ids) unassigned = allqs.pop('unassigned') for eid, qs in allqs.items(): self.assertTrue(isinstance(qs, dict)) self.assertEqual(sorted(qs.keys()), ['completed', 'queue', 'tasks']) def test_shutdown(self): ids = self.client.ids id0 = ids[-1] pid = self.client[id0].apply_sync(os.getpid) self.client.shutdown(id0, block=True) for i in range(150): # give the engine 15 seconds to die if id0 not in self.client.ids: break time.sleep(0.1) self.assertNotIn(id0, self.client.ids) self.assertRaises(IndexError, lambda: self.client[id0]) def test_result_status(self): pass # to be written def test_db_query_dt(self): """test db query by date""" hist = self.client.hub_history() middle = self.client.db_query({'msg_id': hist[len(hist) // 2]})[0] tic = middle['submitted'] before = self.client.db_query({'submitted': {'$lt': tic}}) after = self.client.db_query({'submitted': {'$gte': tic}}) self.assertEqual(len(before) + len(after), len(hist)) for b in before: self.assertTrue(b['submitted'] < tic) for a in after: self.assertTrue(a['submitted'] >= tic) same = self.client.db_query({'submitted': tic}) for s in same: self.assertTrue(s['submitted'] == tic) def test_db_query_keys(self): """test extracting subset of record keys""" found = self.client.db_query( {'msg_id': {'$ne': ''}}, keys=['submitted', 'completed'] ) for rec in found: self.assertEqual(set(rec.keys()), set(['msg_id', 'submitted', 'completed'])) def test_db_query_default_keys(self): """default db_query excludes buffers""" found = self.client.db_query({'msg_id': {'$ne': ''}}) for rec in found: keys = set(rec.keys()) self.assertFalse('buffers' in keys, "'buffers' should not be in: %s" % keys) self.assertFalse( 'result_buffers' in keys, "'result_buffers' should not be in: %s" % keys ) def test_db_query_msg_id(self): """ensure msg_id is always in db queries""" found = self.client.db_query( {'msg_id': {'$ne': ''}}, keys=['submitted', 'completed'] ) for rec in found: self.assertTrue('msg_id' in rec.keys()) found = self.client.db_query({'msg_id': {'$ne': ''}}, keys=['submitted']) for rec in found: self.assertTrue('msg_id' in rec.keys()) found = self.client.db_query({'msg_id': {'$ne': ''}}, keys=['msg_id']) for rec in found: self.assertTrue('msg_id' in rec.keys()) def test_db_query_get_result(self): """pop in db_query shouldn't pop from result itself""" self.client[:].apply_sync(lambda: 1) found = self.client.db_query({'msg_id': {'$ne': ''}}) rc2 = clientmod.Client(profile='iptest') # If this bug is not fixed, this call will hang: ar = rc2.get_result(self.client.history[-1]) ar.wait(2) self.assertTrue(ar.ready()) ar.get() rc2.close() def test_db_query_in(self): """test db query with '$in','$nin' operators""" hist = self.client.hub_history() even = hist[::2] odd = hist[1::2] recs = self.client.db_query({'msg_id': {'$in': even}}) found = [r['msg_id'] for r in recs] self.assertEqual(set(even), set(found)) recs = self.client.db_query({'msg_id': {'$nin': even}}) found = [r['msg_id'] for r in recs] self.assertEqual(set(odd), set(found)) def test_hub_history(self): hist = self.client.hub_history() recs = self.client.db_query({'msg_id': {"$ne": ''}}) recdict = {} for rec in recs: recdict[rec['msg_id']] = rec latest = datetime(1984, 1, 1).replace(tzinfo=utc) for msg_id in hist: rec = recdict[msg_id] newt = rec['submitted'] self.assertTrue(newt >= latest) latest = newt ar = self.client[-1].apply_async(lambda: 1) ar.get() time.sleep(0.25) self.assertEqual(self.client.hub_history()[-1:], ar.msg_ids) def _wait_for_idle(self): """wait for the cluster to become idle, according to the everyone.""" rc = self.client # step 0. wait for local results # this should be sufficient 99% of the time. rc.wait(timeout=5) # step 1. wait for all requests to be noticed # timeout 5s, polling every 100ms msg_ids = set(rc.history) hub_hist = rc.hub_history() for i in range(50): if msg_ids.difference(hub_hist): time.sleep(0.1) hub_hist = rc.hub_history() else: break self.assertEqual(len(msg_ids.difference(hub_hist)), 0) # step 2. wait for all requests to be done # timeout 5s, polling every 100ms qs = rc.queue_status() for i in range(50): if qs['unassigned'] or any( qs[eid]['tasks'] + qs[eid]['queue'] for eid in qs if eid != 'unassigned' ): time.sleep(0.1) qs = rc.queue_status() else: break # ensure Hub up to date: self.assertEqual(qs['unassigned'], 0) for eid in [eid for eid in qs if eid != 'unassigned']: self.assertEqual(qs[eid]['tasks'], 0) self.assertEqual(qs[eid]['queue'], 0) def test_resubmit(self): def f(): import random return random.random() v = self.client.load_balanced_view() ar = v.apply_async(f) r1 = ar.get(1) # give the Hub a chance to notice: self._wait_for_idle() ahr = self.client.resubmit(ar.msg_ids) r2 = ahr.get(1) self.assertFalse(r1 == r2) def test_resubmit_chain(self): """resubmit resubmitted tasks""" v = self.client.load_balanced_view() ar = v.apply_async(lambda x: x, 'x' * 1024) ar.get() self._wait_for_idle() ars = [ar] for i in range(10): ar = ars[-1] ar2 = self.client.resubmit(ar.msg_ids) [ar.get() for ar in ars] def test_resubmit_header(self): """resubmit shouldn't clobber the whole header""" def f(): import random return random.random() v = self.client.load_balanced_view() v.retries = 1 ar = v.apply_async(f) r1 = ar.get(1) # give the Hub a chance to notice: self._wait_for_idle() ahr = self.client.resubmit(ar.msg_ids) ahr.get(1) time.sleep(0.5) records = self.client.db_query( {'msg_id': {'$in': ar.msg_ids + ahr.msg_ids}}, keys='header' ) h1, h2 = [r['header'] for r in records] for key in set(h1.keys()).union(set(h2.keys())): if key in ('msg_id', 'date'): self.assertNotEqual(h1[key], h2[key]) else: self.assertEqual(h1[key], h2[key]) def test_resubmit_aborted(self): def f(): import random return random.random() v = self.client.load_balanced_view() # restrict to one engine, so we can put a sleep # ahead of the task, so it will get aborted eid = self.client.ids[-1] v.targets = [eid] sleep = v.apply_async(time.sleep, 0.5) ar = v.apply_async(f) ar.abort() self.assertRaises(error.TaskAborted, ar.get) # Give the Hub a chance to get up to date: self._wait_for_idle() ahr = self.client.resubmit(ar.msg_ids) r2 = ahr.get(1) def test_resubmit_inflight(self): """resubmit of inflight task""" v = self.client.load_balanced_view() ar = v.apply_async(time.sleep, 1) # give the message a chance to arrive time.sleep(0.2) ahr = self.client.resubmit(ar.msg_ids) ar.get(2) ahr.get(2) def test_resubmit_badkey(self): """ensure KeyError on resubmit of nonexistant task""" self.assertRaisesRemote(KeyError, self.client.resubmit, ['invalid']) def test_purge_hub_results(self): # ensure there are some tasks for i in range(5): self.client[:].apply_sync(lambda: 1) # Wait for the Hub to realise the result is done: # This prevents a race condition, where we # might purge a result the Hub still thinks is pending. self._wait_for_idle() rc2 = clientmod.Client(profile='iptest') hist = self.client.hub_history() ahr = rc2.get_result([hist[-1]]) ahr.wait(10) self.client.purge_hub_results(hist[-1]) newhist = self.client.hub_history() self.assertEqual(len(newhist) + 1, len(hist)) rc2.close() def test_purge_local_results(self): # ensure there are some tasks # purge local results is mostly unnecessary now that we have Futures msg_id = 'asdf' self.client.results[msg_id] = 5 md = self.client.metadata[msg_id] before = len(self.client.results) self.assertEqual(len(self.client.metadata), before) self.client.purge_local_results(msg_id) self.assertLessEqual( len(self.client.results), before - 1, msg="Not removed from results" ) self.assertLessEqual( len(self.client.metadata), before - 1, msg="Not removed from metadata" ) def test_purge_local_results_outstanding(self): v = self.client[-1] ar = v.apply_async(time.sleep, 1) with self.assertRaises(RuntimeError): self.client.purge_local_results(ar) ar.get() self.client.purge_local_results(ar) def test_purge_all_local_results_outstanding(self): v = self.client[-1] ar = v.apply_async(time.sleep, 1) with self.assertRaises(RuntimeError): self.client.purge_local_results('all') ar.get() self.client.purge_local_results('all') def test_purge_all_hub_results(self): self.client.purge_hub_results('all') hist = self.client.hub_history() self.assertEqual(len(hist), 0) def test_purge_all_local_results(self): self.client.purge_local_results('all') self.assertEqual(len(self.client.results), 0, msg="Results not empty") self.assertEqual(len(self.client.metadata), 0, msg="metadata not empty") def test_purge_all_results(self): # ensure there are some tasks for i in range(5): self.client[:].apply_sync(lambda: 1) assert self.client.wait(timeout=10) self._wait_for_idle() self.client.purge_results('all') self.assertEqual(len(self.client.results), 0, msg="Results not empty") self.assertEqual(len(self.client.metadata), 0, msg="metadata not empty") hist = self.client.hub_history() self.assertEqual(len(hist), 0, msg="hub history not empty") def test_purge_everything(self): # ensure there are some tasks for i in range(5): self.client[:].apply_sync(lambda: 1) self.client.wait(timeout=10) self._wait_for_idle() self.client.purge_everything() # The client results self.assertEqual(len(self.client.results), 0, msg="Results not empty") self.assertEqual(len(self.client.metadata), 0, msg="metadata not empty") # The client "bookkeeping" self.assertEqual( len(self.client.session.digest_history), 0, msg="session digest not empty" ) self.assertEqual(len(self.client.history), 0, msg="client history not empty") # the hub results hist = self.client.hub_history() self.assertEqual(len(hist), 0, msg="hub history not empty") def test_activate_on_init(self): ip = get_ipython() magics = ip.magics_manager.magics c = self.connect_client() self.assertTrue('px' in magics['line']) self.assertTrue('px' in magics['cell']) c.close() def test_activate(self): ip = get_ipython() magics = ip.magics_manager.magics v0 = self.client.activate(-1, '0') self.assertTrue('px0' in magics['line']) self.assertTrue('px0' in magics['cell']) self.assertEqual(v0.targets, self.client.ids[-1]) v0 = self.client.activate('all', 'all') self.assertTrue('pxall' in magics['line']) self.assertTrue('pxall' in magics['cell']) self.assertEqual(v0.targets, 'all') def test_wait_interactive(self): ar = self.client[-1].apply_async(lambda: 1) self.client.wait_interactive() self.assertEqual(self.client.outstanding, set()) def test_await_future(self): f = Future() def finish_later(): time.sleep(0.1) f.set_result('future') Thread(target=finish_later).start() assert self.client.wait([f]) assert f.done() assert f.result() == 'future' @skip_without('distributed') @pytest.mark.skipif( sys.version_info[:2] <= (3, 5), reason="become_dask doesn't work on Python 3.5" ) @pytest.mark.skipif( tornado.version_info[:2] < (5,), reason="become_dask doesn't work with tornado 4", ) def test_become_dask(self): executor = self.client.become_dask() reprs = self.client[:].apply_sync(repr, Reference('distributed_worker')) for r in reprs: self.assertIn("Worker", r) squares = executor.map(lambda x: x * x, range(10)) tot = executor.submit(sum, squares) self.assertEqual(tot.result(), 285) # cleanup executor.close() self.client.stop_dask() ar = self.client[:].apply_async(lambda x: x, Reference('distributed_worker')) self.assertRaisesRemote(NameError, ar.get) def test_warning_on_hostname_match(self): location = socket.gethostname() with mock.patch('ipyparallel.client.client.is_local_ip', lambda x: False): with mock.patch('socket.gethostname', lambda: location[0:-1]): with pytest.warns(RuntimeWarning): # should trigger warning c = self.connect_client() c.close() with mock.patch('socket.gethostname', lambda: location): with pytest.warns(None) as record: # should not trigger warning c = self.connect_client() # only capture runtime warnings runtime_warnings = [ w for w in record if isinstance(w.message, RuntimeWarning) ] assert len(runtime_warnings) == 0, str( [str(w) for w in runtime_warnings] ) c.close() def test_local_ip_true_doesnt_trigger_warning(self): with mock.patch('ipyparallel.client.client.is_local_ip', lambda x: True): with pytest.warns(None) as record: c = self.connect_client() # only capture runtime warnings runtime_warnings = [ w for w in record if isinstance(w.message, RuntimeWarning) ] assert len(runtime_warnings) == 0, str([str(w) for w in runtime_warnings]) c.close() def test_wait_for_engines(self): n = len(self.client) assert self.client.wait_for_engines(n) is None f = self.client.wait_for_engines(n, block=False) assert isinstance(f, Future) assert f.done() with pytest.raises(TimeoutError): self.client.wait_for_engines(n + 1, timeout=0.1) f = self.client.wait_for_engines(n + 1, timeout=10, block=False) self.add_engines(1) assert f.result() is None @pytest.mark.skipif( sys.platform.startswith("win"), reason="Signal tests don't pass on Windows yet" ) def test_signal_engines(self): view = self.client[:] if sys.platform.startswith("win"): signame = 'CTRL_C_EVENT' else: signame = 'SIGINT' signum = getattr(signal, signame) for sig in (signum, signame): ar = view.apply_async(time.sleep, 10) # FIXME: use status:busy to wait for tasks to start time.sleep(1) self.client.send_signal(sig, block=True) with raises_remote(KeyboardInterrupt): ar.get() # make sure they were all interrupted for r in ar.get(return_exceptions=True): assert isinstance(r, error.RemoteError) ipyparallel-7.1.0/ipyparallel/tests/test_cluster.py000066400000000000000000000263531412533144600226230ustar00rootroot00000000000000import asyncio import json import logging import os import signal import sys import tempfile import time from pathlib import Path from unittest import mock import pytest from traitlets.config import Config import ipyparallel as ipp from .clienttest import raises_remote from ipyparallel import cluster from ipyparallel.cluster.launcher import find_launcher_class _timeout = 30 def _raise_interrupt(*frame_info): raise KeyboardInterrupt() def _prepare_signal(): """Register signal handler to test with Registers SIGUSR1 to raise KeyboardInterrupt where available """ if hasattr(signal, 'SIGUSR1'): signal.signal(signal.SIGUSR1, _raise_interrupt) # returns the remote value of the signal return int(TEST_SIGNAL) try: TEST_SIGNAL = signal.SIGUSR1 except AttributeError: # Windows TEST_SIGNAL = signal.CTRL_C_EVENT async def test_cluster_id(Cluster): cluster_ids = set() for i in range(3): cluster = Cluster() cluster_ids.add(cluster.cluster_id) assert len(cluster_ids) == 3 cluster = Cluster(cluster_id='abc') assert cluster.cluster_id == 'abc' async def test_ipython_log(ipython): c = cluster.Cluster(parent=ipython) assert c.log.name == f"{cluster.Cluster.__module__}.{c.cluster_id}" assert len(c.log.handlers) == 1 assert c.log.handlers[0].stream is sys.stdout @pytest.fixture def engine_launcher_class(): return 'Local' async def test_start_stop_controller(Cluster): cluster = Cluster() await cluster.start_controller() with pytest.raises(RuntimeError): await cluster.start_controller() assert cluster.config is not None assert cluster.controller.config is cluster.config assert cluster.controller is not None proc = cluster.controller.process assert proc.is_running() with await cluster.connect_client() as rc: assert rc.queue_status() == {'unassigned': 0} await cluster.stop_controller() proc.wait(timeout=3) assert cluster.controller is None # stop is idempotent await cluster.stop_controller() # TODO: test file cleanup async def test_start_stop_engines(Cluster, engine_launcher_class): cluster = Cluster(engine_launcher_class=engine_launcher_class) await cluster.start_controller() n = 2 engine_set_id = await cluster.start_engines(n) assert engine_set_id in cluster.engines engine_set = cluster.engines[engine_set_id] launcher_class = find_launcher_class(engine_launcher_class, "engine") assert isinstance(engine_set, launcher_class) with await cluster.connect_client() as rc: rc.wait_for_engines(n, timeout=_timeout) await cluster.stop_engines(engine_set_id) assert cluster.engines == {} with pytest.raises(KeyError): await cluster.stop_engines(engine_set_id) await cluster.stop_controller() async def test_start_stop_cluster(Cluster, engine_launcher_class): n = 2 cluster = Cluster(engine_launcher_class=engine_launcher_class, n=n) await cluster.start_cluster() controller = cluster.controller assert controller is not None assert len(cluster.engines) == 1 with await cluster.connect_client() as rc: rc.wait_for_engines(n, timeout=_timeout) await cluster.stop_cluster() assert cluster.controller is None assert cluster.engines == {} @pytest.mark.skipif( sys.platform.startswith("win"), reason="Signal tests don't pass on Windows yet" ) async def test_signal_engines(request, Cluster, engine_launcher_class): cluster = Cluster(engine_launcher_class=engine_launcher_class) await cluster.start_controller() engine_set_id = await cluster.start_engines(n=2) rc = await cluster.connect_client() request.addfinalizer(rc.close) rc.wait_for_engines(2) # seems to be a problem if we start too soon... await asyncio.sleep(1) # ensure responsive rc[:].apply_async(lambda: None).get(timeout=_timeout) # register signal handler signals = rc[:].apply_async(_prepare_signal).get(timeout=_timeout) # get test signal from engines in case of cross-platform mismatch, # e.g. SIGUSR1 on mac (30) -> linux (10) test_signal = signals[0] # submit request to be interrupted ar = rc[:].apply_async(time.sleep, 3) # wait for it to be running await asyncio.sleep(0.5) # send signal await cluster.signal_engines(test_signal, engine_set_id) # wait for result, which should raise KeyboardInterrupt with raises_remote(KeyboardInterrupt) as e: ar.get(timeout=_timeout) rc.close() await cluster.stop_engines() await cluster.stop_controller() async def test_restart_engines(Cluster, engine_launcher_class): n = 2 async with Cluster(engine_launcher_class=engine_launcher_class, n=n) as rc: cluster = rc.cluster engine_set_id = next(iter(cluster.engines)) engine_set = cluster.engines[engine_set_id] assert rc.ids[:n] == list(range(n)) before_pids = rc[:].apply_sync(os.getpid) await cluster.restart_engines() # wait for unregister while any(eid in rc.ids for eid in range(n)): await asyncio.sleep(0.1) # wait for register rc.wait_for_engines(n, timeout=_timeout) after_pids = rc[:].apply_sync(os.getpid) assert set(after_pids).intersection(before_pids) == set() async def test_async_with(Cluster): async with Cluster(n=5) as rc: assert sorted(rc.ids) == list(range(5)) rc[:]['a'] = 5 assert rc[:]['a'] == [5] * 5 def test_sync_with(Cluster): with Cluster(log_level=10, n=5) as rc: assert sorted(rc.ids) == list(range(5)) rc[:]['a'] = 5 assert rc[:]['a'] == [5] * 5 def test_load_profile(tmpdir): profile_dir = tmpdir.join("profile").mkdir() # config cases: # - only in profile config (used) # - in profile config and direct config (direct used) # - in profile config and kwargs (kwargs used) with profile_dir.join("ipcluster_config.json").open("w") as f: json.dump( { "Cluster": { "controller_args": ["--from-profile"], "n": 5, "engine_timeout": 10, } }, f, ) print(profile_dir.listdir()) config = Config() config.Cluster.engine_timeout = 20 c = cluster.Cluster(profile_dir=str(profile_dir), n=10, config=config) print(c.config) assert c.profile_dir == str(profile_dir) assert c.controller_args == ['--from-profile'] # from profile assert c.engine_timeout == 20 # from config assert c.n == 10 # from kwarg @pytest.mark.parametrize( "classname, expected_class", [ ("MPI", cluster.launcher.MPIEngineSetLauncher), ("SGE", cluster.launcher.SGEEngineSetLauncher), ( "ipyparallel.cluster.launcher.LocalEngineSetLauncher", cluster.launcher.LocalEngineSetLauncher, ), ], ) def test_cluster_abbreviations(classname, expected_class): c = cluster.Cluster(engine_launcher_class=classname) assert c.engine_launcher_class is expected_class async def test_cluster_repr(Cluster): tmp = tempfile.gettempdir() c = Cluster(cluster_id="test", profile_dir=tmp) assert repr(c) == f"" await c.start_controller() assert ( repr(c) == f")>" ) await c.start_engines(1, 'engineid') assert ( repr(c) == f", engine_sets=['engineid'])>" ) async def test_cluster_manager(): m = cluster.ClusterManager() assert m.clusters == {} tmp = tempfile.gettempdir() key, c = m.new_cluster(profile_dir=tmp) assert c.profile_dir == tmp assert m.get_cluster(key) is c with pytest.raises(KeyError): m.get_cluster("nosuchcluster") with pytest.raises(KeyError): m.new_cluster(cluster_id=c.cluster_id, profile_dir=c.profile_dir) assert list(m.clusters) == [key] m.remove_cluster(key) with pytest.raises(KeyError): m.remove_cluster("nosuchcluster") async def test_to_from_dict(Cluster, engine_launcher_class): cluster = Cluster(engine_launcher_class=engine_launcher_class, n=2) print(cluster.config, cluster.controller_args) async with cluster as rc: d = cluster.to_dict() cluster2 = ipp.Cluster.from_dict(d) assert not cluster2.shutdown_atexit assert cluster2.controller is not None assert cluster2.controller.process.pid == cluster.controller.process.pid assert list(cluster2.engines) == list(cluster.engines) es1 = next(iter(cluster.engines.values())) es2 = next(iter(cluster2.engines.values())) # ensure responsive rc[:].apply_async(lambda: None).get(timeout=_timeout) if not sys.platform.startswith("win"): # signal tests doesn't work yet on Windows # register signal handler signals = rc[:].apply_async(_prepare_signal).get(timeout=_timeout) # get test signal from engines in case of cross-platform mismatch, # e.g. SIGUSR1 on mac (30) -> linux (10) test_signal = signals[0] # submit request to be interrupted ar = rc[:].apply_async(time.sleep, 3) await asyncio.sleep(0.5) # send signal await cluster2.signal_engines(test_signal) # wait for result, which should raise KeyboardInterrupt with raises_remote(KeyboardInterrupt) as e: ar.get(timeout=_timeout) assert es1.n == es2.n assert cluster2.engine_launcher_class is cluster.engine_launcher_class # shutdown from cluster2, shouldn't raise in cluster1 await cluster2.stop_cluster() async def test_default_from_file(Cluster): cluster = Cluster(n=1, profile="default", cluster_id="") async with cluster: cluster2 = ipp.Cluster.from_file() assert cluster2.cluster_file == cluster.cluster_file with await cluster.connect_client() as rc: assert len(rc) == 1 async def test_cluster_manager_notice_stop(Cluster): cm = cluster.ClusterManager(log=logging.getLogger()) cm.load_clusters() c = Cluster(n=1, log=cm.log) key = cm._cluster_key(c) assert key not in cm.clusters await c.start_cluster() cm.load_clusters() assert key in cm.clusters c_copy = cm.clusters[key] await c.stop_cluster() # refresh list, cleans out stopped clusters # can take some time to notice tic = time.perf_counter() deadline = time.perf_counter() + _timeout while time.perf_counter() < deadline and key in cm.clusters: await asyncio.sleep(0.2) cm.load_clusters() assert key not in cm.clusters async def test_wait_for_engines_crash(Cluster): """wait_for_engines is cancelled when the engines stop""" c = Cluster(n=2, log_level=10) crash_on_startup = str(Path(__file__).parent.joinpath("_test_startup_crash.py")) with mock.patch.dict(os.environ, {"PYTHONSTARTUP": crash_on_startup}): c.start_cluster_sync() rc = c.connect_client_sync() with pytest.raises(ipp.error.EngineError): rc.wait_for_engines(3, timeout=20) ipyparallel-7.1.0/ipyparallel/tests/test_db.py000066400000000000000000000260101412533144600215150ustar00rootroot00000000000000"""Tests for db backends""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import division import logging import os import tempfile import time from datetime import datetime from datetime import timedelta from unittest import TestCase from jupyter_client.session import Session from tornado.ioloop import IOLoop from ipyparallel import util from ipyparallel.controller.dictdb import DictDB from ipyparallel.controller.hub import init_record from ipyparallel.controller.sqlitedb import SQLiteDB from ipyparallel.util import utc class TaskDBTest: def setUp(self): self.session = Session() self.db = self.create_db() self.load_records(16) def create_db(self): raise NotImplementedError def load_records(self, n=1, buffer_size=100): """load n records for testing""" # sleep 1/10 s, to ensure timestamp is different to previous calls time.sleep(0.1) msg_ids = [] for i in range(n): msg = self.session.msg('apply_request', content=dict(a=5)) msg['buffers'] = [os.urandom(buffer_size)] rec = init_record(msg) msg_id = msg['header']['msg_id'] msg_ids.append(msg_id) self.db.add_record(msg_id, rec) return msg_ids def test_add_record(self): before = self.db.get_history() self.load_records(5) after = self.db.get_history() self.assertEqual(len(after), len(before) + 5) self.assertEqual(after[:-5], before) def test_drop_record(self): msg_id = self.load_records()[-1] rec = self.db.get_record(msg_id) self.db.drop_record(msg_id) self.assertRaises(KeyError, self.db.get_record, msg_id) def _round_to_millisecond(self, dt): """necessary because mongodb rounds microseconds""" micro = dt.microsecond extra = int(str(micro)[-3:]) return dt - timedelta(microseconds=extra) def test_update_record(self): now = self._round_to_millisecond(util.utcnow()) msg_id = self.db.get_history()[-1] rec1 = self.db.get_record(msg_id) data = {'stdout': 'hello there', 'completed': now} self.db.update_record(msg_id, data) rec2 = self.db.get_record(msg_id) self.assertEqual(rec2['stdout'], 'hello there') self.assertEqual(rec2['completed'], now) rec1.update(data) self.assertEqual(rec1, rec2) # def test_update_record_bad(self): # """test updating nonexistant records""" # msg_id = str(uuid.uuid4()) # data = {'stdout': 'hello there'} # self.assertRaises(KeyError, self.db.update_record, msg_id, data) def test_find_records_dt(self): """test finding records by date""" hist = self.db.get_history() middle = self.db.get_record(hist[len(hist) // 2]) tic = middle['submitted'] before = self.db.find_records({'submitted': {'$lt': tic}}) after = self.db.find_records({'submitted': {'$gte': tic}}) self.assertEqual(len(before) + len(after), len(hist)) for b in before: self.assertLess(b['submitted'], tic) for a in after: self.assertGreaterEqual(a['submitted'], tic) same = self.db.find_records({'submitted': tic}) for s in same: self.assertEqual(s['submitted'], tic) def test_find_records_keys(self): """test extracting subset of record keys""" found = self.db.find_records( {'msg_id': {'$ne': ''}}, keys=['submitted', 'completed'] ) for rec in found: self.assertEqual(set(rec.keys()), set(['msg_id', 'submitted', 'completed'])) def test_find_records_msg_id(self): """ensure msg_id is always in found records""" found = self.db.find_records( {'msg_id': {'$ne': ''}}, keys=['submitted', 'completed'] ) for rec in found: self.assertTrue('msg_id' in rec.keys()) found = self.db.find_records({'msg_id': {'$ne': ''}}, keys=['submitted']) for rec in found: self.assertTrue('msg_id' in rec.keys()) found = self.db.find_records({'msg_id': {'$ne': ''}}, keys=['msg_id']) for rec in found: self.assertTrue('msg_id' in rec.keys()) def test_find_records_in(self): """test finding records with '$in','$nin' operators""" hist = self.db.get_history() even = hist[::2] odd = hist[1::2] recs = self.db.find_records({'msg_id': {'$in': even}}) found = [r['msg_id'] for r in recs] self.assertEqual(set(even), set(found)) recs = self.db.find_records({'msg_id': {'$nin': even}}) found = [r['msg_id'] for r in recs] self.assertEqual(set(odd), set(found)) def test_get_history(self): msg_ids = self.db.get_history() latest = datetime(1984, 1, 1).replace(tzinfo=utc) for msg_id in msg_ids: rec = self.db.get_record(msg_id) newt = rec['submitted'] self.assertTrue(newt >= latest) latest = newt msg_id = self.load_records(1)[-1] self.assertEqual(self.db.get_history()[-1], msg_id) def test_datetime(self): """get/set timestamps with datetime objects""" msg_id = self.db.get_history()[-1] rec = self.db.get_record(msg_id) self.assertTrue(isinstance(rec['submitted'], datetime)) self.db.update_record(msg_id, dict(completed=util.utcnow())) rec = self.db.get_record(msg_id) self.assertTrue(isinstance(rec['completed'], datetime)) def test_drop_matching(self): msg_ids = self.load_records(10) query = {'msg_id': {'$in': msg_ids}} self.db.drop_matching_records(query) recs = self.db.find_records(query) self.assertEqual(len(recs), 0) def test_null(self): """test None comparison queries""" msg_ids = self.load_records(10) query = {'msg_id': None} recs = self.db.find_records(query) self.assertEqual(len(recs), 0) query = {'msg_id': {'$ne': None}} recs = self.db.find_records(query) self.assertTrue(len(recs) >= 10) def test_pop_safe_get(self): """editing query results shouldn't affect record [get]""" msg_id = self.db.get_history()[-1] rec = self.db.get_record(msg_id) rec.pop('buffers') rec['garbage'] = 'hello' rec['header']['msg_id'] = 'fubar' rec2 = self.db.get_record(msg_id) self.assertTrue('buffers' in rec2) self.assertFalse('garbage' in rec2) self.assertEqual(rec2['header']['msg_id'], msg_id) def test_pop_safe_find(self): """editing query results shouldn't affect record [find]""" msg_id = self.db.get_history()[-1] rec = self.db.find_records({'msg_id': msg_id})[0] rec.pop('buffers') rec['garbage'] = 'hello' rec['header']['msg_id'] = 'fubar' rec2 = self.db.find_records({'msg_id': msg_id})[0] self.assertTrue('buffers' in rec2) self.assertFalse('garbage' in rec2) self.assertEqual(rec2['header']['msg_id'], msg_id) def test_pop_safe_find_keys(self): """editing query results shouldn't affect record [find+keys]""" msg_id = self.db.get_history()[-1] rec = self.db.find_records({'msg_id': msg_id}, keys=['buffers', 'header'])[0] rec.pop('buffers') rec['garbage'] = 'hello' rec['header']['msg_id'] = 'fubar' rec2 = self.db.find_records({'msg_id': msg_id})[0] self.assertTrue('buffers' in rec2) self.assertFalse('garbage' in rec2) self.assertEqual(rec2['header']['msg_id'], msg_id) class TestDictBackend(TaskDBTest, TestCase): def create_db(self): return DictDB() def test_cull_count(self): self.db = self.create_db() # skip the load-records init from setUp self.db.record_limit = 20 self.db.cull_fraction = 0.2 self.load_records(20) self.assertEqual(len(self.db.get_history()), 20) self.load_records(1) # 0.2 * 20 = 4, 21 - 4 = 17 self.assertEqual(len(self.db.get_history()), 17) self.load_records(3) self.assertEqual(len(self.db.get_history()), 20) self.load_records(1) self.assertEqual(len(self.db.get_history()), 17) for i in range(25): self.load_records(1) self.assertTrue(len(self.db.get_history()) >= 17) self.assertTrue(len(self.db.get_history()) <= 20) def test_cull_size(self): self.db = self.create_db() # skip the load-records init from setUp self.db.size_limit = 1000 self.db.cull_fraction = 0.2 self.load_records(100, buffer_size=10) self.assertEqual(len(self.db.get_history()), 100) self.load_records(1, buffer_size=0) self.assertEqual(len(self.db.get_history()), 101) self.load_records(1, buffer_size=1) # 0.2 * 100 = 20, 101 - 20 = 81 self.assertEqual(len(self.db.get_history()), 81) def test_cull_size_drop(self): """dropping records updates tracked buffer size""" self.db = self.create_db() # skip the load-records init from setUp self.db.size_limit = 1000 self.db.cull_fraction = 0.2 self.load_records(100, buffer_size=10) self.assertEqual(len(self.db.get_history()), 100) self.db.drop_record(self.db.get_history()[-1]) self.assertEqual(len(self.db.get_history()), 99) self.load_records(1, buffer_size=5) self.assertEqual(len(self.db.get_history()), 100) self.load_records(1, buffer_size=5) self.assertEqual(len(self.db.get_history()), 101) self.load_records(1, buffer_size=1) self.assertEqual(len(self.db.get_history()), 81) def test_cull_size_update(self): """updating records updates tracked buffer size""" self.db = self.create_db() # skip the load-records init from setUp self.db.size_limit = 1000 self.db.cull_fraction = 0.2 self.load_records(100, buffer_size=10) self.assertEqual(len(self.db.get_history()), 100) msg_id = self.db.get_history()[-1] self.db.update_record(msg_id, dict(result_buffers=[os.urandom(10)], buffers=[])) self.assertEqual(len(self.db.get_history()), 100) self.db.update_record(msg_id, dict(result_buffers=[os.urandom(11)], buffers=[])) self.assertEqual(len(self.db.get_history()), 79) class TestSQLiteBackend(TaskDBTest, TestCase): def setUp(self): # make a new IOLoop IOLoop().make_current() self.temp_db = tempfile.NamedTemporaryFile(suffix='.db').name super(TestSQLiteBackend, self).setUp() def create_db(self): location, fname = os.path.split(self.temp_db) log = logging.getLogger('test') log.setLevel(logging.CRITICAL) return SQLiteDB(location=location, filename=fname, log=log) def tearDown(self): self.db.close() try: os.remove(self.temp_db) except: pass IOLoop.clear_current() ipyparallel-7.1.0/ipyparallel/tests/test_dependency.py000066400000000000000000000071051412533144600232520ustar00rootroot00000000000000"""Tests for dependency.py""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import ipyparallel as ipp from .clienttest import ClusterTestCase from ipyparallel.serialize import can from ipyparallel.serialize import uncan from ipyparallel.util import interactive @ipp.require('time') def wait(n): time.sleep(n) # noqa: F821 return n @ipp.interactive def func(x): return x * x mixed = list(map(str, range(10))) completed = list(map(str, range(0, 10, 2))) failed = list(map(str, range(1, 10, 2))) class DependencyTest(ClusterTestCase): def setUp(self): ClusterTestCase.setUp(self) self.user_ns = {'__builtins__': __builtins__} self.view = self.client.load_balanced_view() self.dview = self.client[-1] self.succeeded = set(map(str, range(0, 25, 2))) self.failed = set(map(str, range(1, 25, 2))) def assertMet(self, dep): self.assertTrue( dep.check(self.succeeded, self.failed), "Dependency should be met" ) def assertUnmet(self, dep): self.assertFalse( dep.check(self.succeeded, self.failed), "Dependency should not be met" ) def assertUnreachable(self, dep): self.assertTrue( dep.unreachable(self.succeeded, self.failed), "Dependency should be unreachable", ) def assertReachable(self, dep): self.assertFalse( dep.unreachable(self.succeeded, self.failed), "Dependency should be reachable", ) def cancan(self, f): """decorator to pass through canning into self.user_ns""" return uncan(can(f), self.user_ns) def test_require_imports(self): """test that @require imports names""" @self.cancan @ipp.require('base64') @interactive def encode(arg): return base64.b64encode(arg) # noqa: F821 # must pass through canning to properly connect namespaces self.assertEqual(encode(b'foo'), b'Zm9v') def test_success_only(self): dep = ipp.Dependency(mixed, success=True, failure=False) self.assertUnmet(dep) self.assertUnreachable(dep) dep.all = False self.assertMet(dep) self.assertReachable(dep) dep = ipp.Dependency(completed, success=True, failure=False) self.assertMet(dep) self.assertReachable(dep) dep.all = False self.assertMet(dep) self.assertReachable(dep) def test_failure_only(self): dep = ipp.Dependency(mixed, success=False, failure=True) self.assertUnmet(dep) self.assertUnreachable(dep) dep.all = False self.assertMet(dep) self.assertReachable(dep) dep = ipp.Dependency(completed, success=False, failure=True) self.assertUnmet(dep) self.assertUnreachable(dep) dep.all = False self.assertUnmet(dep) self.assertUnreachable(dep) def test_require_function(self): @ipp.interactive def bar(a): return func(a) @ipp.require(func) @ipp.interactive def bar2(a): return func(a) self.client[:].clear() self.assertRaisesRemote(NameError, self.view.apply_sync, bar, 5) ar = self.view.apply_async(bar2, 5) self.assertEqual(ar.get(5), func(5)) def test_require_object(self): @ipp.require(foo=func) @ipp.interactive def bar(a): return foo(a) # noqa: F821 ar = self.view.apply_async(bar, 5) self.assertEqual(ar.get(5), func(5)) ipyparallel-7.1.0/ipyparallel/tests/test_executor.py000066400000000000000000000034161412533144600227730ustar00rootroot00000000000000"""Tests for Executor API""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import time import types from tornado.ioloop import IOLoop from .clienttest import ClusterTestCase from ipyparallel.client.view import LoadBalancedView def wait(n): import time time.sleep(n) return n def echo(x): return x class AsyncResultTest(ClusterTestCase): def resolve(self, future): return IOLoop().run_sync(lambda: future) def test_client_executor(self): executor = self.client.executor() assert isinstance(executor.view, LoadBalancedView) f = executor.submit(lambda x: 2 * x, 5) r = self.resolve(f) self.assertEqual(r, 10) def test_view_executor(self): view = self.client.load_balanced_view() executor = view.executor self.assertIs(executor.view, view) def test_executor_submit(self): view = self.client.load_balanced_view() executor = view.executor f = executor.submit(lambda x, y: x * y, 2, 3) r = self.resolve(f) self.assertEqual(r, 6) def test_executor_map(self): view = self.client.load_balanced_view() executor = view.executor gen = executor.map(lambda x: x, range(5)) assert isinstance(gen, types.GeneratorType) for i, r in enumerate(gen): assert i == r def test_executor_context(self): view = self.client.load_balanced_view() executor = view.executor with executor: f = executor.submit(time.sleep, 0.5) assert not f.done() # Executor context calls shutdown # shutdown doesn't shutdown engines, # but it should at least wait for results to finish assert f.done() ipyparallel-7.1.0/ipyparallel/tests/test_joblib.py000066400000000000000000000026341412533144600223770ustar00rootroot00000000000000from unittest import mock import pytest import ipyparallel as ipp from .clienttest import add_engines from .clienttest import ClusterTestCase try: from joblib import Parallel, delayed from ipyparallel.client._joblib import IPythonParallelBackend # noqa except (ImportError, TypeError): have_joblib = False else: have_joblib = True def neg(x): return -1 * x class TestJobLib(ClusterTestCase): def setUp(self): if not have_joblib: pytest.skip("Requires joblib >= 0.10") super(TestJobLib, self).setUp() add_engines(1, total=True) def test_default_backend(self): """ipyparallel.register_joblib_backend() registers default backend""" ipp.register_joblib_backend() with mock.patch.object(ipp.Client, "__new__", lambda *a, **kw: self.client): p = Parallel(backend='ipyparallel') assert p._backend._view.client is self.client def test_register_backend(self): view = self.client.load_balanced_view() view.register_joblib_backend('view') p = Parallel(backend='view') self.assertIs(p._backend._view, view) def test_joblib_backend(self): view = self.client.load_balanced_view() view.register_joblib_backend('view') p = Parallel(backend='view') result = p(delayed(neg)(i) for i in range(10)) self.assertEqual(result, [neg(i) for i in range(10)]) ipyparallel-7.1.0/ipyparallel/tests/test_launcher.py000066400000000000000000000113451412533144600227360ustar00rootroot00000000000000"""Tests for launchers Doesn't actually start any subprocesses, but goes through the motions of constructing objects, which should test basic config. """ import logging import os import sys import time from subprocess import Popen import entrypoints import pytest from traitlets.config import Config from ipyparallel.cluster import launcher as launcher_mod # ------------------------------------------------------------------------------- # TestCase Mixins # ------------------------------------------------------------------------------- @pytest.fixture() def profile_dir(tmpdir): return str(tmpdir.mkdir("profile_foo")) @pytest.fixture() def cluster_id(): return str(time.time()) @pytest.fixture() def work_dir(tmpdir): return str(tmpdir.mkdir("work")) @pytest.fixture() def build_launcher(work_dir, profile_dir, cluster_id): default_kwargs = dict( work_dir=work_dir, profile_dir=profile_dir, cluster_id=cluster_id, log=logging.getLogger(), ) def build_launcher(Launcher, **kwargs): kw = {} kw.update(default_kwargs) kw.update(kwargs) return Launcher(**kw) return build_launcher @pytest.fixture(params=launcher_mod.all_launchers) def launcher(request, build_launcher): return build_launcher(Launcher=request.param) BATCH_LAUNCHERS = [ cls for cls in launcher_mod.all_launchers if issubclass( cls, (launcher_mod.BatchControllerLauncher, launcher_mod.BatchEngineSetLauncher) ) ] @pytest.fixture(params=BATCH_LAUNCHERS) def batch_launcher(request, build_launcher): return build_launcher(Launcher=request.param) SSH_LAUNCHERS = [ l for l in launcher_mod.all_launchers if issubclass(l, launcher_mod.SSHLauncher) and l is not launcher_mod.SSHLauncher ] @pytest.fixture(params=SSH_LAUNCHERS) def ssh_launcher(request, build_launcher): return build_launcher(Launcher=request.param) WINHPC_LAUNCHERS = [ l for l in launcher_mod.all_launchers if issubclass(l, launcher_mod.WindowsHPCLauncher) and l is not launcher_mod.WindowsHPCLauncher ] @pytest.fixture(params=WINHPC_LAUNCHERS) def winhpc_launcher(request, build_launcher): return build_launcher(Launcher=request.param) def test_profile_dir_arg(launcher, profile_dir): assert "--profile-dir" in launcher.cluster_args arg_idx = launcher.cluster_args.index("--profile-dir") assert profile_dir in launcher.cluster_args assert launcher.cluster_args[arg_idx + 1] == profile_dir def test_cluster_id_arg(launcher, cluster_id): assert "--cluster-id" in launcher.cluster_args arg_idx = launcher.cluster_args.index("--cluster-id") assert cluster_id in launcher.cluster_args assert launcher.cluster_args[arg_idx + 1] == cluster_id def test_batch_template(batch_launcher, work_dir): launcher = batch_launcher batch_file = os.path.join(work_dir, launcher.batch_file_name) assert launcher.batch_file == batch_file launcher.write_batch_script(1) assert os.path.isfile(batch_file) def test_winhpc_template(winhpc_launcher, work_dir): launcher = winhpc_launcher job_file = os.path.join(work_dir, launcher.job_file_name) print(job_file) assert launcher.job_file == job_file launcher.write_job_file(1) assert os.path.isfile(job_file) def test_ssh_remote_profile_dir(ssh_launcher, profile_dir): launcher = ssh_launcher assert launcher.remote_profile_dir == profile_dir cfg = Config() cfg[launcher.__class__.__name__].remote_profile_dir = "foo" launcher.update_config(cfg) assert launcher.remote_profile_dir == "foo" def test_ssh_waitpid(capsys): proc = Popen([sys.executable, '-c', 'import time; time.sleep(1)']) pid = proc.pid def _wait_one(timeout): launcher_mod.ssh_waitpid(pid, timeout=timeout) captured = capsys.readouterr() return launcher_mod._ssh_outputs(captured.out) # first run, it's alive assert _wait_one(timeout=0.1) == {"process_running": "1"} # second run, process exits assert _wait_one(timeout=5) == {"process_running": "0", "exit_code": "-1"} # third run, process has already exited assert _wait_one(timeout=5) == {"process_running": "0", "exit_code": "-1"} @pytest.mark.parametrize("kind", ("controller", "engine")) def test_entrypoints(kind): group_name = f"ipyparallel.{kind}_launchers" group = entrypoints.get_group_named(group_name) assert len(group) > 2 for key, entrypoint in group.items(): # verify entrypoints are valid cls = entrypoint.load() # verify find method assert launcher_mod.find_launcher_class(key, kind=kind) is cls # verify abbreviation roundtrip abbreviation = launcher_mod.abbreviate_launcher_class(cls) assert abbreviation == key ipyparallel-7.1.0/ipyparallel/tests/test_lbview.py000066400000000000000000000211111412533144600224150ustar00rootroot00000000000000# -*- coding: utf-8 -*- """test LoadBalancedView objects""" import time from itertools import count import ipyparallel as ipp from .clienttest import ClusterTestCase from .clienttest import crash from ipyparallel import error class TestLoadBalancedView(ClusterTestCase): def setUp(self): ClusterTestCase.setUp(self) self.view = self.client.load_balanced_view() def test_z_crash(self): """test graceful handling of engine death (balanced)""" self.add_engines(1) ar = self.view.apply_async(crash) self.assertRaisesRemote(error.EngineError, ar.get, 10) eid = ar.engine_id tic = time.time() while eid in self.client.ids and time.time() - tic < 5: time.sleep(0.01) assert eid not in self.client.ids def test_map(self): def f(x): return x ** 2 data = list(range(16)) r = self.view.map_sync(f, data) self.assertEqual(r, list(map(f, data))) def test_map_generator(self): def f(x): return x ** 2 data = list(range(16)) r = self.view.map_sync(f, iter(data)) self.assertEqual(r, list(map(f, iter(data)))) def test_map_short_first(self): def f(x, y): if y is None: return y if x is None: return x return x * y data = list(range(10)) data2 = list(range(4)) r = self.view.map_sync(f, data, data2) self.assertEqual(r, list(map(f, data, data2))) def test_map_short_last(self): def f(x, y): if y is None: return y if x is None: return x return x * y data = list(range(4)) data2 = list(range(10)) r = self.view.map_sync(f, data, data2) self.assertEqual(r, list(map(f, data, data2))) def test_map_unordered(self): def f(x): return x ** 2 def slow_f(x): import time time.sleep(0.05 * x) return x ** 2 data = list(range(16, 0, -1)) reference = list(map(f, data)) amr = self.view.map_async(slow_f, data, ordered=False) self.assertTrue(isinstance(amr, ipp.AsyncMapResult)) # check individual elements, retrieved as they come # list comprehension uses __iter__ astheycame = [r for r in amr] # Ensure that at least one result came out of order: self.assertNotEqual(astheycame, reference, "should not have preserved order") self.assertEqual( sorted(astheycame, reverse=True), reference, "result corrupted" ) def test_map_ordered(self): def f(x): return x ** 2 def slow_f(x): import time time.sleep(0.05 * x) return x ** 2 data = list(range(16, 0, -1)) reference = list(map(f, data)) amr = self.view.map_async(slow_f, data) self.assertTrue(isinstance(amr, ipp.AsyncMapResult)) # check individual elements, retrieved as they come # list(amr) uses __iter__ astheycame = list(amr) # Ensure that results came in order self.assertEqual(astheycame, reference) self.assertEqual(amr.get(), reference) def test_map_iterable(self): """test map on iterables (balanced)""" view = self.view # 101 is prime, so it won't be evenly distributed arr = range(101) # so that it will be an iterator, even in Python 3 it = iter(arr) r = view.map_sync(lambda x: x, arr) self.assertEqual(r, list(arr)) def test_imap_max_outstanding(self): view = self.view source = count() def task(i): import time time.sleep(0.1) return i gen = view.imap(task, source, max_outstanding=5) # should submit at least max_outstanding first_result = next(gen) assert len(view.history) == 5 # retrieving results should first result second_result = next(gen) assert 6 <= len(view.history) <= 8 self.client.wait(timeout=self.timeout) def test_imap_infinite(self): view = self.view source = count() def task(i): import time time.sleep(0.1) return i gen = view.imap(task, source, max_outstanding=2) results = [] for i in gen: results.append(i) if i >= 3: break assert len(results) == 4 # wait self.client.wait(timeout=self.timeout) # verify that max_outstanding wasn't exceeded assert 5 <= len(self.view.history) < 8 def test_imap_unordered(self): self.minimum_engines(4) view = self.view source = count() def yield_up_and_down(n): for i in range(n): if i % 4 == 0: yield 1 + i / 100 else: yield i / 100 def task(t): import time time.sleep(t) return t gen = view.imap(task, yield_up_and_down(10), max_outstanding=2, ordered=False) results = [] for i, t in enumerate(gen): results.append(t) if i >= 2: break assert len(results) == 3 print(results) assert all([r < 1 for r in results]) # wait self.client.wait(timeout=self.timeout) # verify that max_outstanding wasn't exceeded assert 4 <= len(self.view.history) <= 6 def test_imap_return_exceptions(self): view = self.view source = count() def fail_on_even(n): if n % 2 == 0: raise ValueError("even!") return n gen = view.imap(fail_on_even, range(5), return_exceptions=True) for i, r in enumerate(gen): if i % 2 == 0: assert isinstance(r, error.RemoteError) else: assert r == i def test_abort(self): view = self.view ar = self.client[:].apply_async(time.sleep, 0.5) ar = self.client[:].apply_async(time.sleep, 0.5) time.sleep(0.2) ar2 = view.apply_async(lambda: 2) ar3 = view.apply_async(lambda: 3) view.abort(ar2) view.abort(ar3.msg_ids) self.assertRaises(error.TaskAborted, ar2.get) self.assertRaises(error.TaskAborted, ar3.get) def test_retries(self): self.minimum_engines(3) view = self.view def fail(): raise ValueError("Failed!") for r in range(len(self.client) - 1): with view.temp_flags(retries=r): self.assertRaisesRemote(ValueError, view.apply_sync, fail) with view.temp_flags(retries=len(self.client), timeout=0.1): self.assertRaisesRemote(error.TaskTimeout, view.apply_sync, fail) def test_short_timeout(self): self.minimum_engines(2) view = self.view def fail(): import time time.sleep(0.25) raise ValueError("Failed!") with view.temp_flags(retries=1, timeout=0.01): self.assertRaisesRemote(ValueError, view.apply_sync, fail) def test_invalid_dependency(self): view = self.view with view.temp_flags(after='12345'): self.assertRaisesRemote(error.InvalidDependency, view.apply_sync, lambda: 1) def test_impossible_dependency(self): self.minimum_engines(2) view = self.client.load_balanced_view() ar1 = view.apply_async(lambda: 1) ar1.get() e1 = ar1.engine_id e2 = e1 while e2 == e1: ar2 = view.apply_async(lambda: 1) ar2.get() e2 = ar2.engine_id with view.temp_flags(follow=[ar1, ar2]): self.assertRaisesRemote( error.ImpossibleDependency, view.apply_sync, lambda: 1 ) def test_follow(self): ar = self.view.apply_async(lambda: 1) ar.get() ars = [] first_id = ar.engine_id self.view.follow = ar for i in range(5): ars.append(self.view.apply_async(lambda: 1)) self.view.wait(ars) for ar in ars: self.assertEqual(ar.engine_id, first_id) def test_after(self): view = self.view ar = view.apply_async(time.sleep, 0.5) with view.temp_flags(after=ar): ar2 = view.apply_async(lambda: 1) ar.wait() ar2.wait() self.assertTrue( ar2.started >= ar.completed, "%s not >= %s" % (ar.started, ar.completed) ) ipyparallel-7.1.0/ipyparallel/tests/test_magics.py000066400000000000000000000331171412533144600224010ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Test Parallel magics""" import re import time import pytest from IPython import get_ipython from IPython.utils.io import capture_output import ipyparallel as ipp from .clienttest import ClusterTestCase from .clienttest import generate_output from ipyparallel import AsyncResult @pytest.mark.usefixtures('ipython_interactive') class TestParallelMagics(ClusterTestCase): def test_px_blocking(self): ip = get_ipython() v = self.client[-1:] v.activate() v.block = True ip.magic('px a=5') self.assertEqual(v['a'], [5]) ip.magic('px a=10') self.assertEqual(v['a'], [10]) # just 'print a' works ~99% of the time, but this ensures that # the stdout message has arrived when the result is finished: with capture_output() as io: ip.magic('px import sys,time;print(a);sys.stdout.flush();time.sleep(0.2)') self.assertIn('[stdout:', io.stdout) self.assertNotIn('\n\n', io.stdout) assert io.stdout.rstrip().endswith('10') self.assertRaisesRemote(ZeroDivisionError, ip.magic, 'px 1/0') def _check_generated_stderr(self, stderr, n): expected = [ r'\[stderr:\d+\]', '^stderr$', '^stderr2$', ] * n self.assertNotIn('\n\n', stderr) lines = stderr.splitlines() self.assertEqual(len(lines), len(expected), stderr) for line, expect in zip(lines, expected): if isinstance(expect, str): expect = [expect] for ex in expect: assert re.search(ex, line) is not None, "Expected %r in %r" % (ex, line) def _check_expected_lines_unordered(self, expected, lines): for expect in expected: found = False for line in lines: found = found | (re.search(expect, line) is not None) if found: break assert found, "Expected %r in output" % (expect,) def test_cellpx_block_args(self): """%%px --[no]block flags work""" ip = get_ipython() v = self.client[-1:] v.activate() v.block = False for block in (True, False): v.block = block ip.magic("pxconfig --verbose") with capture_output(display=False) as io: ip.run_cell_magic("px", "", "1") if block: assert io.stdout.startswith("Parallel"), io.stdout else: assert io.stdout.startswith("Async"), io.stdout with capture_output(display=False) as io: ip.run_cell_magic("px", "--block", "1") assert io.stdout.startswith("Parallel"), io.stdout with capture_output(display=False) as io: ip.run_cell_magic("px", "--noblock", "1") assert io.stdout.startswith("Async"), io.stdout def test_cellpx_groupby_engine(self): """%%px --group-outputs=engine""" ip = get_ipython() v = self.client[:] v.block = True v.activate() v['generate_output'] = generate_output with capture_output(display=False) as io: ip.run_cell_magic( 'px', '--group-outputs=engine --no-stream', 'generate_output()' ) self.assertNotIn('\n\n', io.stdout) lines = io.stdout.splitlines() expected = [ r'\[stdout:\d+\]', 'stdout', 'stdout2', r'\[output:\d+\]', r'IPython\.core\.display\.HTML', r'IPython\.core\.display\.Math', r'Out\[\d+:\d+\]:.*IPython\.core\.display\.Math', ] * len(v) self.assertEqual(len(lines), len(expected), io.stdout) for line, expect in zip(lines, expected): if isinstance(expect, str): expect = [expect] for ex in expect: assert re.search(ex, line) is not None, "Expected %r in %r" % (ex, line) self._check_generated_stderr(io.stderr, len(v)) def test_cellpx_groupby_order(self): """%%px --group-outputs=order""" ip = get_ipython() v = self.client[:] v.block = True v.activate() v['generate_output'] = generate_output with capture_output(display=False) as io: ip.run_cell_magic( 'px', '--group-outputs=order --no-stream', 'generate_output()' ) self.assertNotIn('\n\n', io.stdout) lines = io.stdout.splitlines() expected = [] expected.extend( [ r'\[stdout:\d+\]', 'stdout', 'stdout2', ] * len(v) ) expected.extend( [ r'\[output:\d+\]', 'IPython.core.display.HTML', ] * len(v) ) expected.extend( [ r'\[output:\d+\]', 'IPython.core.display.Math', ] * len(v) ) expected.extend([r'Out\[\d+:\d+\]:.*IPython\.core\.display\.Math'] * len(v)) self.assertEqual(len(lines), len(expected), io.stdout) for line, expect in zip(lines, expected): if isinstance(expect, str): expect = [expect] for ex in expect: assert re.search(ex, line) is not None, "Expected %r in %r" % (ex, line) self._check_generated_stderr(io.stderr, len(v)) def test_cellpx_groupby_type(self): """%%px --group-outputs=type""" ip = get_ipython() v = self.client[:] v.block = True v.activate() v['generate_output'] = generate_output with capture_output(display=False) as io: ip.run_cell_magic( 'px', '--group-outputs=type --no-stream', 'generate_output()' ) self.assertNotIn('\n\n', io.stdout) lines = io.stdout.splitlines() expected = [] expected.extend( [ r'\[stdout:\d+\]', 'stdout', 'stdout2', ] * len(v) ) expected.extend( [ r'\[output:\d+\]', r'IPython\.core\.display\.HTML', r'IPython\.core\.display\.Math', ] * len(v) ) expected.extend([(r'Out\[\d+:\d+\]', r'IPython\.core\.display\.Math')] * len(v)) self.assertEqual(len(lines), len(expected), io.stdout) for line, expect in zip(lines, expected): if isinstance(expect, str): expect = [expect] for ex in expect: assert re.search(ex, line) is not None, "Expected %r in %r" % (ex, line) self._check_generated_stderr(io.stderr, len(v)) def test_cellpx_stream(self): """%%px --stream""" ip = get_ipython() v = self.client[:] v.block = True v.activate() v['generate_output'] = generate_output with capture_output(display=False) as io: ip.run_cell_magic('px', '--stream', 'generate_output()') assert '\n\n' not in io.stdout lines = io.stdout.splitlines() expected = [] expected.extend( [ r'\[stdout:\d+\]', 'stdout', r'\[stdout:\d+\]', 'stdout2', r'\[output:\d+\]', r'IPython\.core\.display\.HTML', r'\[output:\d+\]', r'IPython\.core\.display\.Math', ] * len(v) ) expected.extend([r'Out\[\d+:\d+\]:.*IPython\.core\.display\.Math'] * len(v)) assert len(lines) == len(expected) # Check that all expected lines are in the output self._check_expected_lines_unordered(expected, lines) # Do the same for stderr assert '\n\n' not in io.stderr lines = io.stderr.splitlines() expected = [] expected.extend( [ r'\[stderr:\d+\]', 'stderr', r'\[stderr:\d+\]', 'stderr2', ] * len(v) ) assert len(lines) == len(expected) self._check_expected_lines_unordered(expected, lines) def test_px_nonblocking(self): ip = get_ipython() v = self.client[-1:] v.activate() v.block = False ip.magic('px a=5') self.assertEqual(v['a'], [5]) ip.magic('px a=10') self.assertEqual(v['a'], [10]) ip.magic('pxconfig --verbose') with capture_output() as io: ar = ip.magic('px print (a)') self.assertIsInstance(ar, AsyncResult) self.assertIn('Async', io.stdout) self.assertNotIn('[stdout:', io.stdout) self.assertNotIn('\n\n', io.stdout) ar = ip.magic('px 1/0') self.assertRaisesRemote(ZeroDivisionError, ar.get) def test_autopx_blocking(self): ip = get_ipython() v = self.client[-1] v.activate() v.block = True with capture_output(display=False) as io: ip.magic('autopx') ip.run_cell('\n'.join(('a=5', 'b=12345', 'c=0'))) ip.run_cell('b*=2') ip.run_cell('print (b)') ip.run_cell('b') ip.run_cell("b/c") ip.magic('autopx') output = io.stdout assert output.startswith('%autopx enabled'), output assert output.rstrip().endswith('%autopx disabled'), output self.assertIn('ZeroDivisionError', output) self.assertIn('\nOut[', output) self.assertIn(': 24690', output) ar = v.get_result(-1) # prevent TaskAborted on pulls, due to ZeroDivisionError time.sleep(0.5) self.assertEqual(v['a'], 5) self.assertEqual(v['b'], 24690) self.assertRaisesRemote(ZeroDivisionError, ar.get) def test_autopx_nonblocking(self): ip = get_ipython() v = self.client[-1] v.activate() v.block = False with capture_output() as io: ip.magic('autopx') ip.run_cell('\n'.join(('a=5', 'b=10', 'c=0'))) ip.run_cell('print (b)') ip.run_cell('import time; time.sleep(0.1)') ip.run_cell("b/c") ip.run_cell('b*=2') ip.magic('autopx') output = io.stdout.rstrip() assert output.startswith('%autopx enabled'), output assert output.endswith('%autopx disabled'), output self.assertNotIn('ZeroDivisionError', output) ar = v.get_result(-2, owner=False) self.assertRaisesRemote(ZeroDivisionError, ar.get) # prevent TaskAborted on pulls, due to ZeroDivisionError time.sleep(0.5) self.assertEqual(v['a'], 5) # b*=2 will not fire, due to abort self.assertEqual(v['b'], 10) def test_result(self): ip = get_ipython() v = self.client[-1] v.activate() data = dict(a=111, b=222) v.push(data, block=True) for name in ('a', 'b'): ip.magic('px ' + name) with capture_output(display=False) as io: ip.magic('pxresult') self.assertIn(str(data[name]), io.stdout) def test_px_pylab(self): """%pylab works on engines""" pytest.importorskip('matplotlib') ip = get_ipython() v = self.client[-1] v.block = True v.activate() with capture_output() as io: ip.magic("px %pylab inline") self.assertIn( "Populating the interactive namespace from numpy and matplotlib", io.stdout ) with capture_output(display=False) as io: ip.magic("px plot(rand(100))") self.assertIn('Out[', io.stdout) self.assertIn('matplotlib.lines', io.stdout) def test_pxconfig(self): ip = get_ipython() rc = self.client v = rc.activate(-1, '_tst') self.assertEqual(v.targets, rc.ids[-1]) ip.magic("%pxconfig_tst -t :") self.assertEqual(v.targets, rc.ids) ip.magic("%pxconfig_tst -t ::2") self.assertEqual(v.targets, rc.ids[::2]) ip.magic("%pxconfig_tst -t 1::2") self.assertEqual(v.targets, rc.ids[1::2]) ip.magic("%pxconfig_tst -t 1") self.assertEqual(v.targets, 1) ip.magic("%pxconfig_tst --block") self.assertEqual(v.block, True) ip.magic("%pxconfig_tst --noblock") self.assertEqual(v.block, False) def test_cellpx_targets(self): """%%px --targets doesn't change defaults""" ip = get_ipython() rc = self.client view = rc.activate(rc.ids) self.assertEqual(view.targets, rc.ids) ip.magic('pxconfig --verbose') for cell in ("pass", "1/0"): with capture_output(display=False) as io: try: ip.run_cell_magic("px", "--targets all", cell) except ipp.RemoteError: pass self.assertIn('engine(s): all', io.stdout) self.assertEqual(view.targets, rc.ids) def test_cellpx_block(self): """%%px --block doesn't change default""" ip = get_ipython() rc = self.client view = rc.activate(rc.ids) view.block = False self.assertEqual(view.targets, rc.ids) ip.magic('pxconfig --verbose') for cell in ("pass", "1/0"): with capture_output(display=False) as io: try: ip.run_cell_magic("px", "--block", cell) except ipp.RemoteError: pass self.assertNotIn('Async', io.stdout) self.assertEqual(view.block, False) ipyparallel-7.1.0/ipyparallel/tests/test_mongodb.py000066400000000000000000000025561412533144600225660ustar00rootroot00000000000000"""Tests for mongodb backend""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import os from unittest import TestCase import pytest from . import test_db c = None @pytest.fixture(scope='module') def mongo_conn(request): global c try: from pymongo import MongoClient except ImportError: pytest.skip("Requires mongodb") conn_kwargs = {} if 'DB_IP' in os.environ: conn_kwargs['host'] = os.environ['DB_IP'] if 'DBA_MONGODB_ADMIN_URI' in os.environ: # On ShiningPanda, we need a username and password to connect. They are # passed in a mongodb:// URI. conn_kwargs['host'] = os.environ['DBA_MONGODB_ADMIN_URI'] if 'DB_PORT' in os.environ: conn_kwargs['port'] = int(os.environ['DB_PORT']) try: c = MongoClient(**conn_kwargs) except Exception: c = None if c is not None: request.addfinalizer(lambda: c.drop_database('iptestdb')) return c @pytest.mark.usefixtures('mongo_conn') class TestMongoBackend(test_db.TaskDBTest, TestCase): """MongoDB backend tests""" def create_db(self): try: from ipyparallel.controller.mongodb import MongoDB return MongoDB(database='iptestdb', _connection=c) except Exception: pytest.skip("Couldn't connect to mongodb") ipyparallel-7.1.0/ipyparallel/tests/test_mpi.py000066400000000000000000000007701412533144600217220ustar00rootroot00000000000000import shutil import pytest from .test_cluster import test_restart_engines # noqa: F401 from .test_cluster import test_signal_engines # noqa: F401 from .test_cluster import test_start_stop_cluster # noqa: F401 from .test_cluster import test_to_from_dict # noqa: F401 # import tests that use engine_launcher_class fixture # override engine_launcher_class @pytest.fixture def engine_launcher_class(): if shutil.which("mpiexec") is None: pytest.skip("Requires mpiexec") return 'MPI' ipyparallel-7.1.0/ipyparallel/tests/test_remotefunction.py000066400000000000000000000030071412533144600241720ustar00rootroot00000000000000"""Tests for remote functions""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import division import ipyparallel as ipp from .clienttest import ClusterTestCase class TestRemoteFunctions(ClusterTestCase): def test_remote(self): v = self.client[-1] @ipp.remote(v, block=True) def foo(x, y=5): """multiply x * y""" return x * y self.assertEqual(foo.__name__, 'foo') self.assertIn('RemoteFunction', foo.__doc__) self.assertIn('multiply x', foo.__doc__) z = foo(5) self.assertEqual(z, 25) z = foo(2, 3) self.assertEqual(z, 6) z = foo(x=5, y=2) self.assertEqual(z, 10) def test_parallel(self): n = 2 v = self.client[:n] @ipp.parallel(v, block=True) def foo(x): """multiply x * y""" return x * 2 self.assertEqual(foo.__name__, 'foo') self.assertIn('ParallelFunction', foo.__doc__) self.assertIn('multiply x', foo.__doc__) z = foo([1, 2, 3, 4]) self.assertEqual(z, [1, 2, 1, 2, 3, 4, 3, 4]) def test_parallel_map(self): v = self.client.load_balanced_view() @ipp.parallel(v, block=True) def foo(x, y=5): """multiply x * y""" return x * y z = foo.map([1, 2, 3]) self.assertEqual(z, [5, 10, 15]) z = foo.map([1, 2, 3], [1, 2, 3]) self.assertEqual(z, [1, 4, 9]) ipyparallel-7.1.0/ipyparallel/tests/test_serialize.py000066400000000000000000000143701412533144600231250ustar00rootroot00000000000000"""test serialization tools""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import pickle from collections import namedtuple import pytest from ipyparallel import interactive from ipyparallel.serialize import deserialize_object from ipyparallel.serialize import serialize_object from ipyparallel.serialize.canning import CannedArray from ipyparallel.serialize.canning import CannedClass # ------------------------------------------------------------------------------- # Globals and Utilities # ------------------------------------------------------------------------------- def roundtrip(obj): """roundtrip an object through serialization""" bufs = serialize_object(obj) obj2, remainder = deserialize_object(bufs) assert remainder == [] return obj2 SHAPES = ((100,), (1024, 10), (10, 8, 6, 5), (), (0,)) DTYPES = ('uint8', 'float64', 'int32', [('g', 'float32')], '|S10') # ------------------------------------------------------------------------------- # Tests # ------------------------------------------------------------------------------- def new_array(shape, dtype): import numpy return numpy.random.random(shape).astype(dtype) def test_roundtrip_simple(): for obj in [ 'hello', dict(a='b', b=10), [1, 2, 'hi'], (b'123', 'hello'), ]: obj2 = roundtrip(obj) assert obj == obj2 def test_roundtrip_nested(): for obj in [ dict(a=range(5), b={1: b'hello'}), [range(5), [range(3), (1, [b'whoda'])]], ]: obj2 = roundtrip(obj) assert obj == obj2 def test_roundtrip_buffered(): for obj in [dict(a=b"x" * 1025), b"hello" * 500, [b"hello" * 501, 1, 2, 3]]: bufs = serialize_object(obj) assert len(bufs) == 2 obj2, remainder = deserialize_object(bufs) assert remainder == [] assert obj == obj2 def test_roundtrip_memoryview(): b = b'asdf' * 1025 view = memoryview(b) bufs = serialize_object(view) assert len(bufs) == 2 v2, remainder = deserialize_object(bufs) assert remainder == [] assert v2.tobytes() == b def test_numpy(): pytest.importorskip('numpy') from numpy.testing import assert_array_equal for shape in SHAPES: for dtype in DTYPES: A = new_array(shape, dtype=dtype) bufs = serialize_object(A) bufs = [memoryview(b) for b in bufs] B, r = deserialize_object(bufs) assert r == [] assert A.shape == B.shape assert A.dtype == B.dtype assert_array_equal(A, B) def test_recarray(): pytest.importorskip('numpy') from numpy.testing import assert_array_equal for shape in SHAPES: for dtype in [ [('f', float), ('s', '|S10')], [('n', int), ('s', '|S1'), ('u', 'uint32')], ]: A = new_array(shape, dtype=dtype) bufs = serialize_object(A) B, r = deserialize_object(bufs) assert r == [] assert A.shape == B.shape assert A.dtype == B.dtype assert_array_equal(A, B) def test_numpy_in_seq(): pytest.importorskip('numpy') from numpy.testing import assert_array_equal for shape in SHAPES: for dtype in DTYPES: A = new_array(shape, dtype=dtype) bufs = serialize_object((A, 1, 2, b'hello')) canned = pickle.loads(bufs[0]) assert isinstance(canned[0], CannedArray) tup, r = deserialize_object(bufs) B = tup[0] assert r == [] assert A.shape == B.shape assert A.dtype == B.dtype assert_array_equal(A, B) def test_numpy_in_dict(): pytest.importorskip('numpy') from numpy.testing import assert_array_equal for shape in SHAPES: for dtype in DTYPES: A = new_array(shape, dtype=dtype) bufs = serialize_object(dict(a=A, b=1, c=range(20))) canned = pickle.loads(bufs[0]) assert isinstance(canned['a'], CannedArray) d, r = deserialize_object(bufs) B = d['a'] assert r == [] assert A.shape == B.shape assert A.dtype == B.dtype assert_array_equal(A, B) def test_class(): @interactive class C: a = 5 bufs = serialize_object(dict(C=C)) canned = pickle.loads(bufs[0]) assert isinstance(canned['C'], CannedClass) d, r = deserialize_object(bufs) C2 = d['C'] assert C2.a == C.a def test_class_oldstyle(): @interactive class C: a = 5 bufs = serialize_object(dict(C=C)) canned = pickle.loads(bufs[0]) assert isinstance(canned['C'], CannedClass) d, r = deserialize_object(bufs) C2 = d['C'] assert C2.a == C.a def test_tuple(): tup = (lambda x: x, 1) bufs = serialize_object(tup) canned = pickle.loads(bufs[0]) assert isinstance(canned, tuple) t2, r = deserialize_object(bufs) assert t2[0](t2[1]) == tup[0](tup[1]) point = namedtuple('point', 'x y') def test_namedtuple(): p = point(1, 2) bufs = serialize_object(p) canned = pickle.loads(bufs[0]) assert isinstance(canned, point) p2, r = deserialize_object(bufs, globals()) assert p2.x == p.x assert p2.y == p.y def test_list(): lis = [lambda x: x, 1] bufs = serialize_object(lis) canned = pickle.loads(bufs[0]) assert isinstance(canned, list) l2, r = deserialize_object(bufs) assert l2[0](l2[1]) == lis[0](lis[1]) def test_class_inheritance(): @interactive class C: a = 5 @interactive class D(C): b = 10 bufs = serialize_object(dict(D=D)) canned = pickle.loads(bufs[0]) assert isinstance(canned['D'], CannedClass) d, r = deserialize_object(bufs) D2 = d['D'] assert D2.a == D.a assert D2.b == D.b def test_pickle_threshold(): numpy = pytest.importorskip('numpy') from numpy.testing import assert_array_equal A = numpy.ones((5, 5)) bufs = serialize_object(A, 1024) assert len(bufs) == 1 B, _ = deserialize_object(bufs) assert_array_equal(A, B) A = numpy.ones((512, 512)) bufs = serialize_object(A, 1024) assert len(bufs) == 2 B, _ = deserialize_object(bufs) assert_array_equal(A, B) ipyparallel-7.1.0/ipyparallel/tests/test_slurm.py000066400000000000000000000007671412533144600223050ustar00rootroot00000000000000import shutil import pytest from .test_cluster import test_restart_engines # noqa: F401 from .test_cluster import test_signal_engines # noqa: F401 from .test_cluster import test_start_stop_cluster # noqa: F401 from .test_cluster import test_to_from_dict # noqa: F401 # import tests that use engine_launcher_class fixture # override engine_launcher_class @pytest.fixture def engine_launcher_class(): if shutil.which("sbatch") is None: pytest.skip("Requires slurm") return 'Slurm' ipyparallel-7.1.0/ipyparallel/tests/test_ssh.py000066400000000000000000000032701412533144600217300ustar00rootroot00000000000000from functools import partial import pytest from traitlets.config import Config from .conftest import Cluster as BaseCluster # noqa: F401 from .test_cluster import test_restart_engines # noqa: F401 from .test_cluster import test_signal_engines # noqa: F401 from .test_cluster import test_start_stop_cluster # noqa: F401 from .test_cluster import test_to_from_dict # noqa: F401 # import tests that use engine_launcher_class fixture @pytest.fixture(params=["SSH", "SSHProxy"]) def ssh_config(ssh_key, request): c = Config() c.Cluster.controller_ip = '0.0.0.0' c.Cluster.engine_launcher_class = request.param engine_set_cfg = c[f"{request.param}EngineSetLauncher"] engine_set_cfg.ssh_args = [ "-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no", "-i", ssh_key, ] engine_set_cfg.scp_args = list(engine_set_cfg.ssh_args) # copy engine_set_cfg.remote_python = "/opt/conda/bin/python3" engine_set_cfg.remote_profile_dir = "/home/ciuser/.ipython/profile_default" engine_set_cfg.engine_args = ['--debug'] c.SSHProxyEngineSetLauncher.hostname = "127.0.0.1" c.SSHProxyEngineSetLauncher.ssh_args.append("-p2222") c.SSHProxyEngineSetLauncher.scp_args.append("-P2222") c.SSHProxyEngineSetLauncher.user = "ciuser" c.SSHEngineSetLauncher.engines = {"ciuser@127.0.0.1:2222": 4} return c @pytest.fixture def Cluster(ssh_config, BaseCluster): # noqa: F811 """Override Cluster to add ssh config""" return partial(BaseCluster, config=ssh_config) # override engine_launcher_class @pytest.fixture def engine_launcher_class(ssh_config): return ssh_config.Cluster.engine_launcher_class ipyparallel-7.1.0/ipyparallel/tests/test_util.py000066400000000000000000000015151412533144600221100ustar00rootroot00000000000000import socket from unittest import mock from jupyter_client.localinterfaces import localhost from jupyter_client.localinterfaces import public_ips from ipyparallel import util @mock.patch('warnings.warn') def test_disambiguate_ip(warn_mock): # garbage in, garbage out public_ip = public_ips()[0] assert util.disambiguate_ip_address('garbage') == 'garbage' assert util.disambiguate_ip_address('0.0.0.0', socket.gethostname()) == localhost() wontresolve = 'this.wontresolve.dns' assert util.disambiguate_ip_address('0.0.0.0', wontresolve) == wontresolve assert warn_mock.called_once_with( 'IPython could not determine IPs for {}: ' '[Errno -2] Name or service not known'.format(wontresolve), RuntimeWarning, ) assert util.disambiguate_ip_address('0.0.0.0', public_ip) == localhost() ipyparallel-7.1.0/ipyparallel/tests/test_view.py000066400000000000000000000731001412533144600221040ustar00rootroot00000000000000# -*- coding: utf-8 -*- """test View objects""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import base64 import platform import sys import time from collections import namedtuple from tempfile import NamedTemporaryFile import pytest import zmq from IPython import get_ipython from IPython.utils.io import capture_output import ipyparallel as ipp from .clienttest import ClusterTestCase from .clienttest import conditional_crash from .clienttest import skip_without from .clienttest import wait from ipyparallel import AsyncHubResult from ipyparallel import AsyncMapResult from ipyparallel import AsyncResult from ipyparallel import error from ipyparallel.util import interactive point = namedtuple("point", "x y") @pytest.mark.usefixtures('ipython') class TestView(ClusterTestCase): def setUp(self): # On Win XP, wait for resource cleanup, else parallel test group fails if platform.system() == "Windows" and platform.win32_ver()[0] == "XP": # 1 sec fails. 1.5 sec seems ok. Using 2 sec for margin of safety time.sleep(2) super(TestView, self).setUp() def test_z_crash_mux(self): """test graceful handling of engine death (direct)""" self.add_engines(1) self.minimum_engines(2) eid = self.client.ids[-1] view = self.client[-2:] view.scatter('should_crash', [False, True], flatten=True) ar = view.apply_async(conditional_crash, ipp.Reference("should_crash")) self.assertRaisesRemote(error.EngineError, ar.get, 10) tic = time.perf_counter() while eid in self.client.ids and time.perf_counter() - tic < 5: time.sleep(0.05) assert eid not in self.client.ids def test_push_pull(self): """test pushing and pulling""" data = dict(a=10, b=1.05, c=list(range(10)), d={'e': (1, 2), 'f': 'hi'}) t = self.client.ids[-1] v = self.client[t] push = v.push pull = v.pull v.block = True nengines = len(self.client) push({'data': data}) d = pull('data') self.assertEqual(d, data) self.client[:].push({'data': data}) d = self.client[:].pull('data', block=True) self.assertEqual(d, nengines * [data]) ar = push({'data': data}, block=False) self.assertTrue(isinstance(ar, AsyncResult)) r = ar.get() ar = self.client[:].pull('data', block=False) self.assertTrue(isinstance(ar, AsyncResult)) r = ar.get() self.assertEqual(r, nengines * [data]) self.client[:].push(dict(a=10, b=20)) r = self.client[:].pull(('a', 'b'), block=True) self.assertEqual(r, nengines * [[10, 20]]) def test_push_pull_function(self): "test pushing and pulling functions" def testf(x): return 2.0 * x t = self.client.ids[-1] v = self.client[t] v.block = True push = v.push pull = v.pull execute = v.execute push({'testf': testf}) r = pull('testf') self.assertEqual(r(1.0), testf(1.0)) execute('r = testf(10)') r = pull('r') self.assertEqual(r, testf(10)) ar = self.client[:].push({'testf': testf}, block=False) ar.get() ar = self.client[:].pull('testf', block=False) rlist = ar.get() for r in rlist: self.assertEqual(r(1.0), testf(1.0)) execute("def g(x): return x*x") r = pull(('testf', 'g')) self.assertEqual((r[0](10), r[1](10)), (testf(10), 100)) def test_push_function_globals(self): """test that pushed functions have access to globals""" @interactive def geta(): return a # noqa: F821 # self.add_engines(1) v = self.client[-1] v.block = True v['f'] = geta self.assertRaisesRemote(NameError, v.execute, 'b=f()') v.execute('a=5') v.execute('b=f()') self.assertEqual(v['b'], 5) def test_push_function_defaults(self): """test that pushed functions preserve default args""" def echo(a=10): return a v = self.client[-1] v.block = True v['f'] = echo v.execute('b=f()') self.assertEqual(v['b'], 10) def test_get_result(self): """test getting results from the Hub.""" c = ipp.Client(profile='iptest') # self.add_engines(1) t = c.ids[-1] v = c[t] v2 = self.client[t] ar = v.apply_async(wait, 1) # give the monitor time to notice the message time.sleep(0.25) ahr = v2.get_result(ar.msg_ids[0], owner=False) self.assertIsInstance(ahr, AsyncHubResult) self.assertEqual(ahr.get(), ar.get()) ar2 = v2.get_result(ar.msg_ids[0]) self.assertNotIsInstance(ar2, AsyncHubResult) self.assertEqual(ahr.get(), ar2.get()) c.close() def test_run_newline(self): """test that run appends newline to files""" with NamedTemporaryFile('w', delete=False) as f: f.write( """def g(): return 5 """ ) v = self.client[-1] v.run(f.name, block=True) self.assertEqual(v.apply_sync(lambda f: f(), ipp.Reference('g')), 5) def test_apply_f_kwarg(self): v = self.client[-1] def echo_kwargs(**kwargs): return kwargs kwargs = v.apply_async(echo_kwargs, f=5).get(timeout=30) assert kwargs == dict(f=5) def test_apply_tracked(self): """test tracking for apply""" # self.add_engines(1) t = self.client.ids[-1] v = self.client[t] v.block = False def echo(n=1024 * 1024, **kwargs): with v.temp_flags(**kwargs): return v.apply(lambda x: x, 'x' * n) ar = echo(1, track=False) ar.wait_for_send(5) self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker)) self.assertTrue(ar.sent) ar = echo(track=True) ar.wait_for_send(5) self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker)) self.assertEqual(ar.sent, ar._tracker.done) ar._tracker.wait() self.assertTrue(ar.sent) def test_push_tracked(self): t = self.client.ids[-1] ns = dict(x='x' * 1024 * 1024) v = self.client[t] ar = v.push(ns, block=False, track=False) self.assertTrue(ar.sent) ar = v.push(ns, block=False, track=True) ar.wait_for_send() self.assertEqual(ar.sent, ar._tracker.done) self.assertTrue(ar.sent) ar.get() def test_scatter_tracked(self): t = self.client.ids x = 'x' * 1024 * 1024 ar = self.client[t].scatter('x', x, block=False, track=False) self.assertTrue(ar.sent) ar = self.client[t].scatter('x', x, block=False, track=True) ar._sent_event.wait() self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker)) self.assertEqual(ar.sent, ar._tracker.done) ar.wait_for_send() self.assertTrue(ar.sent) ar.get() def test_remote_reference(self): v = self.client[-1] v['a'] = 123 ra = ipp.Reference('a') b = v.apply_sync(lambda x: x, ra) self.assertEqual(b, 123) def test_scatter_gather(self): view = self.client[:] seq1 = list(range(16)) view.scatter('a', seq1) seq2 = view.gather('a', block=True) self.assertEqual(seq2, seq1) self.assertRaisesRemote(NameError, view.gather, 'asdf', block=True) @skip_without('numpy') def test_scatter_gather_numpy(self): import numpy from numpy.testing import assert_array_equal view = self.client[:] a = numpy.arange(64) view.scatter('a', a, block=True) b = view.gather('a', block=True) assert_array_equal(b, a) def test_scatter_gather_lazy(self): """scatter/gather with targets='all'""" view = self.client.direct_view(targets='all') x = list(range(64)) view.scatter('x', x) gathered = view.gather('x', block=True) self.assertEqual(gathered, x) @skip_without('numpy') def test_apply_numpy(self): """view.apply(f, ndarray)""" import numpy from numpy.testing import assert_array_equal A = numpy.random.random((100, 100)) view = self.client[-1] for dt in ['int32', 'uint8', 'float32', 'float64']: B = A.astype(dt) C = view.apply_sync(lambda x: x, B) assert_array_equal(B, C) @skip_without('numpy') def test_apply_numpy_object_dtype(self): """view.apply(f, ndarray) with dtype=object""" import numpy from numpy.testing import assert_array_equal view = self.client[-1] A = numpy.array([dict(a=5)]) B = view.apply_sync(lambda x: x, A) assert_array_equal(A, B) A = numpy.array([(0, dict(b=10))], dtype=[('i', int), ('o', object)]) B = view.apply_sync(lambda x: x, A) assert_array_equal(A, B) @skip_without('numpy') def test_push_pull_recarray(self): """push/pull recarrays""" import numpy from numpy.testing import assert_array_equal view = self.client[-1] R = numpy.array( [ (1, 'hi', 0.0), (2 ** 30, 'there', 2.5), (-99999, 'world', -12345.6789), ], [('n', int), ('s', '|S10'), ('f', float)], ) view['RR'] = R R2 = view['RR'] r_dtype, r_shape = view.apply_sync( interactive(lambda: (RR.dtype, RR.shape)) # noqa: F821 ) self.assertEqual(r_dtype, R.dtype) self.assertEqual(r_shape, R.shape) self.assertEqual(R2.dtype, R.dtype) self.assertEqual(R2.shape, R.shape) assert_array_equal(R2, R) @skip_without('pandas') def test_push_pull_timeseries(self): """push/pull pandas.Series""" import pandas ts = pandas.Series(list(range(10))) view = self.client[-1] view.push(dict(ts=ts), block=True) rts = view['ts'] self.assertEqual(type(rts), type(ts)) self.assertTrue((ts == rts).all()) def test_map(self): view = self.client[:] def f(x): return x ** 2 data = list(range(16)) r = view.map_sync(f, data) self.assertEqual(r, list(map(f, data))) def test_map_empty_sequence(self): view = self.client[:] r = view.map_sync(lambda x: x, []) self.assertEqual(r, []) def test_map_iterable(self): """test map on iterables (direct)""" view = self.client[:] # 101 is prime, so it won't be evenly distributed arr = range(101) # ensure it will be an iterator, even in Python 3 it = iter(arr) r = view.map_sync(lambda x: x, it) self.assertEqual(r, list(arr)) @skip_without('numpy') def test_map_numpy(self): """test map on numpy arrays (direct)""" import numpy from numpy.testing import assert_array_equal view = self.client[:] # 101 is prime, so it won't be evenly distributed arr = numpy.arange(101) r = view.map_sync(lambda x: x, arr) assert_array_equal(r, arr) def test_scatter_gather_nonblocking(self): data = list(range(16)) view = self.client[:] view.scatter('a', data, block=False) ar = view.gather('a', block=False) self.assertEqual(ar.get(), data) @skip_without('numpy') def test_scatter_gather_numpy_nonblocking(self): import numpy from numpy.testing import assert_array_equal a = numpy.arange(64) view = self.client[:] ar = view.scatter('a', a, block=False) self.assertTrue(isinstance(ar, AsyncResult)) amr = view.gather('a', block=False) self.assertTrue(isinstance(amr, AsyncMapResult)) assert_array_equal(amr.get(), a) def test_execute(self): view = self.client[:] # self.client.debug=True execute = view.execute ar = execute('c=30', block=False) self.assertTrue(isinstance(ar, AsyncResult)) ar = execute('d=[0,1,2]', block=False) self.client.wait(ar, 1) self.assertEqual(len(ar.get()), len(self.client)) for c in view['c']: self.assertEqual(c, 30) def test_abort(self): view = self.client[-1] ar = view.execute('import time; time.sleep(1)', block=False) ar2 = view.apply_async(lambda: 2) view.abort(ar2) ar3 = view.apply_async(lambda: 3) view.abort(ar3.msg_ids) self.assertRaises(error.TaskAborted, ar2.get) self.assertRaises(error.TaskAborted, ar3.get) def test_abort_all(self): """view.abort() aborts all outstanding tasks""" view = self.client[-1] ars = [view.apply_async(time.sleep, 0.25) for i in range(10)] view.abort() view.wait(timeout=5) for ar in ars[5:]: self.assertRaises(error.TaskAborted, ar.get) def test_temp_flags(self): view = self.client[-1] view.block = True with view.temp_flags(block=False): self.assertFalse(view.block) self.assertTrue(view.block) def test_importer(self): view = self.client[-1] view.clear(block=True) with view.importer: import re # noqa: F401 @interactive def findall(pat, s): # this globals() step isn't necessary in real code # only to prevent a closure in the test re = globals()['re'] # noqa: F811 return re.findall(pat, s) self.assertEqual( view.apply_sync(findall, r'\w+', 'hello world'), 'hello world'.split() ) def test_unicode_execute(self): """test executing unicode strings""" v = self.client[-1] v.block = True if sys.version_info[0] >= 3: code = "a='é'" else: code = u"a=u'é'" v.execute(code) self.assertEqual(v['a'], u'é') def test_unicode_apply_result(self): """test unicode apply results""" v = self.client[-1] r = v.apply_sync(lambda: u'é') self.assertEqual(r, u'é') def test_unicode_apply_arg(self): """test passing unicode arguments to apply""" v = self.client[-1] @interactive def check_unicode(a, check): assert not isinstance(a, bytes), "%r is bytes, not unicode" % a assert isinstance(check, bytes), "%r is not bytes" % check assert a.encode('utf8') == check, "%s != %s" % (a, check) for s in [u'é', u'ßø®∫', u'asdf']: try: v.apply_sync(check_unicode, s, s.encode('utf8')) except error.RemoteError as e: if e.ename == 'AssertionError': self.fail(e.evalue) else: raise e def test_map_reference(self): """view.map(, *seqs) should work""" v = self.client[:] v.scatter('n', self.client.ids, flatten=True) v.execute("f = lambda x,y: x*y") rf = ipp.Reference('f') nlist = list(range(10)) mlist = nlist[::-1] expected = [m * n for m, n in zip(mlist, nlist)] result = v.map_sync(rf, mlist, nlist) self.assertEqual(result, expected) def test_apply_reference(self): """view.apply(, *args) should work""" v = self.client[:] v.scatter('n', self.client.ids, flatten=True) v.execute("f = lambda x: n*x") rf = ipp.Reference('f') result = v.apply_sync(rf, 5) expected = [5 * id for id in self.client.ids] self.assertEqual(result, expected) def test_eval_reference(self): v = self.client[self.client.ids[0]] v['g'] = list(range(5)) rg = ipp.Reference('g[0]') echo = lambda x: x self.assertEqual(v.apply_sync(echo, rg), 0) def test_reference_nameerror(self): v = self.client[self.client.ids[0]] r = ipp.Reference('elvis_has_left') echo = lambda x: x self.assertRaisesRemote(NameError, v.apply_sync, echo, r) def test_single_engine_map(self): e0 = self.client[self.client.ids[0]] r = list(range(5)) check = [-1 * i for i in r] result = e0.map_sync(lambda x: -1 * x, r) self.assertEqual(result, check) def test_len(self): """len(view) makes sense""" e0 = self.client[self.client.ids[0]] self.assertEqual(len(e0), 1) v = self.client[:] self.assertEqual(len(v), len(self.client.ids)) v = self.client.direct_view('all') self.assertEqual(len(v), len(self.client.ids)) v = self.client[:2] self.assertEqual(len(v), 2) v = self.client[:1] self.assertEqual(len(v), 1) v = self.client.load_balanced_view() self.assertEqual(len(v), len(self.client.ids)) # begin execute tests def test_execute_reply(self): e0 = self.client[self.client.ids[0]] e0.block = True ar = e0.execute("5", silent=False) er = ar.get() self.assertEqual(str(er), "" % er.execution_count) self.assertEqual(er.execute_result['data']['text/plain'], '5') def test_execute_reply_rich(self): e0 = self.client[self.client.ids[0]] e0.block = True e0.execute("from IPython.display import Image, HTML") ar = e0.execute("Image(data=b'garbage', format='png', width=10)", silent=False) er = ar.get() b64data = base64.b64encode(b'garbage').decode('ascii') data, metadata = er._repr_png_() assert data.strip() == b64data.strip() assert metadata == dict(width=10) ar = e0.execute("HTML('bold')", silent=False) er = ar.get() self.assertEqual(er._repr_html_(), "bold") def test_execute_reply_stdout(self): e0 = self.client[self.client.ids[0]] e0.block = True ar = e0.execute("print (5)", silent=False) er = ar.get() self.assertEqual(er.stdout.strip(), '5') def test_execute_result(self): """execute triggers execute_result with silent=False""" view = self.client[:] ar = view.execute("5", silent=False, block=True) expected = [{'text/plain': '5'}] * len(view) mimes = [out['data'] for out in ar.execute_result] self.assertEqual(mimes, expected) def test_execute_silent(self): """execute does not trigger execute_result with silent=True""" view = self.client[:] ar = view.execute("5", block=True) expected = [None] * len(view) self.assertEqual(ar.execute_result, expected) def test_execute_magic(self): """execute accepts IPython commands""" view = self.client[:] view.execute("a = 5") ar = view.execute("%whos", block=True) # this will raise, if that failed ar.get(5) for stdout in ar.stdout: lines = stdout.splitlines() self.assertEqual(lines[0].split(), ['Variable', 'Type', 'Data/Info']) found = False for line in lines[2:]: split = line.split() if split == ['a', 'int', '5']: found = True break self.assertTrue(found, "whos output wrong: %s" % stdout) def test_execute_displaypub(self): """execute tracks display_pub output""" view = self.client[:] view.execute("from IPython.core.display import *") ar = view.execute("[ display(i) for i in range(5) ]", block=True) expected = [{u'text/plain': str(j)} for j in range(5)] for outputs in ar.outputs: mimes = [out['data'] for out in outputs] self.assertEqual(mimes, expected) def test_apply_displaypub(self): """apply tracks display_pub output""" view = self.client[:] view.execute("from IPython.core.display import *") @interactive def publish(): [display(i) for i in range(5)] # noqa: F821 ar = view.apply_async(publish) ar.get(5) assert ar.wait_for_output(5) expected = [{u'text/plain': str(j)} for j in range(5)] for outputs in ar.outputs: mimes = [out['data'] for out in outputs] self.assertEqual(mimes, expected) def test_execute_raises(self): """exceptions in execute requests raise appropriately""" view = self.client[-1] ar = view.execute("1/0") self.assertRaisesRemote(ZeroDivisionError, ar.get, 2) def test_remoteerror_render_exception(self): """RemoteErrors get nice tracebacks""" view = self.client[-1] ar = view.execute("1/0") ip = get_ipython() ip.user_ns['ar'] = ar with capture_output() as io: ip.run_cell("ar.get(2)") self.assertTrue('ZeroDivisionError' in io.stdout, io.stdout) def test_compositeerror_render_exception(self): """CompositeErrors get nice tracebacks""" view = self.client[:] ar = view.execute("1/0") ip = get_ipython() ip.user_ns['ar'] = ar with capture_output() as io: ip.run_cell("ar.get(2)") count = min(error.CompositeError.tb_limit, len(view)) self.assertEqual(io.stdout.count('ZeroDivisionError'), count * 2, io.stdout) self.assertEqual(io.stdout.count('by zero'), count, io.stdout) self.assertEqual(io.stdout.count(':execute'), count, io.stdout) def test_compositeerror_truncate(self): """Truncate CompositeErrors with many exceptions""" view = self.client[:] requests = [] for i in range(10): requests.append(view.execute("1/0")) ar = self.client.get_result(requests) try: ar.get() except error.CompositeError as _e: e = _e else: self.fail("Should have raised CompositeError") lines = e.render_traceback() with capture_output() as io: e.print_traceback() self.assertTrue("more exceptions" in lines[-1]) count = e.tb_limit self.assertEqual(io.stdout.count('ZeroDivisionError'), 2 * count, io.stdout) self.assertEqual(io.stdout.count('by zero'), count, io.stdout) self.assertEqual(io.stdout.count(':execute'), count, io.stdout) def test_magic_pylab(self): """%pylab works on engines""" pytest.importorskip('matplotlib') view = self.client[-1] ar = view.execute("%pylab inline") # at least check if this raised: reply = ar.get(5) # include imports, in case user config ar = view.execute("plot(rand(100))", silent=False) reply = ar.get(5) assert ar.wait_for_output(5) self.assertEqual(len(reply.outputs), 1) output = reply.outputs[0] self.assertTrue("data" in output) data = output['data'] self.assertTrue("image/png" in data) def test_func_default_func(self): """interactively defined function as apply func default""" def foo(): return 'foo' def bar(f=foo): return f() view = self.client[-1] ar = view.apply_async(bar) r = ar.get(10) self.assertEqual(r, 'foo') def test_data_pub_single(self): view = self.client[-1] ar = view.execute( '\n'.join( [ 'from ipyparallel.datapub import publish_data', 'for i in range(5):', ' publish_data(dict(i=i))', ] ), block=False, ) self.assertTrue(isinstance(ar.data, dict)) ar.get(5) assert ar.wait_for_output(5) self.assertEqual(ar.data, dict(i=4)) def test_data_pub(self): view = self.client[:] ar = view.execute( '\n'.join( [ 'from ipyparallel.datapub import publish_data', 'for i in range(5):', ' publish_data(dict(i=i))', ] ), block=False, ) self.assertTrue(all(isinstance(d, dict) for d in ar.data)) ar.get(5) assert ar.wait_for_output(5) self.assertEqual(ar.data, [dict(i=4)] * len(ar)) def test_can_list_arg(self): """args in lists are canned""" view = self.client[-1] view['a'] = 128 rA = ipp.Reference('a') ar = view.apply_async(lambda x: x, [rA]) r = ar.get(5) self.assertEqual(r, [128]) def test_can_dict_arg(self): """args in dicts are canned""" view = self.client[-1] view['a'] = 128 rA = ipp.Reference('a') ar = view.apply_async(lambda x: x, dict(foo=rA)) r = ar.get(5) self.assertEqual(r, dict(foo=128)) def test_can_list_kwarg(self): """kwargs in lists are canned""" view = self.client[-1] view['a'] = 128 rA = ipp.Reference('a') ar = view.apply_async(lambda x=5: x, x=[rA]) r = ar.get(5) self.assertEqual(r, [128]) def test_can_dict_kwarg(self): """kwargs in dicts are canned""" view = self.client[-1] view['a'] = 128 rA = ipp.Reference('a') ar = view.apply_async(lambda x=5: x, dict(foo=rA)) r = ar.get(5) self.assertEqual(r, dict(foo=128)) def test_map_ref(self): """view.map works with references""" view = self.client[:] ranks = sorted(self.client.ids) view.scatter('rank', ranks, flatten=True) rrank = ipp.Reference('rank') amr = view.map_async(lambda x: x * 2, [rrank] * len(view)) drank = amr.get(5) self.assertEqual(drank, [r * 2 for r in ranks]) def test_nested_getitem_setitem(self): """get and set with view['a.b']""" view = self.client[-1] view.execute( '\n'.join( [ 'class A: pass', 'a = A()', 'a.b = 128', ] ), block=True, ) ra = ipp.Reference('a') r = view.apply_sync(lambda x: x.b, ra) self.assertEqual(r, 128) self.assertEqual(view['a.b'], 128) view['a.b'] = 0 r = view.apply_sync(lambda x: x.b, ra) self.assertEqual(r, 0) self.assertEqual(view['a.b'], 0) def test_return_namedtuple(self): def namedtuplify(x, y): return point(x, y) view = self.client[-1] p = view.apply_sync(namedtuplify, 1, 2) self.assertEqual(p.x, 1) self.assertEqual(p.y, 2) def test_apply_namedtuple(self): def echoxy(p): return p.y, p.x view = self.client[-1] tup = view.apply_sync(echoxy, point(1, 2)) self.assertEqual(tup, (2, 1)) def test_sync_imports(self): view = self.client[-1] with capture_output() as io: with view.sync_imports(): import IPython # noqa assert "IPython" in io.stdout @interactive def find_ipython(): return 'IPython' in globals() assert view.apply_sync(find_ipython) def test_sync_imports_quiet(self): view = self.client[-1] with capture_output() as io: with view.sync_imports(quiet=True): import IPython # noqa assert io.stdout == '' @interactive def find_ipython(): return 'IPython' in globals() assert view.apply_sync(find_ipython) @skip_without('cloudpickle') def test_use_cloudpickle(self): view = self.client[:] view['_a'] = 'engine' __main__ = sys.modules['__main__'] @interactive def get_a(): return _a # noqa: F821 # verify initial condition a_list = view.apply_sync(get_a) self.assertEqual(a_list, ['engine'] * len(view)) # enable cloudpickle view.use_cloudpickle() # cloudpickle prefers client values __main__._a = 'client' a_list = view.apply_sync(get_a) self.assertEqual(a_list, ['client'] * len(view)) # still works even if remote is undefined view.execute('del _a', block=True) a_list = view.apply_sync(get_a) self.assertEqual(a_list, ['client'] * len(view)) # restore pickle, shouldn't resolve view.use_pickle() self.assertRaisesRemote(NameError, view.apply_sync, get_a) @skip_without('cloudpickle') def test_cloudpickle_push_pull(self): view = self.client[:] # enable cloudpickle view.use_cloudpickle() # push/pull still work view.push({"key": "pushed"}, block=True) ar = view.pull("key") assert ar.get(timeout=10) == ["pushed"] * len(view) # restore pickle, should get the same value view.use_pickle() ar = view.pull("key") assert ar.get(timeout=10) == ["pushed"] * len(view) @skip_without('cloudpickle') @pytest.mark.xfail(reason="@require doesn't work with cloudpickle") def test_cloudpickle_require(self): view = self.client[:] # enable cloudpickle view.use_cloudpickle() assert ( 'types' not in globals() ), "Test condition isn't met if types is already imported" @ipp.require("types") @ipp.interactive def func(x): return types.SimpleNamespace(n=x) # noqa: F821 res = view.apply_async(func, 5) assert res.get(timeout=10) == [] view.use_pickle() ipyparallel-7.1.0/ipyparallel/tests/test_view_broadcast.py000066400000000000000000000070201412533144600241240ustar00rootroot00000000000000"""test BroadcastView objects""" import pytest from . import test_view needs_map = pytest.mark.xfail(reason="map not yet implemented") @pytest.mark.usefixtures('ipython') class TestBroadcastView(test_view.TestView): is_coalescing = False def setUp(self): super().setUp() self._broadcast_view_used = False # use broadcast view for direct API real_direct_view = self.client.real_direct_view = self.client.direct_view def broadcast_or_direct(targets): if isinstance(targets, int): return real_direct_view(targets) else: self._broadcast_view_used = True return self.client.broadcast_view( targets, is_coalescing=self.is_coalescing ) self.client.direct_view = broadcast_or_direct def tearDown(self): super().tearDown() # note that a test didn't use a broadcast view if not self._broadcast_view_used: pytest.skip("No broadcast view used") @needs_map def test_map(self): pass @needs_map def test_map_ref(self): pass @needs_map def test_map_reference(self): pass @needs_map def test_map_iterable(self): pass @needs_map def test_map_empty_sequence(self): pass @needs_map def test_map_numpy(self): pass @pytest.mark.xfail(reason="Tracking gets disconnected from original message") def test_scatter_tracked(self): pass class TestBroadcastViewCoalescing(TestBroadcastView): is_coalescing = True @pytest.mark.xfail(reason="coalescing view doesn't preserve target order") def test_target_ordering(self): self.minimum_engines(4) ids_in_order = self.client.ids dv = self.client.real_direct_view(ids_in_order) dv.scatter('rank', ids_in_order, flatten=True, block=True) assert dv['rank'] == ids_in_order view = self.client.broadcast_view(ids_in_order, is_coalescing=True) assert view['rank'] == ids_in_order view = self.client.broadcast_view(ids_in_order[::-1], is_coalescing=True) assert view['rank'] == ids_in_order[::-1] view = self.client.broadcast_view(ids_in_order[::2], is_coalescing=True) assert view['rank'] == ids_in_order[::2] view = self.client.broadcast_view(ids_in_order[::-2], is_coalescing=True) assert view['rank'] == ids_in_order[::-2] def test_engine_metadata(self): self.minimum_engines(4) ids_in_order = sorted(self.client.ids) dv = self.client.real_direct_view(ids_in_order) dv.scatter('rank', ids_in_order, flatten=True, block=True) view = self.client.broadcast_view(ids_in_order, is_coalescing=True) ar = view.pull('rank', block=False) result = ar.get(timeout=10) assert isinstance(ar.engine_id, list) assert isinstance(ar.engine_uuid, list) assert result == ar.engine_id assert sorted(ar.engine_id) == ids_in_order even_ids = ids_in_order[::-2] view = self.client.broadcast_view(even_ids, is_coalescing=True) ar = view.pull('rank', block=False) result = ar.get(timeout=10) assert isinstance(ar.engine_id, list) assert isinstance(ar.engine_uuid, list) assert result == ar.engine_id assert sorted(ar.engine_id) == sorted(even_ids) @pytest.mark.xfail(reason="displaypub ordering not preserved") def test_apply_displaypub(self): pass # FIXME del TestBroadcastView ipyparallel-7.1.0/ipyparallel/traitlets.py000066400000000000000000000061141412533144600207450ustar00rootroot00000000000000"""Custom ipyparallel trait types""" import entrypoints from traitlets import List from traitlets import TraitError from traitlets import Type class Launcher(Type): """Entry point-extended Type classes can be registered via entry points in addition to standard 'mypackage.MyClass' strings """ def __init__(self, *args, entry_point_group, **kwargs): self.entry_point_group = entry_point_group kwargs.setdefault('klass', 'ipyparallel.cluster.launcher.BaseLauncher') super().__init__(*args, **kwargs) _original_help = '' @property def help(self): """Extend help by listing currently installed choices""" chunks = [self._original_help] chunks.append("Currently installed: ") for key, entry_point in self.load_entry_points().items(): chunks.append( " - {}: {}.{}".format( key, entry_point.module_name, entry_point.object_name ) ) return '\n'.join(chunks) @help.setter def help(self, value): self._original_help = value def load_entry_points(self): """Load my entry point group""" # load the group group = entrypoints.get_group_named(self.entry_point_group) # make it case-insensitive return {key.lower(): value for key, value in group.items()} def validate(self, obj, value): if isinstance(value, str): # first, look up in entry point registry registry = self.load_entry_points() key = value.lower() if key in registry: value = registry[key].load() return super().validate(obj, value) class PortList(List): """List of ports For use configuring a list of ports to consume Ports will be a list of valid ports Can be specified as a port-range string for convenience (mainly for use on the command-line) e.g. '10101-10105,10108' """ @staticmethod def parse_port_range(s): """Parse a port range string in the form '1,3-5,6' into [1,3,4,5,6]""" ports = [] ranges = s.split(",") for r in ranges: start, _, end = r.partition("-") start = int(start) if end: end = int(end) ports.extend(range(start, end + 1)) else: ports.append(start) return ports def from_string_list(self, s_list): ports = [] for s in s_list: ports.extend(self.parse_port_range(s)) return ports def validate(self, obj, value): if isinstance(value, str): value = self.parse_port_range(value) value = super().validate(obj, value) for item in value: if not isinstance(item, int): raise TraitError( f"Ports must be integers in range 1-65536, not {item!r}" ) if not 1 <= item <= 65536: raise TraitError( f"Ports must be integers in range 1-65536, not {item!r}" ) return value ipyparallel-7.1.0/ipyparallel/util.py000066400000000000000000000552571412533144600177230ustar00rootroot00000000000000# coding: utf-8 """Some generic utilities for dealing with classes, urls, and serialization.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import asyncio import functools import inspect import logging import os import re import shlex import socket import sys import warnings from datetime import datetime from datetime import timezone from functools import lru_cache from signal import SIGABRT from signal import SIGINT from signal import signal from signal import SIGTERM from types import FunctionType import traitlets import zmq from dateutil.parser import parse as dateutil_parse from dateutil.tz import tzlocal from IPython import get_ipython from IPython.core.profiledir import ProfileDir from IPython.core.profiledir import ProfileDirError from IPython.paths import get_ipython_dir from jupyter_client.localinterfaces import is_public_ip from jupyter_client.localinterfaces import localhost from jupyter_client.localinterfaces import public_ips from tornado.ioloop import IOLoop from traitlets.log import get_logger from zmq.log import handlers utc = timezone.utc # ----------------------------------------------------------------------------- # Classes # ----------------------------------------------------------------------------- class Namespace(dict): """Subclass of dict for attribute access to keys.""" def __getattr__(self, key): """getattr aliased to getitem""" if key in self: return self[key] else: raise NameError(key) def __setattr__(self, key, value): """setattr aliased to setitem, with strict""" if hasattr(dict, key): raise KeyError("Cannot override dict keys %r" % key) self[key] = value class ReverseDict(dict): """simple double-keyed subset of dict methods.""" def __init__(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) self._reverse = dict() for key, value in self.items(): self._reverse[value] = key def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: return self._reverse[key] def __setitem__(self, key, value): if key in self._reverse: raise KeyError("Can't have key %r on both sides!" % key) dict.__setitem__(self, key, value) self._reverse[value] = key def pop(self, key): value = dict.pop(self, key) self._reverse.pop(value) return value def get(self, key, default=None): try: return self[key] except KeyError: return default # ----------------------------------------------------------------------------- # Functions # ----------------------------------------------------------------------------- def log_errors(f): """decorator to log unhandled exceptions raised in a method. For use wrapping on_recv callbacks, so that exceptions do not cause the stream to be closed. """ @functools.wraps(f) def logs_errors(self, *args, **kwargs): try: result = f(self, *args, **kwargs) except Exception as e: self.log.exception(f"Uncaught exception in {f}: {e}") return if inspect.isawaitable(result): # if it's async, schedule logging for when the future resolves future = asyncio.ensure_future(result) def _log_error(future): if future.exception(): self.log.error(f"Uncaught exception in {f}: {future.exception()}") future.add_done_callback(_log_error) return logs_errors def is_url(url): """boolean check for whether a string is a zmq url""" if '://' not in url: return False proto, addr = url.split('://', 1) if proto.lower() not in ['tcp', 'pgm', 'epgm', 'ipc', 'inproc']: return False return True def validate_url(url): """validate a url for zeromq""" if not isinstance(url, str): raise TypeError("url must be a string, not %r" % type(url)) url = url.lower() proto_addr = url.split('://') assert len(proto_addr) == 2, 'Invalid url: %r' % url proto, addr = proto_addr assert proto in ['tcp', 'pgm', 'epgm', 'ipc', 'inproc'], ( "Invalid protocol: %r" % proto ) # domain pattern adapted from http://www.regexlib.com/REDetails.aspx?regexp_id=391 # author: Remi Sabourin pat = re.compile( r'^([\w\d]([\w\d\-]{0,61}[\w\d])?\.)*[\w\d]([\w\d\-]{0,61}[\w\d])?$' ) if proto == 'tcp': lis = addr.split(':') assert len(lis) == 2, 'Invalid url: %r' % url addr, s_port = lis try: port = int(s_port) except ValueError: raise AssertionError("Invalid port %r in url: %r" % (port, url)) assert addr == '*' or pat.match(addr) is not None, 'Invalid url: %r' % url else: # only validate tcp urls currently pass return True def validate_url_container(container): """validate a potentially nested collection of urls.""" if isinstance(container, str): url = container return validate_url(url) elif isinstance(container, dict): container = container.values() for element in container: validate_url_container(element) def split_url(url): """split a zmq url (tcp://ip:port) into ('tcp','ip','port').""" proto_addr = url.split('://') assert len(proto_addr) == 2, 'Invalid url: %r' % url proto, addr = proto_addr lis = addr.split(':') assert len(lis) == 2, 'Invalid url: %r' % url addr, s_port = lis return proto, addr, s_port def is_ip(location): """Is a location an ip? It could be a hostname. """ return bool(re.match(location, r'(\d+\.){3}\d+')) @lru_cache() def ip_for_host(host): """Get the ip address for a host If no ips can be found for the host, the host is returned unmodified. """ try: return socket.gethostbyname_ex(host)[2][0] except Exception as e: warnings.warn( "IPython could not determine IPs for %s: %s" % (host, e), RuntimeWarning ) return host def disambiguate_ip_address(ip, location=None): """turn multi-ip interfaces '0.0.0.0' and '*' into a connectable address Explicit IP addresses are returned unmodified. Parameters ---------- ip : IP address An IP address, or the special values 0.0.0.0, or * location : IP address or hostname, optional A public IP of the target machine, or its hostname. If location is an IP of the current machine, localhost will be returned, otherwise location will be returned. """ if ip in {'0.0.0.0', '*'}: if not location: # unspecified location, localhost is the only choice return localhost() elif not is_ip(location): if location == socket.gethostname(): # hostname matches, use localhost return localhost() else: # hostname doesn't match, but the machine can have a few names. location = ip_for_host(location) if is_public_ip(location): # location is a public IP on this machine, use localhost ip = localhost() elif not public_ips(): # this machine's public IPs cannot be determined, # assume `location` is not this machine warnings.warn("IPython could not determine public IPs", RuntimeWarning) ip = location else: # location is not this machine, do not use loopback ip = location return ip def disambiguate_url(url, location=None): """turn multi-ip interfaces '0.0.0.0' and '*' into connectable ones, based on the location (default interpretation is localhost). This is for zeromq urls, such as ``tcp://*:10101``. """ try: proto, ip, port = split_url(url) except AssertionError: # probably not tcp url; could be ipc, etc. return url ip = disambiguate_ip_address(ip, location) return "%s://%s:%s" % (proto, ip, port) # -------------------------------------------------------------------------- # helpers for implementing old MEC API via view.apply # -------------------------------------------------------------------------- def interactive(f): """decorator for making functions appear as interactively defined. This results in the function being linked to the user_ns as globals() instead of the module globals(). """ # build new FunctionType, so it can have the right globals # interactive functions never have closures, that's kind of the point if isinstance(f, FunctionType): mainmod = __import__('__main__') f = FunctionType( f.__code__, mainmod.__dict__, f.__name__, f.__defaults__, ) # associate with __main__ for uncanning f.__module__ = '__main__' return f def _push(**ns): """helper method for implementing `client.push` via `client.apply`""" user_ns = get_ipython().user_global_ns tmp = '_IP_PUSH_TMP_' while tmp in user_ns: tmp = tmp + '_' try: for name, value in ns.items(): user_ns[tmp] = value exec("%s = %s" % (name, tmp), user_ns) finally: user_ns.pop(tmp, None) def _pull(keys): """helper method for implementing `client.pull` via `client.apply`""" user_ns = get_ipython().user_global_ns if isinstance(keys, (list, tuple, set)): return [eval(key, user_ns) for key in keys] else: return eval(keys, user_ns) def _execute(code): """helper method for implementing `client.execute` via `client.apply`""" user_ns = get_ipython().user_global_ns exec(code, user_ns) # -------------------------------------------------------------------------- # extra process management utilities # -------------------------------------------------------------------------- _random_ports = set() def select_random_ports(n): """Selects and return n random ports that are available.""" ports = [] for i in range(n): sock = socket.socket() sock.bind(('', 0)) while sock.getsockname()[1] in _random_ports: sock.close() sock = socket.socket() sock.bind(('', 0)) ports.append(sock) for i, sock in enumerate(ports): port = sock.getsockname()[1] sock.close() ports[i] = port _random_ports.add(port) return ports def signal_children(children): """Relay interupt/term signals to children, for more solid process cleanup.""" def terminate_children(sig, frame): log = get_logger() log.critical("Got signal %i, terminating children..." % sig) for child in children: child.terminate() sys.exit(sig != SIGINT) # sys.exit(sig) for sig in (SIGINT, SIGABRT, SIGTERM): signal(sig, terminate_children) def integer_loglevel(loglevel): try: loglevel = int(loglevel) except ValueError: if isinstance(loglevel, str): loglevel = getattr(logging, loglevel) return loglevel def connect_logger(logname, context, iface, root="ip", loglevel=logging.DEBUG): logger = logging.getLogger(logname) if any([isinstance(h, handlers.PUBHandler) for h in logger.handlers]): # don't add a second PUBHandler return loglevel = integer_loglevel(loglevel) lsock = context.socket(zmq.PUB) lsock.connect(iface) handler = handlers.PUBHandler(lsock) handler.setLevel(loglevel) handler.root_topic = root logger.addHandler(handler) logger.setLevel(loglevel) return logger def connect_engine_logger(context, iface, engine, loglevel=logging.DEBUG): from ipyparallel.engine.log import EnginePUBHandler logger = logging.getLogger() if any([isinstance(h, handlers.PUBHandler) for h in logger.handlers]): # don't add a second PUBHandler return loglevel = integer_loglevel(loglevel) lsock = context.socket(zmq.PUB) lsock.connect(iface) handler = EnginePUBHandler(engine, lsock) handler.setLevel(loglevel) logger.addHandler(handler) logger.setLevel(loglevel) return logger def local_logger(logname, loglevel=logging.DEBUG): loglevel = integer_loglevel(loglevel) logger = logging.getLogger(logname) if any([isinstance(h, logging.StreamHandler) for h in logger.handlers]): # don't add a second StreamHandler return handler = logging.StreamHandler() handler.setLevel(loglevel) formatter = logging.Formatter( "%(asctime)s.%(msecs).03d [%(name)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(loglevel) return logger def set_hwm(sock, hwm=0): """set zmq High Water Mark on a socket in a way that always works for various pyzmq / libzmq versions. """ import zmq for key in ('HWM', 'SNDHWM', 'RCVHWM'): opt = getattr(zmq, key, None) if opt is None: continue try: sock.setsockopt(opt, hwm) except zmq.ZMQError: pass def int_keys(dikt): """Rekey a dict that has been forced to cast number keys to str for JSON where there should be ints. """ for k in list(dikt): if isinstance(k, str): nk = None try: nk = int(k) except ValueError: try: nk = float(k) except ValueError: continue if nk in dikt: raise KeyError("already have key %r" % nk) dikt[nk] = dikt.pop(k) return dikt def become_dask_worker(address, nanny=False, **kwargs): """Task function for becoming a dask.distributed Worker Parameters ---------- address : str The URL of the dask Scheduler. **kwargs Any additional keyword arguments will be passed to the Worker constructor. """ shell = get_ipython() kernel = shell.kernel if getattr(kernel, 'dask_worker', None) is not None: kernel.log.info("Dask worker is already running.") return from distributed import Worker, Nanny if nanny: w = Nanny(address, **kwargs) else: w = Worker(address, **kwargs) shell.user_ns['dask_worker'] = shell.user_ns[ 'distributed_worker' ] = kernel.distributed_worker = w kernel.io_loop.add_callback(w.start) def stop_distributed_worker(): """Task function for stopping the the distributed worker on an engine.""" shell = get_ipython() kernel = shell.kernel if getattr(kernel, 'distributed_worker', None) is None: kernel.log.info("Distributed worker already stopped.") return w = kernel.distributed_worker kernel.distributed_worker = None if shell.user_ns.get('distributed_worker', None) is w: shell.user_ns.pop('distributed_worker', None) IOLoop.current().add_callback(lambda: w.terminate(None)) def ensure_timezone(dt): """Ensure a datetime object has a timezone If it doesn't have one, attach the local timezone. """ if dt.tzinfo is None: return dt.replace(tzinfo=tzlocal()) else: return dt # extract_dates forward-port from jupyter_client 5.0 # timestamp formats ISO8601 = "%Y-%m-%dT%H:%M:%S.%f" ISO8601_PAT = re.compile( r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d{1,6})?(Z|([\+\-]\d{2}:?\d{2}))?$" ) def _ensure_tzinfo(dt): """Ensure a datetime object has tzinfo If no tzinfo is present, add tzlocal """ if not dt.tzinfo: # No more naïve datetime objects! warnings.warn( u"Interpreting naïve datetime as local %s. Please add timezone info to timestamps." % dt, DeprecationWarning, stacklevel=4, ) dt = dt.replace(tzinfo=tzlocal()) return dt def _parse_date(s): """parse an ISO8601 date string If it is None or not a valid ISO8601 timestamp, it will be returned unmodified. Otherwise, it will return a datetime object. """ if s is None: return s m = ISO8601_PAT.match(s) if m: dt = dateutil_parse(s) return _ensure_tzinfo(dt) return s def extract_dates(obj): """extract ISO8601 dates from unpacked JSON""" if isinstance(obj, dict): new_obj = {} # don't clobber for k, v in obj.items(): new_obj[k] = extract_dates(v) obj = new_obj elif isinstance(obj, (list, tuple)): obj = [extract_dates(o) for o in obj] elif isinstance(obj, str): obj = _parse_date(obj) return obj def compare_datetimes(a, b): """Compare two datetime objects If one has a timezone and the other doesn't, treat the naïve datetime as local time to avoid errors. Returns the timedelta """ if a.tzinfo is None and b.tzinfo is not None: a = a.replace(tzinfo=tzlocal()) elif a.tzinfo is not None and b.tzinfo is None: b = b.replace(tzinfo=tzlocal()) return a - b def utcnow(): """Timezone-aware UTC timestamp""" return datetime.utcnow().replace(tzinfo=utc) def _patch_jupyter_client_dates(): """Monkeypatch jupyter_client.extract_dates to be nondestructive wrt timezone info""" import jupyter_client from distutils.version import LooseVersion as V if V(jupyter_client.__version__) < V('5.0'): from jupyter_client import session if hasattr(session, '_save_extract_dates'): return session._save_extract_dates = session.extract_dates session.extract_dates = extract_dates # FIXME: remove patch when we require jupyter_client 5.0 _patch_jupyter_client_dates() def progress(*args, widget=None, **kwargs): """Create a tqdm progress bar If `widget` is None, autodetects if IPython widgets should be used, otherwise use basic tqdm. """ if widget is None: # auto widget if in a kernel ip = get_ipython() if ip is not None and getattr(ip, 'kernel', None) is not None: try: import ipywidgets # noqa except ImportError: widget = False else: widget = True else: widget = False if widget: import tqdm.notebook f = tqdm.notebook.tqdm_notebook else: import tqdm kwargs.setdefault("file", sys.stdout) f = tqdm.tqdm return f(*args, **kwargs) def abbreviate_profile_dir(profile_dir): """Abbreviate IPython profile directory if in $IPYTHONDIR""" profile_prefix = os.path.join(get_ipython_dir(), "profile_") if profile_dir.startswith(profile_prefix): # use just the profile name if it's in $IPYTHONDIR return profile_dir[len(profile_prefix) :] else: return profile_dir def _all_profile_dirs(): """List all IPython profile directories""" profile_dirs = [] with os.scandir(get_ipython_dir()) as paths: for path in paths: if path.is_dir() and path.name.startswith('profile_'): profile_dirs.append(path.path) return profile_dirs def _default_profile_dir(profile=None): """Locate the default IPython profile directory Priorities: - named profile, if specified - current IPython profile, if run inside IPython - $IPYTHONDIR/profile_default Returns absolute profile directory path, ensuring it exists """ if not profile: ip = get_ipython() if ip is not None: return ip.profile_dir.location ipython_dir = get_ipython_dir() profile = profile or 'default' try: pd = ProfileDir.find_profile_dir_by_name(ipython_dir, name=profile) except ProfileDirError: pd = ProfileDir.create_profile_dir_by_name(ipython_dir, name=profile) return pd.location def _locate_profiles(profiles=None): """Locate one or more IPython profiles by name""" ipython_dir = get_ipython_dir() return [ ProfileDir.find_profile_dir_by_name(ipython_dir, name=profile).location for profile in profiles ] def shlex_join(cmd): """Backport shlex.join to Python < 3.8""" return ' '.join(shlex.quote(s) for s in cmd) _traitlet_annotations = { traitlets.Bool: bool, traitlets.Integer: int, traitlets.Float: float, traitlets.List: list, traitlets.Dict: dict, traitlets.Set: set, traitlets.Unicode: str, traitlets.Tuple: tuple, } class _TraitAnnotation: """Trait annotation for a trait type""" def __init__(self, trait_type): self.trait_type = trait_type def __repr__(self): return self.trait_type.__name__ def _trait_annotation(trait_type): """Return an annotation for a trait""" if trait_type in _traitlet_annotations: return _traitlet_annotations[trait_type] else: annotation = _traitlet_annotations[trait_type] = _TraitAnnotation(trait_type) return annotation def _traitlet_signature(cls): """Add traitlet-based signature to a class""" parameters = [] for name, trait in cls.class_traits().items(): if name.startswith("_"): # omit private traits continue if hasattr(trait, 'default'): # traitlets 5 default = trait.default() else: default = trait.default_value if default is traitlets.Undefined: default = None annotation = _trait_annotation(trait.__class__) parameters.append( inspect.Parameter( name=name, kind=inspect.Parameter.KEYWORD_ONLY, annotation=annotation, default=default, ) ) cls.__signature__ = inspect.Signature(parameters) return cls def bind(socket, url, curve_publickey=None, curve_secretkey=None): """Common utility to bind with optional auth info""" if curve_secretkey: socket.setsockopt(zmq.CURVE_SERVER, 1) socket.setsockopt(zmq.CURVE_SECRETKEY, curve_secretkey) return socket.bind(url) def connect( socket, url, curve_serverkey=None, curve_publickey=None, curve_secretkey=None, ): """Common utility to connect with optional auth info""" if curve_serverkey: if not curve_publickey or not curve_secretkey: # unspecified, generate new client credentials # we don't use client secret auth, # so these are just used for encryption. # any values will do. curve_publickey, curve_secretkey = zmq.curve_keypair() socket.setsockopt(zmq.CURVE_SERVERKEY, curve_serverkey) socket.setsockopt(zmq.CURVE_SECRETKEY, curve_secretkey) socket.setsockopt(zmq.CURVE_PUBLICKEY, curve_publickey) return socket.connect(url) ipyparallel-7.1.0/lab/000077500000000000000000000000001412533144600145765ustar00rootroot00000000000000ipyparallel-7.1.0/lab/schema/000077500000000000000000000000001412533144600160365ustar00rootroot00000000000000ipyparallel-7.1.0/lab/schema/plugin.json000066400000000000000000000010731412533144600202300ustar00rootroot00000000000000{ "jupyter.lab.setting-icon-class": "ipp-Logo", "jupyter.lab.setting-icon-label": "IPython Parallel", "title": "IPython Parallel", "description": "Settings for the IPython Parallel plugin.", "properties": { "autoStartClient": { "type": "boolean", "title": "Auto-Start Client", "description": "If set to true, every notebook and console will automatically have an IPython Parallel Cluster and Client for the active cluster injected into the kernel under the names 'cluster' and 'rc'", "default": false } }, "type": "object" } ipyparallel-7.1.0/lab/src/000077500000000000000000000000001412533144600153655ustar00rootroot00000000000000ipyparallel-7.1.0/lab/src/clusters.tsx000066400000000000000000000554721412533144600200060ustar00rootroot00000000000000import { showErrorMessage, Toolbar, ToolbarButton, CommandToolbarButton, } from "@jupyterlab/apputils"; import { IChangedArgs } from "@jupyterlab/coreutils"; import * as nbformat from "@jupyterlab/nbformat"; import { ServerConnection } from "@jupyterlab/services"; import { refreshIcon } from "@jupyterlab/ui-components"; import { ArrayExt } from "@lumino/algorithm"; import { JSONObject, JSONExt, MimeData } from "@lumino/coreutils"; import { ElementExt } from "@lumino/domutils"; import { Drag } from "@lumino/dragdrop"; import { Message } from "@lumino/messaging"; import { Poll } from "@lumino/polling"; import { ISignal, Signal } from "@lumino/signaling"; import { Widget, PanelLayout } from "@lumino/widgets"; import { newClusterDialog, INewCluster } from "./dialog"; import * as React from "react"; import * as ReactDOM from "react-dom"; import { CommandRegistry } from "@lumino/commands"; import { CommandIDs } from "./commands"; /** * A refresh interval (in ms) for polling the backend cluster manager. */ const REFRESH_INTERVAL = 5000; /** * The threshold in pixels to start a drag event. */ const DRAG_THRESHOLD = 5; /** * The mimetype used for Jupyter cell data. */ const JUPYTER_CELL_MIME = "application/vnd.jupyter.cells"; const CLUSTER_PREFIX = "ipyparallel/clusters"; /** * A widget for IPython cluster management. */ export class ClusterManager extends Widget { /** * Create a new cluster manager. */ constructor(options: ClusterManager.IOptions) { super(); this.addClass("ipp-ClusterManager"); this._serverSettings = ServerConnection.makeSettings(); this._injectClientCodeForCluster = options.injectClientCodeForCluster; this._getClientCodeForCluster = options.getClientCodeForCluster; this._registry = options.registry; // A function to set the active cluster. this._setActiveById = (id: string) => { const cluster = this._clusters.find((c) => c.id === id); if (!cluster) { return; } const old = this._activeCluster; if (old && old.id === cluster.id) { return; } this._activeCluster = cluster; this._activeClusterChanged.emit({ name: "cluster", oldValue: old, newValue: cluster, }); this.update(); }; const layout = (this.layout = new PanelLayout()); this._clusterListing = new Widget(); this._clusterListing.addClass("ipp-ClusterListing"); // Create the toolbar. const toolbar = new Toolbar(); // Make a label widget for the toolbar. const toolbarLabel = new Widget(); toolbarLabel.node.textContent = "CLUSTERS"; toolbarLabel.addClass("ipp-ClusterManager-label"); toolbar.addItem("label", toolbarLabel); // Make a refresh button for the toolbar. toolbar.addItem( "refresh", new ToolbarButton({ icon: refreshIcon, onClick: async () => { return this._updateClusterList(); }, tooltip: "Refresh Cluster List", }) ); // Make a new cluster button for the toolbar. toolbar.addItem( CommandIDs.newCluster, new CommandToolbarButton({ commands: this._registry, id: CommandIDs.newCluster, }) ); layout.addWidget(toolbar); layout.addWidget(this._clusterListing); // Do an initial refresh of the cluster list. void this._updateClusterList(); // Also refresh periodically. this._poll = new Poll({ factory: async () => { await this._updateClusterList(); }, frequency: { interval: REFRESH_INTERVAL, backoff: true, max: 60 * 1000 }, standby: "when-hidden", }); } /** * The currently selected cluster, or undefined if there is none. */ get activeCluster(): IClusterModel | undefined { return this._activeCluster; } /** * Set an active cluster by id. */ setActiveCluster(id: string): void { this._setActiveById(id); } /** * A signal that is emitted when an active cluster changes. */ get activeClusterChanged(): ISignal< this, IChangedArgs > { return this._activeClusterChanged; } /** * Whether the cluster manager is ready to launch a cluster */ get isReady(): boolean { return this._isReady; } /** * Get the current clusters known to the manager. */ get clusters(): IClusterModel[] { return this._clusters; } /** * Refresh the current list of clusters. */ async refresh(): Promise { await this._updateClusterList(); } /** * Create a new cluster. */ async create(): Promise { const clusterRequest = await newClusterDialog({}); if (!clusterRequest) { return; } const cluster = await this._newCluster(clusterRequest); return cluster; } /** * Start a cluster by ID. */ async start(id: string): Promise { const cluster = this._clusters.find((c) => c.id === id); if (!cluster) { throw Error(`Cannot find cluster ${id}`); } await this._startById(id); } /** * Stop a cluster by ID. */ async stop(id: string): Promise { const cluster = this._clusters.find((c) => c.id === id); if (!cluster) { throw Error(`Cannot find cluster ${id}`); } await this._stopById(id); } /** * Scale a cluster by ID. */ async scale(id: string): Promise { const cluster = this._clusters.find((c) => c.id === id); if (!cluster) { throw Error(`Cannot find cluster ${id}`); } const newCluster = await this._scaleById(id); return newCluster; } /** * Dispose of the cluster manager. */ dispose(): void { if (this.isDisposed) { return; } this._poll.dispose(); super.dispose(); } /** * Handle an update request. */ protected onUpdateRequest(msg: Message): void { // Don't bother if the sidebar is not visible if (!this.isVisible) { return; } ReactDOM.render( { return this._scaleById(id); }} startById={(id: string) => { return this._startById(id); }} stopById={(id: string) => { return this._stopById(id); }} setActiveById={this._setActiveById} injectClientCodeForCluster={this._injectClientCodeForCluster} />, this._clusterListing.node ); } /** * Rerender after showing. */ protected onAfterShow(msg: Message): void { this.update(); } /** * Handle `after-attach` messages for the widget. */ protected onAfterAttach(msg: Message): void { super.onAfterAttach(msg); let node = this._clusterListing.node; node.addEventListener("p-dragenter", this); node.addEventListener("p-dragleave", this); node.addEventListener("p-dragover", this); node.addEventListener("mousedown", this); } /** * Handle `before-detach` messages for the widget. */ protected onBeforeDetach(msg: Message): void { let node = this._clusterListing.node; node.removeEventListener("p-dragenter", this); node.removeEventListener("p-dragleave", this); node.removeEventListener("p-dragover", this); node.removeEventListener("mousedown", this); document.removeEventListener("mouseup", this, true); document.removeEventListener("mousemove", this, true); } /** * Handle the DOM events for the directory listing. * * @param event - The DOM event sent to the widget. * * #### Notes * This method implements the DOM `EventListener` interface and is * called in response to events on the panel's DOM node. It should * not be called directly by user code. */ handleEvent(event: Event): void { switch (event.type) { case "mousedown": this._evtMouseDown(event as MouseEvent); break; case "mouseup": this._evtMouseUp(event as MouseEvent); break; case "mousemove": this._evtMouseMove(event as MouseEvent); break; default: break; } } /** * Handle `mousedown` events for the widget. */ private _evtMouseDown(event: MouseEvent): void { const { button, shiftKey } = event; // We only handle main or secondary button actions. if (!(button === 0 || button === 2)) { return; } // Shift right-click gives the browser default behavior. if (shiftKey && button === 2) { return; } // Find the target cluster. const clusterIndex = this._findCluster(event); if (clusterIndex === -1) { return; } // Prepare for a drag start this._dragData = { pressX: event.clientX, pressY: event.clientY, index: clusterIndex, }; // Enter possible drag mode document.addEventListener("mouseup", this, true); document.addEventListener("mousemove", this, true); event.preventDefault(); } /** * Handle the `'mouseup'` event on the document. */ private _evtMouseUp(event: MouseEvent): void { // Remove the event listeners we put on the document if (event.button !== 0 || !this._drag) { document.removeEventListener("mousemove", this, true); document.removeEventListener("mouseup", this, true); } event.preventDefault(); event.stopPropagation(); } /** * Handle the `'mousemove'` event for the widget. */ private _evtMouseMove(event: MouseEvent): void { let data = this._dragData; if (!data) { return; } // Check for a drag initialization. let dx = Math.abs(event.clientX - data.pressX); let dy = Math.abs(event.clientY - data.pressY); if (dx >= DRAG_THRESHOLD || dy >= DRAG_THRESHOLD) { event.preventDefault(); event.stopPropagation(); void this._startDrag(data.index, event.clientX, event.clientY); } } /** * Start a drag event. */ private async _startDrag( index: number, clientX: number, clientY: number ): Promise { // Create the drag image. const model = this._clusters[index]; const listingItem = this._clusterListing.node.querySelector( `li.ipp-ClusterListingItem[data-cluster-id="${model.id}"]` ) as HTMLElement; const dragImage = Private.createDragImage(listingItem); // Set up the drag event. this._drag = new Drag({ mimeData: new MimeData(), dragImage, supportedActions: "copy", proposedAction: "copy", source: this, }); // Add mimeData for plain text so that normal editors can // receive the data. const textData = this._getClientCodeForCluster(model); this._drag.mimeData.setData("text/plain", textData); // Add cell data for notebook drops. const cellData: nbformat.ICodeCell[] = [ { cell_type: "code", source: textData, outputs: [], execution_count: null, metadata: {}, }, ]; this._drag.mimeData.setData(JUPYTER_CELL_MIME, cellData); // Remove mousemove and mouseup listeners and start the drag. document.removeEventListener("mousemove", this, true); document.removeEventListener("mouseup", this, true); return this._drag.start(clientX, clientY).then((action) => { if (this.isDisposed) { return; } this._drag = null; this._dragData = null; }); } /** * Launch a new cluster on the server. */ private async _newCluster( clusterRequest: INewCluster ): Promise { this._isReady = false; this._registry.notifyCommandChanged(CommandIDs.newCluster); // TODO: allow requesting a profile, options const response = await ServerConnection.makeRequest( `${this._serverSettings.baseUrl}${CLUSTER_PREFIX}`, { method: "POST", body: JSON.stringify(clusterRequest) }, this._serverSettings ); if (response.status !== 200) { const err = await response.json(); void showErrorMessage("Cluster Create Error", err); this._isReady = true; this._registry.notifyCommandChanged(CommandIDs.newCluster); throw err; } const model = (await response.json()) as IClusterModel; await this._updateClusterList(); this._isReady = true; this._registry.notifyCommandChanged(CommandIDs.newCluster); return model; } /** * Refresh the list of clusters on the server. */ private async _updateClusterList(): Promise { const response = await ServerConnection.makeRequest( `${this._serverSettings.baseUrl}${CLUSTER_PREFIX}`, {}, this._serverSettings ); if (response.status !== 200) { const msg = "Failed to list clusters: might the server extension not be installed/enabled?"; const err = new Error(msg); if (!this._serverErrorShown) { void showErrorMessage("IPP Extension Server Error", err); this._serverErrorShown = true; } throw err; } const data = (await response.json()) as IClusterModel[]; this._clusters = data; // Check to see if the active cluster still exits. // If it doesn't, or if there is no active cluster, // select the first one. const active = this._clusters.find( (c) => c.id === (this._activeCluster && this._activeCluster.id) ); if (!active) { const id = (this._clusters[0] && this._clusters[0].id) || ""; this._setActiveById(id); } this.update(); } /** * Start a cluster by its id. */ private async _startById(id: string): Promise { const response = await ServerConnection.makeRequest( `${this._serverSettings.baseUrl}${CLUSTER_PREFIX}/${id}`, { method: "POST" }, this._serverSettings ); if (response.status > 299) { const err = await response.json(); void showErrorMessage("Failed to start cluster", err); throw err; } await this._updateClusterList(); } /** * Stop a cluster by its id. */ private async _stopById(id: string): Promise { const response = await ServerConnection.makeRequest( `${this._serverSettings.baseUrl}${CLUSTER_PREFIX}/${id}`, { method: "DELETE" }, this._serverSettings ); if (response.status !== 204) { const err = await response.json(); void showErrorMessage("Failed to close cluster", err); throw err; } await this._updateClusterList(); } /** * Scale a cluster by its id. */ private async _scaleById(id: string): Promise { const cluster = this._clusters.find((c) => c.id === id); if (!cluster) { throw Error(`Failed to find cluster ${id} to scale`); } // TODO: scale not implemented // should add an engine set void showErrorMessage("Scale not implemented", ""); // const update = await showScalingDialog(cluster); const update = cluster; if (JSONExt.deepEqual(update, cluster)) { // If the user canceled, or the model is identical don't try to update. return Promise.resolve(cluster); } const response = await ServerConnection.makeRequest( `${this._serverSettings.baseUrl}${CLUSTER_PREFIX}/${id}`, { method: "PATCH", body: JSON.stringify(update), }, this._serverSettings ); if (response.status !== 200) { const err = await response.json(); void showErrorMessage("Failed to scale cluster", err); throw err; } const model = (await response.json()) as IClusterModel; await this._updateClusterList(); return model; } private _findCluster(event: MouseEvent): number { const nodes = Array.from( this.node.querySelectorAll("li.ipp-ClusterListingItem") ); return ArrayExt.findFirstIndex(nodes, (node) => { return ElementExt.hitTest(node, event.clientX, event.clientY); }); } private _drag: Drag | null; private _dragData: { pressX: number; pressY: number; index: number; } | null = null; private _clusterListing: Widget; private _clusters: IClusterModel[] = []; private _activeCluster: IClusterModel | undefined; private _setActiveById: (id: string) => void; private _injectClientCodeForCluster: (model: IClusterModel) => void; private _getClientCodeForCluster: (model: IClusterModel) => string; private _poll: Poll; private _serverSettings: ServerConnection.ISettings; private _activeClusterChanged = new Signal< this, IChangedArgs >(this); private _serverErrorShown = false; private _isReady = true; private _registry: CommandRegistry; } /** * A namespace for DasClusterManager statics. */ export namespace ClusterManager { /** * Options for the constructor. */ export interface IOptions { /** * Registry of all commands */ registry: CommandRegistry; /** * A callback to inject client connection cdoe. */ injectClientCodeForCluster: (model: IClusterModel) => void; /** * A callback to get client code for a cluster. */ getClientCodeForCluster: (model: IClusterModel) => string; } } /** * A React component for a launcher button listing. */ function ClusterListing(props: IClusterListingProps) { let listing = props.clusters.map((cluster) => { return ( props.scaleById(cluster.id)} start={() => props.startById(cluster.id)} stop={() => props.stopById(cluster.id)} setActive={() => props.setActiveById(cluster.id)} injectClientCode={() => props.injectClientCodeForCluster(cluster)} /> ); }); // Return the JSX component. return (
      {listing}
    ); } /** * Props for the cluster listing component. */ export interface IClusterListingProps { /** * A list of items to render. */ clusters: IClusterModel[]; /** * The id of the active cluster. */ activeClusterId: string; /** * A function for starting a cluster by ID. */ startById: (id: string) => Promise; /** * A function for stopping a cluster by ID. */ stopById: (id: string) => Promise; /** * Scale a cluster by id. */ scaleById: (id: string) => Promise; /** * A callback to set the active cluster by id. */ setActiveById: (id: string) => void; /** * A callback to inject client code for a cluster. */ injectClientCodeForCluster: (model: IClusterModel) => void; } /** * A TSX functional component for rendering a single running cluster. */ function ClusterListingItem(props: IClusterListingItemProps) { const { cluster, isActive, setActive, scale, start, stop, injectClientCode } = props; let itemClass = "ipp-ClusterListingItem"; itemClass = isActive ? `${itemClass} jp-mod-active` : itemClass; let cluster_state = "Stopped"; if (cluster.controller) { cluster_state = cluster.controller.state.state; if (cluster_state == "after") { cluster_state = "Stopped"; } } // stop action is 'delete' for already-stopped clusters let STOP = cluster_state === "Stopped" ? "DELETE" : "STOP"; return (
  • { setActive(); evt.stopPropagation(); }} >
    {cluster.id}
    State: {cluster_state}
    Number of engines: {cluster.engines.n || cluster.cluster.n || "auto"}
  • ); } /** * Props for the cluster listing component. */ export interface IClusterListingItemProps { /** * A cluster model to render. */ cluster: IClusterModel; /** * Whether the cluster is currently active. */ isActive: boolean; /** * A function for starting the cluster. */ start: () => Promise; /** * A function for scaling the cluster. */ scale: () => Promise; /** * A function for stopping the cluster. */ stop: () => Promise; /** * A callback function to set the active cluster. */ setActive: () => void; /** * A callback to inject client code into an editor. */ injectClientCode: () => void; } /** * An interface for a JSON-serializable representation of a cluster. */ export interface IClusterModel extends JSONObject { /** * A unique string ID for the cluster. */ id: string; /** * the cluster file for `Cluster.from_file` */ cluster_file: string; cluster: { /** * The number of engines (null = auto) */ n?: number; /** * The cluster id */ cluster_id: string; /** * The profile directory */ profile_dir: string; /** * The profile, abbreviated */ profle: string; /** * The class import string */ class: string; }; controller?: { class: string; state?: { state: string; }; }; engines: { n: number; class: string; sets: { [key: string]: { n: number; state: string; }; }; }; } /** * A namespace for module-private functionality. */ namespace Private { /** * Create a drag image for an HTML node. */ export function createDragImage(node: HTMLElement): HTMLElement { const image = node.cloneNode(true) as HTMLElement; image.classList.add("ipp-ClusterListingItem-drag"); return image; } } ipyparallel-7.1.0/lab/src/commands.ts000066400000000000000000000012451412533144600175400ustar00rootroot00000000000000export namespace CommandIDs { /** * Inject client code into the active editor. */ export const injectClientCode = "ipyparallel:inject-client-code"; /** * Launch a new cluster. */ export const newCluster = "ipyparallel:new-cluster"; /** * Launch a new cluster. */ export const startCluster = "ipyparallel:start-cluster"; /** * Shutdown a cluster. */ export const stopCluster = "ipyparallel:stop-cluster"; /** * Scale a cluster. */ export const scaleCluster = "ipyparallel:scale-cluster"; /** * Toggle the auto-starting of clients. */ export const toggleAutoStartClient = "ipyparallel:toggle-auto-start-client"; } ipyparallel-7.1.0/lab/src/dialog.tsx000066400000000000000000000111231412533144600173620ustar00rootroot00000000000000import { Dialog, showDialog } from "@jupyterlab/apputils"; import * as React from "react"; export interface INewCluster { /** * The cluster id */ cluster_id?: string; /** * The profile */ profile?: string; /** * The number of engines */ n?: number; } /** * A namespace for ClusterDialog statics. */ namespace NewCluster { /** * The props for the NewClusterDialog component. */ export interface IProps { /** * The initial cluster model shown in the Dialog. */ initialModel: INewCluster; /** * A callback that allows the component to write state to an * external object. */ stateEscapeHatch: (model: INewCluster) => void; } /** * The state for the ClusterDialog component. */ export interface IState { /** * The proposed cluster model shown in the Dialog. */ model: INewCluster; } } /** * A component for an HTML form that allows the user * to select Dialog parameters. */ export class NewCluster extends React.Component< NewCluster.IProps, NewCluster.IState > { /** * Construct a new NewCluster component. */ constructor(props: NewCluster.IProps) { super(props); let model: INewCluster; model = props.initialModel; this.state = { model }; } /** * When the component updates we take the opportunity to write * the state of the cluster to an external object so this can * be sent as the result of the dialog. */ componentDidUpdate(): void { let model: INewCluster = { ...this.state.model }; this.props.stateEscapeHatch(model); } /** * React to the number of workers changing. */ onScaleChanged(event: React.ChangeEvent): void { this.setState({ model: { ...this.state.model, n: parseInt((event.target as HTMLInputElement).value || null, null), }, }); } /** * React to the number of workers changing. */ onProfileChanged(event: React.ChangeEvent): void { this.setState({ model: { ...this.state.model, profile: (event.target as HTMLInputElement).value, }, }); } /** * React to the number of workers changing. */ onClusterIdChanged(event: React.ChangeEvent): void { this.setState({ model: { ...this.state.model, cluster_id: (event.target as HTMLInputElement).value, }, }); } /** * Render the component.. */ render() { const model = this.state.model; // const disabledClass = "ipp-mod-disabled"; return (
    Profile { this.onProfileChanged(evt); }} />
    Cluster ID { this.onClusterIdChanged(evt); }} />
    Engines { this.onScaleChanged(evt); }} />
    ); } } /** * Show a dialog for Dialog a cluster model. * * @param model: the initial model. * * @returns a promse that resolves with the user-selected Dialogs for the * cluster model. If they pressed the cancel button, it resolves with * the original model. */ export function newClusterDialog(model: INewCluster): Promise { let updatedModel = { ...model }; const escapeHatch = (update: INewCluster) => { updatedModel = update; }; return showDialog({ title: `New Cluster`, body: , buttons: [Dialog.cancelButton(), Dialog.okButton({ label: "CREATE" })], }).then((result) => { if (result.button.accept) { return updatedModel; } else { return null; } }); } ipyparallel-7.1.0/lab/src/index.ts000066400000000000000000000353471412533144600170600ustar00rootroot00000000000000// IPython Parallel Lab extension derived from dask-labextension@f6141455d770ed7de564fc4aa403b9964cd4e617 // License: BSD-3-Clause import { ILabShell, ILayoutRestorer, JupyterFrontEnd, JupyterFrontEndPlugin, } from "@jupyterlab/application"; import { ICommandPalette, ISessionContext, IWidgetTracker, } from "@jupyterlab/apputils"; import { CodeEditor } from "@jupyterlab/codeeditor"; import { ConsolePanel, IConsoleTracker } from "@jupyterlab/console"; import { IMainMenu } from "@jupyterlab/mainmenu"; import { ISettingRegistry } from "@jupyterlab/settingregistry"; import { IStateDB } from "@jupyterlab/statedb"; import { INotebookTracker, NotebookActions, NotebookPanel, } from "@jupyterlab/notebook"; import { Kernel, KernelMessage, Session } from "@jupyterlab/services"; import { LabIcon } from "@jupyterlab/ui-components"; import { Signal } from "@lumino/signaling"; import { IClusterModel, ClusterManager } from "./clusters"; import { Sidebar } from "./sidebar"; import "../style/index.css"; import logoSvgStr from "../style/logo.svg"; import { CommandIDs } from "./commands"; const PLUGIN_ID = "ipyparallel-labextension:plugin"; /** * The IPython Parallel extension. */ const plugin: JupyterFrontEndPlugin = { activate, id: PLUGIN_ID, requires: [ ICommandPalette, IConsoleTracker, ILabShell, ILayoutRestorer, IMainMenu, INotebookTracker, ISettingRegistry, IStateDB, ], autoStart: true, }; /** * Export the plugin as default. */ export default plugin; /** * Activate the cluster launcher plugin. */ async function activate( app: JupyterFrontEnd, commandPalette: ICommandPalette, consoleTracker: IConsoleTracker, labShell: ILabShell, restorer: ILayoutRestorer, mainMenu: IMainMenu, notebookTracker: INotebookTracker, settingRegistry: ISettingRegistry, state: IStateDB ): Promise { const id = "ipp-cluster-launcher"; const clientCodeInjector = (model: IClusterModel) => { const editor = Private.getCurrentEditor( app, notebookTracker, consoleTracker ); if (!editor) { return; } Private.injectClientCode(model, editor); }; // Create the sidebar panel. const sidebar = new Sidebar({ clientCodeInjector, clientCodeGetter: Private.getClientCode, registry: app.commands, }); sidebar.id = id; sidebar.title.icon = new LabIcon({ name: "ipyparallel:logo", svgstr: logoSvgStr, }); // sidebar.title.iconClass = 'ipp-Logo jp-SideBar-tabIcon'; sidebar.title.caption = "IPython Parallel"; labShell.add(sidebar, "left", { rank: 200 }); sidebar.clusterManager.activeClusterChanged.connect(async () => { const active = sidebar.clusterManager.activeCluster; return state.save(id, { cluster: active ? active.id : "", }); }); // A function to create a new client for a session. const createClientForSession = async ( session: Session.ISessionConnection | null ) => { if (!session) { return; } const cluster = sidebar.clusterManager.activeCluster; if (!cluster || !(await Private.shouldUseKernel(session.kernel))) { return; } return Private.createClientForKernel(cluster, session.kernel!); }; type SessionOwner = NotebookPanel | ConsolePanel; // An array of the trackers to check for active sessions. const trackers: IWidgetTracker[] = [ notebookTracker, consoleTracker, ]; // A function to recreate a client on reconnect. const injectOnSessionStatusChanged = async ( sessionContext: ISessionContext ) => { if ( sessionContext.session && sessionContext.session.kernel && sessionContext.session.kernel.status === "restarting" ) { return createClientForSession(sessionContext.session); } }; // A function to inject a client when a new session owner is added. const injectOnWidgetAdded = ( _: IWidgetTracker, widget: SessionOwner ) => { widget.sessionContext.statusChanged.connect(injectOnSessionStatusChanged); }; // A function to inject a client when the active cluster changes. const injectOnClusterChanged = () => { trackers.forEach((tracker) => { tracker.forEach(async (widget) => { const session = widget.sessionContext.session; if (session && (await Private.shouldUseKernel(session.kernel))) { return createClientForSession(session); } }); }); }; // Whether the cluster clients should aggressively inject themselves // into the current session. let autoStartClient: boolean = false; // Update the existing trackers and signals in light of a change to the // settings system. In particular, this reacts to a change in the setting // for auto-starting cluster client. const updateTrackers = () => { // Clear any existing signals related to the auto-starting. Signal.clearData(injectOnWidgetAdded); Signal.clearData(injectOnSessionStatusChanged); Signal.clearData(injectOnClusterChanged); if (autoStartClient) { // When a new console or notebook is created, inject // a new client into it. trackers.forEach((tracker) => { tracker.widgetAdded.connect(injectOnWidgetAdded); }); // When the status of an existing notebook changes, reinject the client. trackers.forEach((tracker) => { tracker.forEach(async (widget) => { await createClientForSession(widget.sessionContext.session); widget.sessionContext.statusChanged.connect( injectOnSessionStatusChanged ); }); }); // When the active cluster changes, reinject the client. sidebar.clusterManager.activeClusterChanged.connect( injectOnClusterChanged ); } }; // Fetch the initial state of the settings. void Promise.all([settingRegistry.load(PLUGIN_ID), state.fetch(id)]).then( async (res) => { const settings = res[0]; if (!settings) { console.warn("Unable to retrieve ipp-labextension settings"); return; } const state = res[1] as { cluster?: string } | undefined; const cluster = state ? state.cluster : ""; const onSettingsChanged = () => { // Determine whether to use the auto-starting client. autoStartClient = settings.get("autoStartClient").composite as boolean; updateTrackers(); }; onSettingsChanged(); // React to a change in the settings. settings.changed.connect(onSettingsChanged); // If an active cluster is in the state, reset it. if (cluster) { await sidebar.clusterManager.refresh(); sidebar.clusterManager.setActiveCluster(cluster); } } ); // Add a command to inject client connection code for a given cluster model. // This looks for a cluster model in the application context menu, // and looks for an editor among the currently active notebooks and consoles. // If either is not found, it bails. app.commands.addCommand(CommandIDs.injectClientCode, { label: "Inject IPython Client Connection Code", execute: () => { const cluster = Private.clusterFromClick(app, sidebar.clusterManager); if (!cluster) { return; } clientCodeInjector(cluster); }, }); // Add a command to launch a new cluster. app.commands.addCommand(CommandIDs.newCluster, { label: (args) => (args["isPalette"] ? "Create New Cluster" : "NEW"), execute: () => sidebar.clusterManager.create(), iconClass: (args) => args["isPalette"] ? "" : "jp-AddIcon jp-Icon jp-Icon-16", isEnabled: () => sidebar.clusterManager.isReady, caption: () => { if (sidebar.clusterManager.isReady) { return "Start New Cluster"; } return "Cluster starting..."; }, }); // Add a command to launch a new cluster. app.commands.addCommand(CommandIDs.startCluster, { label: "Start Cluster", execute: () => { const cluster = Private.clusterFromClick(app, sidebar.clusterManager); if (!cluster) { return; } return sidebar.clusterManager.start(cluster.id); }, }); // Add a command to stop a cluster. app.commands.addCommand(CommandIDs.stopCluster, { label: "Shutdown Cluster", execute: () => { const cluster = Private.clusterFromClick(app, sidebar.clusterManager); if (!cluster) { return; } return sidebar.clusterManager.stop(cluster.id); }, }); // Add a command to resize a cluster. app.commands.addCommand(CommandIDs.scaleCluster, { label: "Scale Cluster…", execute: () => { const cluster = Private.clusterFromClick(app, sidebar.clusterManager); if (!cluster) { return; } return sidebar.clusterManager.scale(cluster.id); }, }); // Add a command to toggle the auto-starting client code. app.commands.addCommand(CommandIDs.toggleAutoStartClient, { label: "Auto-Start IPython Parallel", isToggled: () => autoStartClient, execute: async () => { const value = !autoStartClient; const key = "autoStartClient"; return settingRegistry .set(PLUGIN_ID, key, value) .catch((reason: Error) => { console.error( `Failed to set ${PLUGIN_ID}:${key} - ${reason.message}` ); }); }, }); // Add some commands to the menu and command palette. mainMenu.settingsMenu.addGroup([ { command: CommandIDs.toggleAutoStartClient }, ]); [CommandIDs.newCluster, CommandIDs.toggleAutoStartClient].forEach( (command) => { commandPalette.addItem({ category: "IPython Parallel", command, args: { isPalette: true }, }); } ); // Add a context menu items. app.contextMenu.addItem({ command: CommandIDs.injectClientCode, selector: ".ipp-ClusterListingItem", rank: 10, }); app.contextMenu.addItem({ command: CommandIDs.stopCluster, selector: ".ipp-ClusterListingItem", rank: 3, }); app.contextMenu.addItem({ command: CommandIDs.scaleCluster, selector: ".ipp-ClusterListingItem", rank: 2, }); app.contextMenu.addItem({ command: CommandIDs.startCluster, selector: ".ipp-ClusterListing-list", rank: 1, }); } namespace Private { /** * A private counter for ids. */ export let id = 0; /** * Whether a kernel should be used. Only evaluates to true * if it is valid and in python. */ export async function shouldUseKernel( kernel: Kernel.IKernelConnection | null | undefined ): Promise { if (!kernel) { return false; } const spec = await kernel.spec; return !!spec && spec.language.toLowerCase().indexOf("python") !== -1; } /** * Connect a kernel to a cluster by creating a new Client. */ export async function createClientForKernel( model: IClusterModel, kernel: Kernel.IKernelConnection ): Promise { const code = getClientCode(model); const content: KernelMessage.IExecuteRequestMsg["content"] = { store_history: false, code, }; return new Promise((resolve, _) => { const future = kernel.requestExecute(content); future.onIOPub = (msg) => { if (msg.header.msg_type !== "display_data") { return; } resolve(void 0); }; }); } /** * Insert code to connect to a given cluster. */ export function injectClientCode( cluster: IClusterModel, editor: CodeEditor.IEditor ): void { const cursor = editor.getCursorPosition(); const offset = editor.getOffsetAt(cursor); const code = getClientCode(cluster); editor.model.value.insert(offset, code); } /** * Get code to connect to a given cluster. */ export function getClientCode(cluster: IClusterModel): string { return `import ipyparallel as ipp cluster = ipp.Cluster.from_file("${cluster.cluster_file}") rc = cluster.connect_client_sync() rc`; } /** * Get the currently focused kernel in the application, * checking both notebooks and consoles. */ export function getCurrentKernel( shell: ILabShell, notebookTracker: INotebookTracker, consoleTracker: IConsoleTracker ): Kernel.IKernelConnection | null | undefined { // Get a handle on the most relevant kernel, // whether it is attached to a notebook or a console. let current = shell.currentWidget; let kernel: Kernel.IKernelConnection | null | undefined; if (current && notebookTracker.has(current)) { kernel = (current as NotebookPanel).sessionContext.session?.kernel; } else if (current && consoleTracker.has(current)) { kernel = (current as ConsolePanel).sessionContext.session?.kernel; } else if (notebookTracker.currentWidget) { const current = notebookTracker.currentWidget; kernel = current.sessionContext.session?.kernel; } else if (consoleTracker.currentWidget) { const current = consoleTracker.currentWidget; kernel = current.sessionContext.session?.kernel; } return kernel; } /** * Get the currently focused editor in the application, * checking both notebooks and consoles. * In the case of a notebook, it creates a new cell above the currently * active cell and then returns that. */ export function getCurrentEditor( app: JupyterFrontEnd, notebookTracker: INotebookTracker, consoleTracker: IConsoleTracker ): CodeEditor.IEditor | null | undefined { // Get a handle on the most relevant kernel, // whether it is attached to a notebook or a console. let current = app.shell.currentWidget; let editor: CodeEditor.IEditor | null | undefined; if (current && notebookTracker.has(current)) { NotebookActions.insertAbove((current as NotebookPanel).content); const cell = (current as NotebookPanel).content.activeCell; editor = cell && cell.editor; } else if (current && consoleTracker.has(current)) { const cell = (current as ConsolePanel).console.promptCell; editor = cell && cell.editor; } else if (notebookTracker.currentWidget) { const current = notebookTracker.currentWidget; NotebookActions.insertAbove(current.content); const cell = current.content.activeCell; editor = cell && cell.editor; } else if (consoleTracker.currentWidget) { const current = consoleTracker.currentWidget; const cell = current.console.promptCell; editor = cell && cell.editor; } return editor; } /** * Get a cluster model based on the application context menu click node. */ export function clusterFromClick( app: JupyterFrontEnd, manager: ClusterManager ): IClusterModel | undefined { const test = (node: HTMLElement) => !!node.dataset.clusterId; const node = app.contextMenuHitTest(test); if (!node) { return undefined; } const id = node.dataset.clusterId; return manager.clusters.find((cluster) => cluster.id === id); } } ipyparallel-7.1.0/lab/src/sidebar.ts000066400000000000000000000027541412533144600173560ustar00rootroot00000000000000import { Widget, PanelLayout } from "@lumino/widgets"; import { CommandRegistry } from "@lumino/commands"; import { ClusterManager, IClusterModel } from "./clusters"; /** * A widget for hosting IPP cluster widgets */ export class Sidebar extends Widget { /** * Create a new IPP sidebar. */ constructor(options: Sidebar.IOptions) { super(); this.addClass("ipp-Sidebar"); let layout = (this.layout = new PanelLayout()); const injectClientCodeForCluster = options.clientCodeInjector; const getClientCodeForCluster = options.clientCodeGetter; // Add the cluster manager component. this._clusters = new ClusterManager({ registry: options.registry, injectClientCodeForCluster, getClientCodeForCluster, }); layout.addWidget(this._clusters); } /** * Get the cluster manager associated with the sidebar. */ get clusterManager(): ClusterManager { return this._clusters; } private _clusters: ClusterManager; } /** * A namespace for Sidebar statics. */ export namespace Sidebar { /** * Options for the constructor. */ export interface IOptions { /** * Registry of all commands */ registry: CommandRegistry; /** * A function that injects client-connection code for a given cluster. */ clientCodeInjector: (model: IClusterModel) => void; /** * A function that gets client-connection code for a given cluster. */ clientCodeGetter: (model: IClusterModel) => string; } } ipyparallel-7.1.0/lab/src/svg.d.ts000066400000000000000000000001271412533144600167560ustar00rootroot00000000000000// svg.d.ts declare module "*.svg" { const value: string; export default value; } ipyparallel-7.1.0/lab/style/000077500000000000000000000000001412533144600157365ustar00rootroot00000000000000ipyparallel-7.1.0/lab/style/code-dark.svg000066400000000000000000000003661412533144600203150ustar00rootroot00000000000000 ipyparallel-7.1.0/lab/style/code-light.svg000066400000000000000000000003661412533144600205030ustar00rootroot00000000000000 ipyparallel-7.1.0/lab/style/index.css000066400000000000000000000072411412533144600175630ustar00rootroot00000000000000:root { --ipp-launch-button-height: 24px; } /** * Rules related to the overall sidebar panel. */ .ipp-Sidebar { background: var(--jp-layout-color1); color: var(--jp-ui-font-color1); font-size: var(--jp-ui-font-size1); overflow: auto; } /** * Rules related to the cluster manager. */ .ipp-ClusterManager { border-top: 6px solid var(--jp-toolbar-border-color); } .ipp-ClusterManager .jp-Toolbar { align-items: center; } .ipp-ClusterManager .jp-Toolbar .ipp-ClusterManager-label { flex: 0 0 auto; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; font-size: var(--jp-ui-font-size0); padding: 8px 8px 8px 12px; margin: 0px; } .ipp-ClusterManager button.jp-Button > span { display: flex; flex-direction: row; align-items: center; } .ipp-ClusterListing ul.ipp-ClusterListing-list { list-style-type: none; padding: 0; margin: 0; } .ipp-ClusterListingItem { display: inline-block; list-style-type: none; padding: 8px; width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: grab; } .ipp-ClusterListingItem-drag { opacity: 0.7; color: var(--jp-ui-font-color1); cursor: grabbing; max-width: 260px; transform: translateX(-50%) translateY(-50%); } .ipp-ClusterListingItem-title { margin: 0px; font-size: var(--jp-ui-font-size2); } .ipp-ClusterListingItem-link a { text-decoration: none; color: var(--jp-content-link-color); } .ipp-ClusterListingItem-link a:hover { text-decoration: underline; } .ipp-ClusterListingItem-link a:visited { color: var(--jp-content-link-color); } .ipp-ClusterListingItem:hover { background: var(--jp-layout-color2); } .ipp-ClusterListingItem.jp-mod-active { color: white; background: var(--jp-brand-color0); } .ipp-ClusterListingItem.jp-mod-active a, .ipp-ClusterListingItem.jp-mod-active a:visited { color: white; } .ipp-ClusterListingItem button.jp-mod-styled { background-color: transparent; } .ipp-ClusterListingItem button.jp-mod-styled:hover { background-color: var(--jp-layout-color3); } .ipp-ClusterListingItem.jp-mod-active button.jp-mod-styled:hover { background-color: var(--jp-brand-color1); } .ipp-ClusterListingItem-button-panel { display: flex; align-content: center; } button.ipp-ClusterListingItem-stop { color: var(--jp-warn-color1); font-weight: 600; } button.ipp-ClusterListingItem-scale { color: var(--jp-accent-color1); font-weight: 600; } button.ipp-ClusterListingItem-start { color: var(--jp-accent-color1); font-weight: 600; } button.ipp-hidden { display: none; } .ipp-ClusterListingItem button.ipp-ClusterListingItem-code.jp-mod-styled { margin: 0 4px 0 4px; background-repeat: no-repeat; background-position: center; } /** * Rules for the scaling dialog. */ .ipp-DialogHeader { font-size: var(--jp-ui-font-size2); } .ipp-DialogSection { margin-left: 24px; } .ipp-DialogSection-item { display: flex; align-items: center; justify-content: space-around; margin: 12px 0 12px 0; } .ipp-DialogHeader input[type="checkbox"] { position: relative; top: 4px; left: 4px; margin: 0 0 0 8px; } .ipp-DialogSection input[type="number"] { width: 72px; } .ipp-DialogSection-label.ipp-mod-disabled { color: var(--jp-ui-font-color3); } .ipp-DialogSection input[type="number"]:disabled { color: var(--jp-ui-font-color3); } /** * Rules for the logos. */ .ipp-SearchIcon { background-image: var(--jp-icon-search); } [data-jp-theme-light="true"] .ipp-CodeIcon { background-image: url(code-light.svg); } [data-jp-theme-light="false"] .ipp-CodeIcon { background-image: url(code-dark.svg); } .ipp-ClusterListingItem.jp-mod-active .ipp-CodeIcon { background-image: url(code-dark.svg); } ipyparallel-7.1.0/lab/style/logo.svg000066400000000000000000000016031412533144600174170ustar00rootroot00000000000000 ipyparallel-7.1.0/package.json000066400000000000000000000065741412533144600163420ustar00rootroot00000000000000{ "name": "ipyparallel-labextension", "version": "7.0.0", "private": false, "description": "A JupyterLab extension for IPython Parallel.", "keywords": [ "ipython", "jupyter", "jupyterlab", "jupyterlab-extension" ], "homepage": "https://github.com/ipython/ipyparallel", "bugs": { "url": "https://github.com/ipython/ipyparallel/issues" }, "repository": { "type": "git", "url": "https://github.com/ipython/ipyparallel" }, "license": "BSD-3-Clause", "author": "Min Ragan-Kelley", "files": [ "lab/lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", "lab/schema/*.json", "lab/style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}" ], "main": "lab/lib/index.js", "types": "lab/lib/index.d.ts", "scripts": { "build": "jlpm run build:lib && jlpm run build:labextension:dev", "build:labextension": "jupyter labextension build .", "build:labextension:dev": "jupyter labextension build --development True .", "build:lib": "tsc", "build:prod": "jlpm run build:lib && jlpm run build:labextension", "clean": "jlpm run clean:lib", "clean:all": "jlpm run clean:lib && jlpm run clean:labextension", "clean:labextension": "rimraf ipyparallel/labextension", "clean:lib": "rimraf lab/lib tsconfig.tsbuildinfo", "eslint": "eslint . --ext .ts,.tsx --fix", "eslint:check": "eslint . --ext .ts,.tsx", "install:extension": "jupyter labextension develop --overwrite .", "lint": "prettier --check '**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}' && jlpm eslint:check", "prettier": "prettier --write '**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}'", "prettier:check": "prettier --list-different '**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}'", "test": "mocha", "watch": "run-p watch:src watch:labextension", "watch:labextension": "jupyter labextension watch .", "watch:src": "tsc -w" }, "dependencies": { "@jupyterlab/application": "^3.0.0", "@jupyterlab/apputils": "^3.0.0", "@jupyterlab/codeeditor": "^3.0.0", "@jupyterlab/console": "^3.0.0", "@jupyterlab/coreutils": "^5.0.0", "@jupyterlab/mainmenu": "^3.0.0", "@jupyterlab/nbformat": "^3.0.0", "@jupyterlab/notebook": "^3.0.0", "@jupyterlab/services": "^6.0.0", "@jupyterlab/settingregistry": "^3.0.0", "@jupyterlab/statedb": "^3.0.0", "@jupyterlab/ui-components": "^3.0.0", "@lumino/algorithm": "^1.3.3", "@lumino/coreutils": "^1.5.3", "@lumino/domutils": "^1.2.3", "@lumino/dragdrop": "^1.7.1", "@lumino/messaging": "^1.4.3", "@lumino/polling": "^1.0.4", "@lumino/signaling": "^1.4.3", "@lumino/widgets": "^1.17.0", "react": "^17.0.1", "react-dom": "^17.0.1" }, "devDependencies": { "@jupyterlab/builder": "^3.1.10", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@typescript-eslint/eslint-plugin": "^2.27.0", "@typescript-eslint/parser": "^2.27.0", "eslint": "^7.14.0", "eslint-config-prettier": "^6.10.1", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-react": "^7.21.5", "mkdirp": "^1.0.3", "mocha": "^6.2.0", "npm-run-all": "^4.1.5", "prettier": "^2.1.1", "rimraf": "^3.0.2", "typescript": "~4.1.3", "yarn": "^1.22.0" }, "resolutions": { "@types/react": "~17.0.0" }, "jupyterlab": { "extension": true, "schemaDir": "schema", "outputDir": "ipyparallel/labextension" } } ipyparallel-7.1.0/pyproject.toml000066400000000000000000000003751412533144600167610ustar00rootroot00000000000000[build-system] requires = [ "jupyterlab>=3.0.0,==3.*", "packaging", "setuptools>=40.8.0", "wheel", ] build-backend = "setuptools.build_meta" [tool.black] skip-string-normalization = true target_version = [ "py36", "py37", "py38", ] ipyparallel-7.1.0/readthedocs.yml000066400000000000000000000004031412533144600170450ustar00rootroot00000000000000version: 2 sphinx: configuration: docs/source/conf.py conda: environment: docs/environment.yml build: image: latest python: version: 3.8 install: # install ipp itself - method: pip path: . - requirements: docs/requirements.txt ipyparallel-7.1.0/setup.cfg000066400000000000000000000000451412533144600156600ustar00rootroot00000000000000[metadata] license_file = COPYING.md ipyparallel-7.1.0/setup.py000066400000000000000000000130371412533144600155560ustar00rootroot00000000000000#!/usr/bin/env python # coding: utf-8 # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import print_function import os import sys import setuptools from setuptools.command.bdist_egg import bdist_egg class bdist_egg_disabled(bdist_egg): """Disabled version of bdist_egg Prevents setup.py install performing setuptools' default easy_install, which it should never ever do. """ def run(self): sys.exit( "Aborting implicit building of eggs. Use `pip install .` to install from source." ) # the name of the project name = 'ipyparallel' pjoin = os.path.join here = os.path.abspath(os.path.dirname(__file__)) pkg_root = pjoin(here, name) lab_path = pjoin(pkg_root, 'labextension') package_data_spec = {'ipyparallel.nbextension': [pjoin('static', '*')]} data_files_spec = [ # all extension-enabling config files ( 'etc/jupyter', 'etc/jupyter', '**', ), # nbclassic extension ( 'share/jupyter/nbextensions/ipyparallel', 'ipyparallel/nbextension/static', '*', ), # lab extension ('share/jupyter/labextensions/ipyparallel-labextension', here, 'install.json'), ('share/jupyter/labextensions/ipyparallel-labextension', lab_path, '**'), ] version_ns = {} with open(pjoin(here, name, '_version.py')) as f: exec(f.read(), {}, version_ns) with open(pjoin(here, "README.md")) as f: readme = f.read() # import setupbase from jupyter_packaging (0.10.4) if '' not in sys.path: sys.path.insert(0, '') from setupbase import wrap_installers, npm_builder, get_data_files data_files = get_data_files(data_files_spec) builder = npm_builder() if os.environ.get("IPP_DISABLE_JS") == "1": print("Skipping js installation") cmdclass = {} else: cmdclass = wrap_installers(pre_develop=builder, pre_dist=builder) if "bdist_egg" not in sys.argv: cmdclass["bdist_egg"] = bdist_egg_disabled setup_args = dict( name=name, version=version_ns["__version__"], packages=setuptools.find_packages(), description="Interactive Parallel Computing with IPython", data_files=data_files, long_description=readme, long_description_content_type="text/markdown", author="IPython Development Team", author_email="ipython-dev@scipy.org", url="https://ipython.org", license="BSD", platforms="Linux, Mac OS X, Windows", keywords=["Interactive", "Interpreter", "Shell", "Parallel"], classifiers=[ "Intended Audience :: Developers", "Intended Audience :: System Administrators", "Intended Audience :: Science/Research", "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", ], cmdclass=cmdclass, include_package_data=True, install_requires=[ "entrypoints", "decorator", "pyzmq>=18", "traitlets>=4.3", "ipython>=4", "jupyter_client", "ipykernel>=4.4", "tornado>=5.1", "psutil", "python-dateutil>=2.1", "tqdm", ], python_requires=">=3.6", extras_require={ "nbext": ["notebook", "jupyter_server"], "serverextension": ["jupyter_server"], "labextension": ["jupyter_server", "jupyterlab>=3"], "test": [ "pytest", "pytest-cov", "pytest-asyncio", "pytest-tornado", "ipython[test]", "testpath", ], }, entry_points={ 'ipyparallel.controller_launchers': [ 'batch = ipyparallel.cluster.launcher:BatchControllerLauncher', 'htcondor = ipyparallel.cluster.launcher:HTCondorControllerLauncher', 'local = ipyparallel.cluster.launcher:LocalControllerLauncher', 'lsf = ipyparallel.cluster.launcher:LSFControllerLauncher', 'mpi = ipyparallel.cluster.launcher:MPIControllerLauncher', 'pbs = ipyparallel.cluster.launcher:PBSControllerLauncher', 'sge = ipyparallel.cluster.launcher:SGEControllerLauncher', 'ssh = ipyparallel.cluster.launcher:SSHControllerLauncher', 'slurm = ipyparallel.cluster.launcher:SlurmControllerLauncher', 'winhpc = ipyparallel.cluster.launcher:WindowsHPCControllerLauncher', ], 'ipyparallel.engine_launchers': [ 'batch = ipyparallel.cluster.launcher:BatchEngineSetLauncher', 'htcondor = ipyparallel.cluster.launcher:HTCondorEngineSetLauncher', 'local = ipyparallel.cluster.launcher:LocalEngineSetLauncher', 'lsf = ipyparallel.cluster.launcher:LSFEngineSetLauncher', 'mpi = ipyparallel.cluster.launcher:MPIEngineSetLauncher', 'sge = ipyparallel.cluster.launcher:SGEEngineSetLauncher', 'slurm = ipyparallel.cluster.launcher:SlurmEngineSetLauncher', 'ssh = ipyparallel.cluster.launcher:SSHEngineSetLauncher', 'sshproxy = ipyparallel.cluster.launcher:SSHProxyEngineSetLauncher', 'winhpc = ipyparallel.cluster.launcher:WindowsHPCEngineSetLauncher', ], "console_scripts": [ "ipcluster = ipyparallel.cluster.app:main", "ipcontroller = ipyparallel.controller.app:main", "ipengine = ipyparallel.engine.app:main", ], }, zip_safe=False, ) if __name__ == "__main__": setuptools.setup(**setup_args) ipyparallel-7.1.0/setupbase.py000066400000000000000000000733231412533144600164150ustar00rootroot00000000000000# coding: utf-8 # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. """ This file originates from the 'jupyter-packaging' package, and contains a set of useful utilities for including npm packages within a Python package. """ from collections import defaultdict from os.path import join as pjoin from pathlib import Path import io import os import functools import pipes import re import shlex from shutil import which import subprocess import sys try: from deprecation import deprecated except ImportError: # shim deprecated to allow setuptools to find the version string in this file deprecated = lambda *args, **kwargs: lambda *args, **kwargs: None # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly # update it when the contents of directories change. if os.path.exists('MANIFEST'): os.remove('MANIFEST') from packaging.version import VERSION_PATTERN from setuptools import Command from setuptools.command.build_py import build_py from setuptools.config import StaticModule # Note: distutils must be imported after setuptools from distutils import log from setuptools.command.sdist import sdist from setuptools.command.develop import develop from setuptools.command.bdist_egg import bdist_egg try: from wheel.bdist_wheel import bdist_wheel except ImportError: # pragma: no cover bdist_wheel = None if sys.platform == 'win32': # pragma: no cover from subprocess import list2cmdline else: def list2cmdline(cmd_list): return ' '.join(map(pipes.quote, cmd_list)) __version__ = '0.10.6' # --------------------------------------------------------------------------- # Top Level Variables # --------------------------------------------------------------------------- SEPARATORS = os.sep if os.altsep is None else os.sep + os.altsep VERSION_REGEX = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) if "--skip-npm" in sys.argv: print("Skipping npm install as requested.") skip_npm = True sys.argv.remove("--skip-npm") else: skip_npm = False # --------------------------------------------------------------------------- # Core Functions # --------------------------------------------------------------------------- def wrap_installers(pre_develop=None, pre_dist=None, post_develop=None, post_dist=None, ensured_targets=None, skip_if_exists=None): """Make a setuptools cmdclass that calls a prebuild function before installing. Parameters ---------- pre_develop: function The function to call prior to the develop command. pre_dist: function The function to call prior to the sdist and wheel commands post_develop: function The function to call after the develop command. post_dist: function The function to call after the sdist and wheel commands. ensured_targets: list A list of local file paths that should exist when the dist commands are run skip_if_exists: list A list of local files whose presence causes the prebuild to skip Notes ----- For any function given, creates a new `setuptools` command that can be run separately, e.g. `python setup.py pre_develop`. Returns ------- A cmdclass dictionary for setup args. """ cmdclass = {} def _make_command(name, func): class _Wrapped(BaseCommand): def run(self): func() _Wrapped.__name__ = name func.__name__ = name cmdclass[name] = _Wrapped for name in ['pre_develop', 'post_develop', 'pre_dist', 'post_dist']: if locals()[name]: _make_command(name, locals()[name]) cmdclass['ensure_targets'] = ensure_targets(ensured_targets or []) skips = skip_if_exists or [] should_skip = skips and all(Path(path).exists() for path in skips) def _make_wrapper(klass, pre_build, post_build): class _Wrapped(klass): def run(self): if pre_build and not should_skip: self.run_command(pre_build.__name__) if klass != develop: self.run_command('ensure_targets') klass.run(self) if post_build and not should_skip: self.run_command(post_build.__name__) cmdclass[klass.__name__] = _Wrapped if pre_develop or post_develop: _make_wrapper(develop, pre_develop, post_develop) if pre_dist or post_dist or ensured_targets: _make_wrapper(sdist, pre_dist, post_dist) if bdist_wheel: _make_wrapper(bdist_wheel, pre_dist, post_dist) return cmdclass def npm_builder(path=None, build_dir=None, source_dir=None, build_cmd='build', force=False, npm=None): """Build function factory for managing an npm installation. Note: The function is a no-op if the `--skip-npm` cli flag is used. Parameters ---------- path: str, optional The base path of the node package. Defaults to the current directory. build_dir: str, optional The target build directory. If this and source_dir are given, the JavaScript will only be build if necessary. source_dir: str, optional The source code directory. build_cmd: str, optional The npm command to build assets to the build_dir. npm: str or list, optional. The npm executable name, or a tuple of ['node', executable]. Returns ------- A build function to use with `wrap_installers` """ def builder(): if skip_npm: log.info('Skipping npm-installation') return node_package = path or os.path.abspath(os.getcwd()) node_modules = pjoin(node_package, 'node_modules') is_yarn = os.path.exists(pjoin(node_package, 'yarn.lock')) if is_yarn and not which('yarn'): log.warn('yarn not found, ignoring yarn.lock file') is_yarn = False npm_cmd = npm if npm is None: if is_yarn: npm_cmd = ['yarn'] else: npm_cmd = ['npm'] elif isinstance(npm, str): npm_cmd = [npm] if not which(npm_cmd[0]): log.error("`{0}` unavailable. If you're running this command " "using sudo, make sure `{0}` is available to sudo" .format(npm_cmd[0])) return if build_dir and source_dir and not force: should_build = is_stale(build_dir, source_dir) else: should_build = True if should_build: log.info('Installing build dependencies with npm. This may ' 'take a while...') run(npm_cmd + ['install'], cwd=node_package) if build_cmd: run(npm_cmd + ['run', build_cmd], cwd=node_package) return builder # --------------------------------------------------------------------------- # Utility Functions # --------------------------------------------------------------------------- def get_data_files(data_specs, *, top=None, exclude=None): """Expand data file specs into valid data files metadata. Parameters ---------- data_files_spec: list A list of (path, dname, pattern) tuples where the path is the `data_files` install path, dname is the source directory, and the pattern is a glob pattern. top: str, optional The top directory exclude: func, optional Function used to test whether to exclude a file Returns ------- A valid list of data_files items. """ return _get_data_files(data_specs, None, top=top, exclude=exclude) def get_version(fpath, name='__version__'): """Get the version of the package from the given file by extracting the given `name`. """ # Try to get it from a static import first try: module = StaticModule(fpath.replace(os.sep, '.').replace('.py', '')) return getattr(module, name) except Exception as e: pass path = os.path.realpath(fpath) version_ns = {} with io.open(path, encoding="utf8") as f: exec(f.read(), {}, version_ns) return version_ns[name] def run(cmd, **kwargs): """Echo a command before running it.""" log.info('> ' + list2cmdline(cmd)) kwargs.setdefault('shell', os.name == 'nt') if not isinstance(cmd, (list, tuple)): cmd = shlex.split(cmd, posix=os.name!='nt') if not os.path.isabs(cmd[0]): # If a command is not an absolute path find it first. cmd_path = which(cmd[0]) if not cmd_path: raise ValueError("Aborting. Could not find cmd (%s) in path. " "If command is not expected to be in user's path, " "use an absolute path." % cmd[0]) cmd[0] = cmd_path return subprocess.check_call(cmd, **kwargs) def is_stale(target, source): """Test whether the target file/directory is stale based on the source file/directory. """ if not os.path.exists(target): return True target_mtime = recursive_mtime(target) or 0 return compare_recursive_mtime(source, cutoff=target_mtime) class BaseCommand(Command): """Empty command because Command needs subclasses to override too much""" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def get_inputs(self): return [] def get_outputs(self): return [] def combine_commands(*commands): """Return a Command that combines several commands.""" class CombinedCommand(BaseCommand): def initialize_options(self): self.commands = [] for C in commands: self.commands.append(C(self.distribution)) for c in self.commands: c.initialize_options() def finalize_options(self): for c in self.commands: c.finalize_options() def run(self): for c in self.commands: c.run() return CombinedCommand def compare_recursive_mtime(path, cutoff, newest=True): """Compare the newest/oldest mtime for all files in a directory. Cutoff should be another mtime to be compared against. If an mtime that is newer/older than the cutoff is found it will return True. E.g. if newest=True, and a file in path is newer than the cutoff, it will return True. """ if os.path.isfile(path): mt = mtime(path) if newest: if mt > cutoff: return True elif mt < cutoff: return True for dirname, _, filenames in os.walk(path, topdown=False): for filename in filenames: mt = mtime(pjoin(dirname, filename)) if newest: # Put outside of loop? if mt > cutoff: return True elif mt < cutoff: return True return False def recursive_mtime(path, newest=True): """Gets the newest/oldest mtime for all files in a directory.""" if os.path.isfile(path): return mtime(path) current_extreme = None for dirname, dirnames, filenames in os.walk(path, topdown=False): for filename in filenames: mt = mtime(pjoin(dirname, filename)) if newest: # Put outside of loop? if mt >= (current_extreme or mt): current_extreme = mt elif mt <= (current_extreme or mt): current_extreme = mt return current_extreme def mtime(path): """shorthand for mtime""" return os.stat(path).st_mtime def skip_if_exists(paths, CommandClass): """Skip a command if list of paths exists.""" def should_skip(): return all(Path(path).exists() for path in paths) class SkipIfExistCommand(Command): def initialize_options(self): if not should_skip(): self.command = CommandClass(self.distribution) self.command.initialize_options() else: self.command = None def finalize_options(self): if self.command is not None: self.command.finalize_options() def run(self): if self.command is not None: self.command.run() return SkipIfExistCommand def ensure_targets(targets): """Return a Command that checks that certain files exist. Raises a ValueError if any of the files are missing. Note: The check is skipped if the `--skip-npm` flag is used. """ class TargetsCheck(BaseCommand): def run(self): if skip_npm: log.info('Skipping target checks') return missing = [t for t in targets if not os.path.exists(t)] if missing: raise ValueError(('missing files: %s' % missing)) return TargetsCheck # --------------------------------------------------------------------------- # Deprecated Functions # --------------------------------------------------------------------------- @deprecated(deprecated_in="0.11", removed_in="2.0", current_version=__version__, details="Parse the version info as described in `get_version_info` docstring") def get_version_info(version_str): """DEPRECATED: Get a version info tuple given a version string Use something like the following instead: ``` import re # Version string must appear intact for tbump versioning __version__ = '1.4.0.dev0' # Build up version_info tuple for backwards compatibility pattern = r'(?P\d+).(?P\d+).(?P\d+)(?P.*)' match = re.match(pattern, __version__) parts = [int(match[part]) for part in ['major', 'minor', 'patch']] if match['rest']: parts.append(match['rest']) version_info = tuple(parts) ``` """ match = VERSION_REGEX.match(version_str) if not match: raise ValueError(f'Invalid version "{version_str}"') release = match['release'] version_info = [int(p) for p in release.split('.')] if release != version_str: version_info.append(version_str[len(release):]) return tuple(version_info) @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="Use `BaseCommand` directly instead") def command_for_func(func): """Create a command that calls the given function.""" class FuncCommand(BaseCommand): def run(self): func() update_package_data(self.distribution) return FuncCommand @deprecated(deprecated_in="0.7", removed_in="1.0", current_version=__version__, details="Use `setuptools` `python_requires` instead") def ensure_python(specs): """Given a list of range specifiers for python, ensure compatibility. """ if sys.version_info >= (3, 10): raise RuntimeError("ensure_python is deprecated and not compatible with Python 3.10+") if not isinstance(specs, (list, tuple)): specs = [specs] v = sys.version_info part = '%s.%s' % (v.major, v.minor) for spec in specs: if part == spec: return try: if eval(part + spec): return except SyntaxError: pass raise ValueError('Python version %s unsupported' % part) @deprecated(deprecated_in="0.7", removed_in="1.0", current_version=__version__, details="Use `setuptools.find_packages` instead") def find_packages(top): """ Find all of the packages. """ from setuptools import find_packages as fp return fp(top) @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="Use `use_package_data=True` and `MANIFEST.in` instead") def update_package_data(distribution): """update build_py options to get package_data changes""" build_py = distribution.get_command_obj('build_py') build_py.finalize_options() @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="Not needed") class bdist_egg_disabled(bdist_egg): """Disabled version of bdist_egg Prevents setup.py install performing setuptools' default easy_install, which it should never ever do. """ def run(self): sys.exit("Aborting implicit building of eggs. Use `pip install .` " " to install from source.") @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="""" Use `wrap_installers` to handle prebuild steps in cmdclass. Use `get_data_files` to handle data files. Use `include_package_data=True` and `MANIFEST.in` for package data. """) def create_cmdclass(prerelease_cmd=None, package_data_spec=None, data_files_spec=None, exclude=None): """Create a command class with the given optional prerelease class. Parameters ---------- prerelease_cmd: (name, Command) tuple, optional The command to run before releasing. package_data_spec: dict, optional A dictionary whose keys are the dotted package names and whose values are a list of glob patterns. data_files_spec: list, optional A list of (path, dname, pattern) tuples where the path is the `data_files` install path, dname is the source directory, and the pattern is a glob pattern. exclude: function A function which takes a string filename and returns True if the file should be excluded from package data and data files, False otherwise. Notes ----- We use specs so that we can find the files *after* the build command has run. The package data glob patterns should be relative paths from the package folder containing the __init__.py file, which is given as the package name. e.g. `dict(foo=['bar/*', 'baz/**'])` The data files directories should be absolute paths or relative paths from the root directory of the repository. Data files are specified differently from `package_data` because we need a separate path entry for each nested folder in `data_files`, and this makes it easier to parse. e.g. `('share/foo/bar', 'pkgname/bizz, '*')` """ wrapped = [prerelease_cmd] if prerelease_cmd else [] if package_data_spec or data_files_spec: wrapped.append('handle_files') wrapper = functools.partial(_wrap_command, wrapped) handle_files = _get_file_handler(package_data_spec, data_files_spec, exclude) develop_handler = _get_develop_handler() if 'bdist_egg' in sys.argv: egg = wrapper(bdist_egg, strict=True) else: egg = bdist_egg_disabled is_repo = os.path.exists('.git') cmdclass = dict( build_py=wrapper(build_py, strict=is_repo), bdist_egg=egg, sdist=wrapper(sdist, strict=True), handle_files=handle_files, ) if bdist_wheel: cmdclass['bdist_wheel'] = wrapper(bdist_wheel, strict=True) cmdclass['develop'] = wrapper(develop_handler, strict=True) return cmdclass @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="Use `npm_builder` and `wrap_installers`") def install_npm(path=None, build_dir=None, source_dir=None, build_cmd='build', force=False, npm=None): """Return a Command for managing an npm installation. Note: The command is skipped if the `--skip-npm` flag is used. Parameters ---------- path: str, optional The base path of the node package. Defaults to the current directory. build_dir: str, optional The target build directory. If this and source_dir are given, the JavaScript will only be build if necessary. source_dir: str, optional The source code directory. build_cmd: str, optional The npm command to build assets to the build_dir. npm: str or list, optional. The npm executable name, or a tuple of ['node', executable]. """ builder = npm_builder(path=path, build_dir=build_dir, source_dir=source_dir, build_cmd=build_cmd, force=force, npm=npm) class NPM(BaseCommand): description = 'install package.json dependencies using npm' def run(self): builder() return NPM # --------------------------------------------------------------------------- # Private Functions # --------------------------------------------------------------------------- @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="Use `npm_builder` and `wrap_installers`") def _wrap_command(cmds, cls, strict=True): """Wrap a setup command Parameters ---------- cmds: list(str) The names of the other commands to run prior to the command. strict: boolean, optional Whether to raise errors when a pre-command fails. """ class WrappedCommand(cls): def run(self): if not getattr(self, 'uninstall', None): try: [self.run_command(cmd) for cmd in cmds] except Exception: if strict: raise else: pass # update package data update_package_data(self.distribution) result = cls.run(self) return result return WrappedCommand @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="Use `npm_builder` and `wrap_installers`") def _get_file_handler(package_data_spec, data_files_spec, exclude=None): """Get a package_data and data_files handler command. """ class FileHandler(BaseCommand): def run(self): package_data = self.distribution.package_data package_spec = package_data_spec or dict() for (key, patterns) in package_spec.items(): files = _get_package_data(key, patterns) if exclude is not None: files = [f for f in files if not exclude(f)] package_data[key] =files self.distribution.data_files = _get_data_files( data_files_spec, self.distribution.data_files, exclude=exclude ) return FileHandler @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="Use `npm_builder` and `wrap_installers`") def _get_develop_handler(): """Get a handler for the develop command""" class _develop(develop): def install_for_development(self): self.finalize_options() super(_develop, self).install_for_development() self.run_command('handle_files') prefix = self.install_base or self.prefix or sys.prefix for target_dir, filepaths in self.distribution.data_files: for filepath in filepaths: filename = os.path.basename(filepath) target = os.path.join(prefix, target_dir, filename) self.mkpath(os.path.dirname(target)) outf, copied = self.copy_file(filepath, target) return _develop def _glob_pjoin(*parts): """Join paths for glob processing""" if parts[0] in ('.', ''): parts = parts[1:] return pjoin(*parts).replace(os.sep, '/') def _get_data_files(data_specs, existing, *, top=None, exclude=None): """Expand data file specs into valid data files metadata. Parameters ---------- data_specs: list of tuples See [create_cmdclass] for description. existing: list of tuples The existing distribution data_files metadata. top: str, optional The top directory exclude: func, optional Function used to test whether to exclude a file Returns ------- A valid list of data_files items. """ if top is None: top = os.path.abspath(os.getcwd()) # Extract the existing data files into a staging object. file_data = defaultdict(list) for (path, files) in existing or []: file_data[path] = files # Extract the files and assign them to the proper data # files path. for (path, dname, pattern) in data_specs or []: if os.path.isabs(dname): dname = os.path.relpath(dname, top) dname = dname.replace(os.sep, '/').rstrip('/') offset = 0 if dname in ('.', '') else len(dname) + 1 files = _get_files(_glob_pjoin(dname, pattern), top=top) for fname in files: # Normalize the path. root = os.path.dirname(fname) full_path = _glob_pjoin(path, root[offset:]) if full_path.endswith('/'): full_path = full_path[:-1] if exclude is not None and exclude(fname): continue file_data[full_path].append(fname) # Construct the data files spec. data_files = [] for (path, files) in file_data.items(): data_files.append((path, files)) return data_files def _get_files(file_patterns, top=None): """Expand file patterns to a list of paths. Parameters ----------- file_patterns: list or str A list of glob patterns for the data file locations. The globs can be recursive if they include a `**`. They should be relative paths from the top directory or absolute paths. top: str the directory to consider for data files Note: Files in `node_modules` are ignored. """ if top is None: top = os.path.abspath(os.getcwd()) if not isinstance(file_patterns, (list, tuple)): file_patterns = [file_patterns] for i, p in enumerate(file_patterns): if os.path.isabs(p): file_patterns[i] = os.path.relpath(p, top) matchers = [_compile_pattern(p) for p in file_patterns] files = set() for root, dirnames, filenames in os.walk(top): # Don't recurse into node_modules if 'node_modules' in dirnames: dirnames.remove('node_modules') for m in matchers: for filename in filenames: fn = os.path.relpath(_glob_pjoin(root, filename), top) fn = fn.replace(os.sep, '/') if m(fn): files.add(fn) return list(files) @deprecated(deprecated_in="0.8", removed_in="1.0", current_version=__version__, details="Use `npm_builder` and `wrap_installers`") def _get_package_data(root, file_patterns=None): """Expand file patterns to a list of `package_data` paths. Parameters ----------- root: str The relative path to the package root from the current dir. file_patterns: list or str, optional A list of glob patterns for the data file locations. The globs can be recursive if they include a `**`. They should be relative paths from the root or absolute paths. If not given, all files will be used. Note: Files in `node_modules` are ignored. """ if file_patterns is None: file_patterns = ['*'] return _get_files(file_patterns, _glob_pjoin(os.path.abspath(os.getcwd()), root)) def _compile_pattern(pat, ignore_case=True): """Translate and compile a glob pattern to a regular expression matcher.""" if isinstance(pat, bytes): pat_str = pat.decode('ISO-8859-1') res_str = _translate_glob(pat_str) res = res_str.encode('ISO-8859-1') else: res = _translate_glob(pat) flags = re.IGNORECASE if ignore_case else 0 return re.compile(res, flags=flags).match def _iexplode_path(path): """Iterate over all the parts of a path. Splits path recursively with os.path.split(). """ (head, tail) = os.path.split(path) if not head or (not tail and head == path): if head: yield head if tail or not head: yield tail return for p in _iexplode_path(head): yield p yield tail def _translate_glob(pat): """Translate a glob PATTERN to a regular expression.""" translated_parts = [] for part in _iexplode_path(pat): translated_parts.append(_translate_glob_part(part)) os_sep_class = '[%s]' % re.escape(SEPARATORS) res = _join_translated(translated_parts, os_sep_class) return '(?ms){res}\\Z'.format(res=res) def _join_translated(translated_parts, os_sep_class): """Join translated glob pattern parts. This is different from a simple join, as care need to be taken to allow ** to match ZERO or more directories. """ res = '' for part in translated_parts[:-1]: if part == '.*': # drop separator, since it is optional # (** matches ZERO or more dirs) res += part else: res += part + os_sep_class if translated_parts[-1] == '.*': # Final part is ** res += '.+' # Follow stdlib/git convention of matching all sub files/directories: res += '({os_sep_class}?.*)?'.format(os_sep_class=os_sep_class) else: res += translated_parts[-1] return res def _translate_glob_part(pat): """Translate a glob PATTERN PART to a regular expression.""" # Code modified from Python 3 standard lib fnmatch: if pat == '**': return '.*' i, n = 0, len(pat) res = [] while i < n: c = pat[i] i = i + 1 if c == '*': # Match anything but path separators: res.append('[^%s]*' % SEPARATORS) elif c == '?': res.append('[^%s]?' % SEPARATORS) elif c == '[': j = i if j < n and pat[j] == '!': j = j + 1 if j < n and pat[j] == ']': j = j + 1 while j < n and pat[j] != ']': j = j + 1 if j >= n: res.append('\\[') else: stuff = pat[i:j].replace('\\', '\\\\') i = j + 1 if stuff[0] == '!': stuff = '^' + stuff[1:] elif stuff[0] == '^': stuff = '\\' + stuff res.append('[%s]' % stuff) else: res.append(re.escape(c)) return ''.join(res) ipyparallel-7.1.0/tsconfig.eslint.json000066400000000000000000000001171412533144600200430ustar00rootroot00000000000000{ "extends": "./tsconfig", "include": [".eslintrc.js", "*", "lab/src/*"] } ipyparallel-7.1.0/tsconfig.json000066400000000000000000000010671412533144600165530ustar00rootroot00000000000000{ "compilerOptions": { "allowSyntheticDefaultImports": true, "composite": true, "declaration": true, "esModuleInterop": true, "incremental": true, "jsx": "react", "module": "esnext", "moduleResolution": "node", "noEmitOnError": true, "noImplicitAny": true, "noUnusedLocals": true, "preserveWatchOutput": true, "resolveJsonModule": true, "outDir": "lab/lib", "rootDir": "lab/src", "strict": true, "strictNullChecks": false, "target": "es2017", "types": [] }, "include": ["lab/src/*"] } ipyparallel-7.1.0/yarn.lock000066400000000000000000010337251412533144600156760ustar00rootroot00000000000000# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: "@babel/highlight" "^7.10.4" "@babel/helper-validator-identifier@^7.14.5": version "7.14.9" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48" integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g== "@babel/highlight@^7.10.4": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== dependencies: "@babel/helper-validator-identifier" "^7.14.5" chalk "^2.0.0" js-tokens "^4.0.0" "@babel/runtime@^7.1.2": version "7.15.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b" integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA== dependencies: regenerator-runtime "^0.13.4" "@blueprintjs/core@^3.36.0", "@blueprintjs/core@^3.47.0": version "3.47.0" resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-3.47.0.tgz#bf33155d224b742ba51c6e1cf5be4523290337a7" integrity sha512-u+bfmCyPXwKZMnwY4+e/iWjO2vDUvr8hA8ydmV0afyvcEe7Sh85UPEorIgQ/CBuRIbVMNm8FpLsFzDxgkfrCNA== dependencies: "@blueprintjs/icons" "^3.27.0" "@types/dom4" "^2.0.1" classnames "^2.2" dom4 "^2.1.5" normalize.css "^8.0.1" popper.js "^1.16.1" react-lifecycles-compat "^3.0.4" react-popper "^1.3.7" react-transition-group "^2.9.0" resize-observer-polyfill "^1.5.1" tslib "~1.13.0" "@blueprintjs/icons@^3.27.0": version "3.27.0" resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-3.27.0.tgz#f4c03e8bc2f9310f7eaefaab26dd91f65935da43" integrity sha512-ItRioyrr2s70chclj5q38HS9omKOa15b3JZXv9JcMIFz+6w6rAcoAH7DA+5xIs27bFjax/SdAZp/eYXSw0+QpA== dependencies: classnames "^2.2" tslib "~1.13.0" "@blueprintjs/select@^3.15.0": version "3.16.6" resolved "https://registry.yarnpkg.com/@blueprintjs/select/-/select-3.16.6.tgz#ae41a73bc7c23b07a20b0c50c71273c9d5d0d83d" integrity sha512-lg2duuzlRw18+pbET6vlRY/TVSuuSI6wI4DObUiBAfU7G3fMa6d10Sp+0Yn00XaMPQ5y3MGn1gz0EbIJ3/A5OA== dependencies: "@blueprintjs/core" "^3.47.0" classnames "^2.2" tslib "~1.13.0" "@discoveryjs/json-ext@^0.5.0": version "0.5.3" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d" integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g== "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== dependencies: ajv "^6.12.4" debug "^4.1.1" espree "^7.3.0" globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" minimatch "^3.0.4" strip-json-comments "^3.1.1" "@fortawesome/fontawesome-free@^5.12.0": version "5.15.4" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz#ecda5712b61ac852c760d8b3c79c96adca5554e5" integrity sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg== "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== dependencies: "@humanwhocodes/object-schema" "^1.2.0" debug "^4.1.1" minimatch "^3.0.4" "@humanwhocodes/object-schema@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== "@hypnosphi/create-react-context@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz#f8bfebdc7665f5d426cba3753e0e9c7d3154d7c6" integrity sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A== dependencies: gud "^1.0.0" warning "^4.0.3" "@jupyterlab/application@^3.0.0": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/application/-/application-3.1.4.tgz#53c8d7fd264ff942d4090ef129c204b3fd67140a" integrity sha512-GngdRCtDPF1GeRsTBGQ3dS2js9aatN7GvjdO4Chypw74hoH7EJnwNHsGO2hr6lLnA/EuKXNkeDvSmsNPjGVe2w== dependencies: "@fortawesome/fontawesome-free" "^5.12.0" "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/docregistry" "^3.1.4" "@jupyterlab/rendermime" "^3.1.4" "@jupyterlab/rendermime-interfaces" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/statedb" "^3.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/application" "^1.16.0" "@lumino/commands" "^1.12.0" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/messaging" "^1.4.3" "@lumino/polling" "^1.3.3" "@lumino/properties" "^1.2.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" "@jupyterlab/apputils@^3.0.0", "@jupyterlab/apputils@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/apputils/-/apputils-3.1.4.tgz#ba1d071b1b6712be1aa8f59f47556f7d4fd3488a" integrity sha512-UYAA6Uvg8a1GAuaJJLMKOXalD6VK9Ws76B3I38f69nOZzzjWTy1u7cXww8hxlfQjEquenBW/2zKV6Yu6c+8I3w== dependencies: "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/settingregistry" "^3.1.4" "@jupyterlab/statedb" "^3.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/commands" "^1.12.0" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/domutils" "^1.2.3" "@lumino/messaging" "^1.4.3" "@lumino/properties" "^1.2.3" "@lumino/signaling" "^1.4.3" "@lumino/virtualdom" "^1.8.0" "@lumino/widgets" "^1.19.0" "@types/react" "^17.0.0" react "^17.0.1" react-dom "^17.0.1" sanitize-html "~2.3.3" url "^0.11.0" "@jupyterlab/attachments@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/attachments/-/attachments-3.1.4.tgz#4ab8284cd741e332d4835eedbf2a9807d8b4ce7a" integrity sha512-dP0ASeirjFKTr7toT2j5+SMPJGwigYsUSQOuVe7pNctIGFTzHZmnqIqdZbnGucNdeNfR+mIJdvBaNVHdOGO1vw== dependencies: "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/rendermime" "^3.1.4" "@jupyterlab/rendermime-interfaces" "^3.1.4" "@lumino/disposable" "^1.4.3" "@lumino/signaling" "^1.4.3" "@jupyterlab/builder@^3.1.10": version "3.1.10" resolved "https://registry.yarnpkg.com/@jupyterlab/builder/-/builder-3.1.10.tgz#b0b53662de5598d172d3190336867ef8e2cb384e" integrity sha512-LM2Ti4KsDCthtEBCTuKLr5WxXZJqaXWCRRrj8+lbHXB8xiRndC9twpUC7lGBsrdrCSlXJ+Eew/ga9n/RLB/4mQ== dependencies: "@jupyterlab/buildutils" "^3.1.10" "@lumino/algorithm" "^1.3.3" "@lumino/application" "^1.16.0" "@lumino/commands" "^1.12.0" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/domutils" "^1.2.3" "@lumino/dragdrop" "^1.7.1" "@lumino/messaging" "^1.4.3" "@lumino/properties" "^1.2.3" "@lumino/signaling" "^1.4.3" "@lumino/virtualdom" "^1.8.0" "@lumino/widgets" "^1.19.0" ajv "^6.12.3" commander "~6.0.0" css-loader "^5.0.1" duplicate-package-checker-webpack-plugin "^3.0.0" file-loader "~6.0.0" fs-extra "^9.0.1" glob "~7.1.6" license-webpack-plugin "^2.3.14" mini-css-extract-plugin "~1.3.2" path-browserify "^1.0.0" process "^0.11.10" raw-loader "~4.0.0" style-loader "~2.0.0" supports-color "^7.2.0" svg-url-loader "~6.0.0" terser-webpack-plugin "^4.1.0" to-string-loader "^1.1.6" url-loader "~4.1.0" webpack "^5.41.1" webpack-cli "^4.1.0" webpack-merge "^5.1.2" worker-loader "^3.0.2" "@jupyterlab/buildutils@^3.1.10": version "3.1.10" resolved "https://registry.yarnpkg.com/@jupyterlab/buildutils/-/buildutils-3.1.10.tgz#1301d4a75b886d7688bb60b22d5b14a68ca19005" integrity sha512-9ZVp0G0yPOp2upU6gY9+Q2f9f0KfDY9BnHRc5VjrAVOx8+IoM6kMkjLbYZXn55J0SoKMO55XLxxBTJWNrt33ug== dependencies: "@lumino/coreutils" "^1.5.3" "@yarnpkg/lockfile" "^1.1.0" child_process "~1.0.2" commander "~6.0.0" crypto "~1.0.1" dependency-graph "^0.9.0" fs-extra "^9.0.1" glob "~7.1.6" inquirer "^7.0.0" minimatch "~3.0.4" os "~0.1.1" package-json "^6.5.0" prettier "~2.1.1" process "^0.11.10" semver "^7.3.2" sort-package-json "~1.44.0" typescript "~4.1.3" verdaccio "^5.1.1" "@jupyterlab/cells@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/cells/-/cells-3.1.4.tgz#2fc638a6160ac193a7387d3420ef490ccc12e7bc" integrity sha512-76X7KGDKv20297KRd1Qyj9C8XUulAasnyi6PbFo0RxXjWvfrwIkaTrBPxBpein+QBtAq7KWkY8TEMK/WLE+pOA== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/attachments" "^3.1.4" "@jupyterlab/codeeditor" "^3.1.4" "@jupyterlab/codemirror" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/filebrowser" "^3.1.4" "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/outputarea" "^3.1.4" "@jupyterlab/rendermime" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/shared-models" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/domutils" "^1.2.3" "@lumino/dragdrop" "^1.7.1" "@lumino/messaging" "^1.4.3" "@lumino/signaling" "^1.4.3" "@lumino/virtualdom" "^1.8.0" "@lumino/widgets" "^1.19.0" marked "^2.0.0" react "^17.0.1" "@jupyterlab/codeeditor@^3.0.0", "@jupyterlab/codeeditor@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/codeeditor/-/codeeditor-3.1.4.tgz#99ab3db8b4fc863135674d03a58f55366868cd49" integrity sha512-KI/q6bKa5CNe0aUVASNXCW1sbWnUVdsObncUjy4lvnuZgbNtHvgqITa69+P+L8jL293QBe2esAqmY9zbonbRbw== dependencies: "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/shared-models" "^3.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/dragdrop" "^1.7.1" "@lumino/messaging" "^1.4.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" "@jupyterlab/codemirror@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/codemirror/-/codemirror-3.1.4.tgz#248630fa1516b493bb4402939dde51cd55533de1" integrity sha512-4OKna9DHOdLO0G0DfoNXF3xJ/L0B5DuY0nBXMzLTp/LIbar1BeEXNu4CG28aJqUdjiuBP6TylfKVx+0NkiFG1A== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/codeeditor" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/shared-models" "^3.1.4" "@jupyterlab/statusbar" "^3.1.4" "@jupyterlab/translation" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/commands" "^1.12.0" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/polling" "^1.3.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" codemirror "~5.61.0" react "^17.0.1" y-codemirror "^2.1.1" "@jupyterlab/console@^3.0.0": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/console/-/console-3.1.4.tgz#3548ae4d694e7401010fdc32c4e7a1e79d5169bc" integrity sha512-T5als32nsQ6SQfVRMAs/gS02n0WHvUQfEZBpGTbwoxFJpjNU08G9nJ+niFUgvveW0k99yosMyttfSX9unmkrlA== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/cells" "^3.1.4" "@jupyterlab/codeeditor" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/rendermime" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/dragdrop" "^1.7.1" "@lumino/messaging" "^1.4.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" "@jupyterlab/coreutils@^5.0.0", "@jupyterlab/coreutils@^5.1.4": version "5.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/coreutils/-/coreutils-5.1.4.tgz#864b6a5fa4708b8da0b255171708cfdf2e251430" integrity sha512-Lt247hs6AqRGQFOUd/JaVElwT33Am5XPOW8wnVelXFOItqqkuQsDMv9Omkwb68UMCP5quWHswFf3S5vSE3ZoBw== dependencies: "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/signaling" "^1.4.3" minimist "~1.2.0" moment "^2.24.0" path-browserify "^1.0.0" url-parse "~1.5.1" "@jupyterlab/docmanager@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/docmanager/-/docmanager-3.1.4.tgz#524ee50730363dffdcaf3193230ca0edc6893946" integrity sha512-YtmEMyy7Q68t6iufo1EwBW3f+wRE+vXdLpwmc5pdhJSvRA2x3oxlcLgt+48au3xU0/na2NKNtiWXsAshA5xpJg== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/docprovider" "^3.1.4" "@jupyterlab/docregistry" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/statusbar" "^3.1.4" "@jupyterlab/translation" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/messaging" "^1.4.3" "@lumino/properties" "^1.2.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" react "^17.0.1" "@jupyterlab/docprovider@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/docprovider/-/docprovider-3.1.4.tgz#b2f2b6c3af97156bd5a3a4b2676445b45ec5f352" integrity sha512-vbsDeNgF14pGIo6x441vGybw9s1sxULfQJhBpavpXDfcVVD3Tq3DYhqgmEDw+d5xX1Nbt8Wj42Lmb/EBjbCm5w== dependencies: "@jupyterlab/shared-models" "^3.1.4" "@lumino/coreutils" "^1.5.3" lib0 "^0.2.42" y-websocket "^1.3.15" yjs "^13.5.6" "@jupyterlab/docregistry@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/docregistry/-/docregistry-3.1.4.tgz#1bd8b72131a373f336f25650028323af4164e962" integrity sha512-PEKFoRBgqXl2pJMqvg5E+0zFGN2eqUKRs8uJHAzZf5vI4nM4sihfu5G5dcpjZ3EjCygh3HIBu1myAE4gysBNgQ== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/codeeditor" "^3.1.4" "@jupyterlab/codemirror" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/docprovider" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/rendermime" "^3.1.4" "@jupyterlab/rendermime-interfaces" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/shared-models" "^3.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/messaging" "^1.4.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" yjs "^13.5.6" "@jupyterlab/filebrowser@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/filebrowser/-/filebrowser-3.1.4.tgz#8862d1534427e6d209d4e5c78c32217d46f2fd6a" integrity sha512-WYODTIvBIIBQKpZjnOqm99BmpgqO3hvuq70W342OCzfyxiQ/sGaZo9AjKhRIjRCBy+tHUVWS/Qf2qjkpl633WQ== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/docmanager" "^3.1.4" "@jupyterlab/docregistry" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/statedb" "^3.1.4" "@jupyterlab/statusbar" "^3.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/domutils" "^1.2.3" "@lumino/dragdrop" "^1.7.1" "@lumino/messaging" "^1.4.3" "@lumino/polling" "^1.3.3" "@lumino/signaling" "^1.4.3" "@lumino/virtualdom" "^1.8.0" "@lumino/widgets" "^1.19.0" react "^17.0.1" "@jupyterlab/mainmenu@^3.0.0": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/mainmenu/-/mainmenu-3.1.4.tgz#83ef9c069ac59551b117c630650c3b608dbea163" integrity sha512-z/RVkbUAIDp9Onmvpzv/ayJ/fY5MC4ual1s7I2YsOiRr9+LTAX0G4S0dy2LD32tV+Hr2SxM0vrSXaDA1tmngvA== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/commands" "^1.12.0" "@lumino/coreutils" "^1.5.3" "@lumino/widgets" "^1.19.0" "@jupyterlab/nbformat@^3.0.0", "@jupyterlab/nbformat@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/nbformat/-/nbformat-3.1.4.tgz#5da09fe6b96f572b97b1aaea004f81861914d5fe" integrity sha512-Dnj6+6QzsY1ruLaZanIVnGt5YhPYINQ4T/Cvn2PcZKXUFxX+olmn0zhTkrXThrVa2nk646I7PMxe2Dhb9qaeBA== dependencies: "@lumino/coreutils" "^1.5.3" "@jupyterlab/notebook@^3.0.0": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/notebook/-/notebook-3.1.4.tgz#f6f05f62835d6eb3eea8b7627514c564f1687176" integrity sha512-iJj/PjV0g1QgGvSqZDnGMijl9Lz0Vn9aUU3FXrHmYMsF0x7v/qgzHyDLajokV1YViImvGPWlgt46Joz3xPNu1w== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/cells" "^3.1.4" "@jupyterlab/codeeditor" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/docregistry" "^3.1.4" "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/rendermime" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/shared-models" "^3.1.4" "@jupyterlab/statusbar" "^3.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/domutils" "^1.2.3" "@lumino/dragdrop" "^1.7.1" "@lumino/messaging" "^1.4.3" "@lumino/properties" "^1.2.3" "@lumino/signaling" "^1.4.3" "@lumino/virtualdom" "^1.8.0" "@lumino/widgets" "^1.19.0" react "^17.0.1" "@jupyterlab/observables@^4.1.4": version "4.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/observables/-/observables-4.1.4.tgz#dc254aebb60e2167a69b80435fde2154892591c3" integrity sha512-veXoN1HssNCzK1YnSOq47NEVlvbe9GUVUNImnZorA3FDYtwjbu1zmqJNWW0l20mgm3QAFzNljYytGsprKwC7nQ== dependencies: "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/messaging" "^1.4.3" "@lumino/signaling" "^1.4.3" "@jupyterlab/outputarea@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/outputarea/-/outputarea-3.1.4.tgz#38f9d7c09aba6ca80036b70c967e7e305989acb7" integrity sha512-Qqe/TVCCYC6H5vMzUKD6xI1kIUB32/Dlrxg8+IEEpXP0894SLME75lWrNensYyt+S76KZwXjzaRynL1tvIw0gw== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/rendermime" "^3.1.4" "@jupyterlab/rendermime-interfaces" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/messaging" "^1.4.3" "@lumino/properties" "^1.2.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" resize-observer-polyfill "^1.5.1" "@jupyterlab/rendermime-interfaces@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-3.1.4.tgz#cad084f2efe62db0bbadc8a1b52171cd246d2142" integrity sha512-5sh3OZ6FN276HUHlrS6yzOclKbABsHb/97AWzHuE3zydfjGFQltNKi9IWtxVxPZ16NIFIot5lmm4kK0KMCUkSw== dependencies: "@jupyterlab/translation" "^3.1.4" "@lumino/coreutils" "^1.5.3" "@lumino/widgets" "^1.19.0" "@jupyterlab/rendermime@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime/-/rendermime-3.1.4.tgz#dbd703c4a3ed8416ec497a9b094f8e122c608a62" integrity sha512-W8LfDwd8SxsqumL+91zOWLv6d88kxN8kFAGiT9bKLsv3cg5isVNWfjT3A8pLBWgJC7hYQIsG7mwkCr6rIYV48A== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/codemirror" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/rendermime-interfaces" "^3.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/translation" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/messaging" "^1.4.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" lodash.escape "^4.0.1" marked "^2.0.0" "@jupyterlab/services@^6.0.0", "@jupyterlab/services@^6.1.4": version "6.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/services/-/services-6.1.4.tgz#ffe08c9bc9c67af24ac65d1cc6a68a962a958df0" integrity sha512-bzZV1DFFuxJGAg+WjykWr5GwVSjjXZJ6ACO4P4EkkkJUOv5kzF/WGYtXv6/CYK48ia1E3IIyyP73vmSo3H2sZw== dependencies: "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/nbformat" "^3.1.4" "@jupyterlab/observables" "^4.1.4" "@jupyterlab/settingregistry" "^3.1.4" "@jupyterlab/statedb" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/polling" "^1.3.3" "@lumino/signaling" "^1.4.3" node-fetch "^2.6.0" ws "^7.4.6" "@jupyterlab/settingregistry@^3.0.0", "@jupyterlab/settingregistry@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/settingregistry/-/settingregistry-3.1.4.tgz#4a24d99f4c3e0e8731bbfa285af20973f26823d8" integrity sha512-vPSGcjFRqW80dGng5T2rte0r9It6YMItR4rAkLpfTMCFmzSVyyCnIByoZ2xTInyEt+HuznGauXQSZZCzTJNkJg== dependencies: "@jupyterlab/statedb" "^3.1.4" "@lumino/commands" "^1.12.0" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/signaling" "^1.4.3" ajv "^6.12.3" json5 "^2.1.1" "@jupyterlab/shared-models@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/shared-models/-/shared-models-3.1.4.tgz#d7174d7424f58d0c3862994562570eca7c80df28" integrity sha512-tkwbJtwrCbTFW83WIFnux4qw4E5jW78gTRU9vdBGwVUD+h0pZma655KUkDW34xdIT4AiMMIGhx38TPSMAEbijg== dependencies: "@jupyterlab/nbformat" "^3.1.4" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/signaling" "^1.4.3" y-protocols "^1.0.5" yjs "^13.5.6" "@jupyterlab/statedb@^3.0.0", "@jupyterlab/statedb@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/statedb/-/statedb-3.1.4.tgz#f00eb22ab2c2417899780ff0f9849dc466a7a66f" integrity sha512-wmC2tDv4VHzNXExsdXBA7ypaeO2e7HwhYENAPI5yMOu/DWErRNBNNonH1Ew493bsEkw8XMmu5D4p/wOQLIlzHQ== dependencies: "@lumino/commands" "^1.12.0" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/properties" "^1.2.3" "@lumino/signaling" "^1.4.3" "@jupyterlab/statusbar@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/statusbar/-/statusbar-3.1.4.tgz#5ba60636f11280942fbda802ac05e5db10f3666e" integrity sha512-TC3LL5jvD9itbY9Vjdu6GQRPP9IW++Me+1dnjx4IS0dK7fSze8q/oUj+4e0Q2k7AGjBcli7N4U5GdkZLq5qDVA== dependencies: "@jupyterlab/apputils" "^3.1.4" "@jupyterlab/codeeditor" "^3.1.4" "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/translation" "^3.1.4" "@jupyterlab/ui-components" "^3.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/messaging" "^1.4.3" "@lumino/signaling" "^1.4.3" "@lumino/widgets" "^1.19.0" csstype "~3.0.3" react "^17.0.1" typestyle "^2.0.4" "@jupyterlab/translation@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/translation/-/translation-3.1.4.tgz#d5393ab3dced8969c20c52aeaf4c8170c615c52b" integrity sha512-3ptqhDGhKuz6ur7I/lYbO3iH0ugUN3dXuB143geQnXprTe2AUss1/kA6Bds19tqqF+gz3VOfRc0USvoojXRLgQ== dependencies: "@jupyterlab/coreutils" "^5.1.4" "@jupyterlab/services" "^6.1.4" "@jupyterlab/statedb" "^3.1.4" "@lumino/coreutils" "^1.5.3" "@jupyterlab/ui-components@^3.0.0", "@jupyterlab/ui-components@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@jupyterlab/ui-components/-/ui-components-3.1.4.tgz#a170c153217b7356a5349d4cd6bd6eda36adade2" integrity sha512-Jkr2LK/zh0//vd3avLunDZAyOjCvaGDtJYTALL8w3IXtSDAfMZlCtntOFcLFqJLuGrdIQkZLYn1Kl/KqKc0YMA== dependencies: "@blueprintjs/core" "^3.36.0" "@blueprintjs/select" "^3.15.0" "@jupyterlab/coreutils" "^5.1.4" "@lumino/algorithm" "^1.3.3" "@lumino/coreutils" "^1.5.3" "@lumino/disposable" "^1.4.3" "@lumino/signaling" "^1.4.3" "@lumino/virtualdom" "^1.8.0" "@lumino/widgets" "^1.19.0" react "^17.0.1" react-dom "^17.0.1" typestyle "^2.0.4" "@lumino/algorithm@^1.3.3", "@lumino/algorithm@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@lumino/algorithm/-/algorithm-1.6.0.tgz#771e7896cd94e660f9b58a52f80e1bb255de1d41" integrity sha512-NMOcm5Yr9nXz5gokS/K4jHBbUMQYBkvDXl1n51XWdcz0LY+oGuIKPhjazhUgmbNRehzdZBj5hMMd1+htYWeVKQ== "@lumino/application@^1.16.0": version "1.23.0" resolved "https://registry.yarnpkg.com/@lumino/application/-/application-1.23.0.tgz#5c04ee2cedcd46d2438930c6ea8af953037794fe" integrity sha512-B4xP4GsOmo1Sx8H9dXhCPmDUdSZYhlzYTApooGR8rBsYo+TVBlct/48/1C2NRiAjCzOQWsrQO+D9bIzDAnTDiw== dependencies: "@lumino/commands" "^1.15.0" "@lumino/coreutils" "^1.8.0" "@lumino/widgets" "^1.26.0" "@lumino/collections@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@lumino/collections/-/collections-1.6.0.tgz#7d3e94cee26409b0cd719c1934bdda471e6a5662" integrity sha512-ZETm0/xF0oUHV03sOXNOfFI1EEpS317HvN5n+fZBJvCNZIrJkWmKD8QuxcfwHb7AChKUhXlVHhDbWlb1LKnd7g== dependencies: "@lumino/algorithm" "^1.6.0" "@lumino/commands@^1.12.0", "@lumino/commands@^1.15.0": version "1.15.0" resolved "https://registry.yarnpkg.com/@lumino/commands/-/commands-1.15.0.tgz#06eb94fb4b34cad59f35b1fcaf473e8d2047f779" integrity sha512-JOE68KfbR9xw5YTfcwo+9E0PSWidifEMAcOC/aXd7iSzfsCRknMTcMQIUGL277IU7J7CJvoe10DUE5QKwTmX+g== dependencies: "@lumino/algorithm" "^1.6.0" "@lumino/coreutils" "^1.8.0" "@lumino/disposable" "^1.7.0" "@lumino/domutils" "^1.5.0" "@lumino/keyboard" "^1.5.0" "@lumino/signaling" "^1.7.0" "@lumino/virtualdom" "^1.11.0" "@lumino/coreutils@^1.5.3", "@lumino/coreutils@^1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@lumino/coreutils/-/coreutils-1.8.0.tgz#4feb3ccbfbc3efc8e395a90f22b5a938fbad959a" integrity sha512-OvCsaASUqOE7R6Dxngyk4/b5QMOjyRUNxuZuuL+fx+JvGKZFZ/B2c9LYtAJ9mDmQ1BQiGNV/qSpL4o7x8PCfjw== "@lumino/disposable@^1.4.3", "@lumino/disposable@^1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@lumino/disposable/-/disposable-1.7.0.tgz#539463490cb42e8d2dc46b5ff7cc291f4f1a8d07" integrity sha512-3mWi11ko3XVY63BPwvys7MXrbFddA2i+gp72d0wAKM2NDDUopVPikMHhJpjGJcw+otjahzXYiTewxPDEau9dYg== dependencies: "@lumino/algorithm" "^1.6.0" "@lumino/signaling" "^1.7.0" "@lumino/domutils@^1.2.3", "@lumino/domutils@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@lumino/domutils/-/domutils-1.5.0.tgz#fdba0cfe404b4817e63aa064f63b3c965655e76e" integrity sha512-dZ0Aa+/qhvfPc1aa5kX4LLGE3B6BW1XmJa0R1XVCEpAFY3cZiujuQWmhYHJtZPrOiqn0UtioT2OpqnWdtCWc0A== "@lumino/dragdrop@^1.10.0", "@lumino/dragdrop@^1.7.1": version "1.10.0" resolved "https://registry.yarnpkg.com/@lumino/dragdrop/-/dragdrop-1.10.0.tgz#2fddacfee055e660dd33dd9a3cfbd8fbba811673" integrity sha512-A3cNLcp09zygOprWmLTkLZCQYNq3dJfN+mhni4IZizqCTkKbTCEzo2/IwoCWvy+ABKft8d/A9Y40wFW6yJ9OyA== dependencies: "@lumino/coreutils" "^1.8.0" "@lumino/disposable" "^1.7.0" "@lumino/keyboard@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@lumino/keyboard/-/keyboard-1.5.0.tgz#c12213822dd2645c412e8689aecd4a2726113ac6" integrity sha512-/uF9xqHYVbIkser2Q6UIv7VWrzThr1fxAmSOShjSoKGocL0XHeaBaCOMezSaVxnJ1wm1ciNdhMsjscVM8Inp7g== "@lumino/messaging@^1.4.3", "@lumino/messaging@^1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@lumino/messaging/-/messaging-1.7.0.tgz#32542f9e9a266fd5b3f71842f70cfe141e016d93" integrity sha512-QYWf9QGIGD0Oes104zw7mVln4S8yRije2mZhNNRBjkYcDuQlPW+eRSuC5LwAMsFnGymBlUPwPbKOUEO2RbhAtg== dependencies: "@lumino/algorithm" "^1.6.0" "@lumino/collections" "^1.6.0" "@lumino/polling@^1.0.4", "@lumino/polling@^1.3.3": version "1.6.0" resolved "https://registry.yarnpkg.com/@lumino/polling/-/polling-1.6.0.tgz#64f40bba4602fe9eceb9f3fae8f3647831e5b7e9" integrity sha512-jG1nqw6UO5XEN7QamOr6iDW8WvYeZQcBVRjM38fszz62dwJ/VGPvO2hlNl6QWWIfCynbJudms0LQm+z0BT1EdA== dependencies: "@lumino/coreutils" "^1.8.0" "@lumino/disposable" "^1.7.0" "@lumino/signaling" "^1.7.0" "@lumino/properties@^1.2.3", "@lumino/properties@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@lumino/properties/-/properties-1.5.0.tgz#7e8638e84c51bb110c5a69f91ca8b0e40b2c3fca" integrity sha512-YqpJE6/1Wkjrie0E+ypu+yzd55B5RlvKYMnQs3Ox+SrJsnNBhA6Oj44EhVf8SUTuHgn1t/mm+LvbswKN5RM4+g== "@lumino/signaling@^1.4.3", "@lumino/signaling@^1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@lumino/signaling/-/signaling-1.7.0.tgz#76da4738bf8f19e7da6de1d457a54220e2140670" integrity sha512-a5kd11Sf04jTfpzxCr7TOBD2o5YvItA4IGwiOoG+QR6sPR0Rwmcf47fPItqXo5st58iNIblC3F+c264N+Me+gg== dependencies: "@lumino/algorithm" "^1.6.0" "@lumino/virtualdom@^1.11.0", "@lumino/virtualdom@^1.8.0": version "1.11.0" resolved "https://registry.yarnpkg.com/@lumino/virtualdom/-/virtualdom-1.11.0.tgz#468b4d28a07e2b8988dc583b4aab40e37dc6955e" integrity sha512-G0sIx4pLYbgJ4w+SIgsCYQgKP/GBrWgjh8wcumD6XpaYZNivJv4c01xITYYlh7FU61jZmMWMrxtZztArNRDSqg== dependencies: "@lumino/algorithm" "^1.6.0" "@lumino/widgets@^1.17.0", "@lumino/widgets@^1.19.0", "@lumino/widgets@^1.26.0": version "1.26.0" resolved "https://registry.yarnpkg.com/@lumino/widgets/-/widgets-1.26.0.tgz#be030a837ac82008aaef3181c9d69e6cb9fd8611" integrity sha512-B8XHR8Rk49LkP2IReVf4NaVGjs8EIIKP+GDoMIiTurQno2MVyWM5ADGtHuwNeYyj1vTynBDp6zGa0aIxcwEHGw== dependencies: "@lumino/algorithm" "^1.6.0" "@lumino/commands" "^1.15.0" "@lumino/coreutils" "^1.8.0" "@lumino/disposable" "^1.7.0" "@lumino/domutils" "^1.5.0" "@lumino/dragdrop" "^1.10.0" "@lumino/keyboard" "^1.5.0" "@lumino/messaging" "^1.7.0" "@lumino/properties" "^1.5.0" "@lumino/signaling" "^1.7.0" "@lumino/virtualdom" "^1.11.0" "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" "@npmcli/move-file@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== dependencies: mkdirp "^1.0.4" rimraf "^3.0.2" "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== dependencies: defer-to-connect "^1.0.1" "@types/dom4@^2.0.1": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.2.tgz#6495303f049689ce936ed328a3e5ede9c51408ee" integrity sha512-Rt4IC1T7xkCWa0OG1oSsPa0iqnxlDeQqKXZAHrQGLb7wFGncWm85MaxKUjAGejOrUynOgWlFi4c6S6IyJwoK4g== "@types/eslint-scope@^3.7.0": version "3.7.1" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.1.tgz#8dc390a7b4f9dd9f1284629efce982e41612116e" integrity sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g== dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== "@types/eslint@*": version "7.28.0" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.0.tgz#7e41f2481d301c68e14f483fe10b017753ce8d5a" integrity sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*", "@types/estree@^0.0.50": version "0.0.50" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== "@types/glob@^7.1.1": version "7.1.4" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.4.tgz#ea59e21d2ee5c517914cb4bc8e4153b99e566672" integrity sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA== dependencies: "@types/minimatch" "*" "@types/node" "*" "@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== "@types/minimatch@*": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/node@*": version "16.4.14" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.14.tgz#e27705ec2278b2355bd59f1952de23a152b9f208" integrity sha512-GZpnVRNtv7sHDXIFncsERt+qvj4rzAgRQtnvzk3Z7OVNtThD2dHXYCMDNc80D5mv4JE278qo8biZCwcmkbdpqw== "@types/prop-types@*": version "15.7.4" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== "@types/react-dom@^17.0.0": version "17.0.9" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add" integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg== dependencies: "@types/react" "*" "@types/react@*", "@types/react@^17.0.0", "@types/react@~17.0.0": version "17.0.17" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.17.tgz#1772d3d5425128e0635a716f49ef57c2955df055" integrity sha512-nrfi7I13cAmrd0wje8czYpf5SFbryczCtPzFc6ijqvdjKcyA3tCvGxwchOUlxb2ucBPuJ9Y3oUqKrRqZvrz0lw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/scheduler@*": version "0.16.2" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== "@types/webpack-sources@^0.1.5": version "0.1.9" resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.9.tgz#da69b06eb34f6432e6658acb5a6893c55d983920" integrity sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new== dependencies: "@types/node" "*" "@types/source-list-map" "*" source-map "^0.6.1" "@typescript-eslint/eslint-plugin@^2.27.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== dependencies: "@typescript-eslint/experimental-utils" "2.34.0" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" tsutils "^3.17.1" "@typescript-eslint/experimental-utils@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== dependencies: "@types/json-schema" "^7.0.3" "@typescript-eslint/typescript-estree" "2.34.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^2.27.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== dependencies: "@types/eslint-visitor-keys" "^1.0.0" "@typescript-eslint/experimental-utils" "2.34.0" "@typescript-eslint/typescript-estree" "2.34.0" eslint-visitor-keys "^1.1.0" "@typescript-eslint/typescript-estree@2.34.0": version "2.34.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== dependencies: debug "^4.1.1" eslint-visitor-keys "^1.1.0" glob "^7.1.6" is-glob "^4.0.1" lodash "^4.17.15" semver "^7.3.2" tsutils "^3.17.1" "@verdaccio/commons-api@10.0.0": version "10.0.0" resolved "https://registry.yarnpkg.com/@verdaccio/commons-api/-/commons-api-10.0.0.tgz#2d7de8722f94181f1a71891fe91198a7c14e6dea" integrity sha512-UC8wrRI9FvqjfDeB1RijF7aVI0JJhCOI8RkEDibCT/JD8zVngphrNmgSWcjo8Es3lRiu7NugWXDSuggCCeCfUg== dependencies: http-errors "1.8.0" http-status-codes "1.4.0" "@verdaccio/file-locking@10.0.0", "@verdaccio/file-locking@^10.0.0": version "10.0.0" resolved "https://registry.yarnpkg.com/@verdaccio/file-locking/-/file-locking-10.0.0.tgz#3d476a6ba28207c795d49828438e7335166c1cfc" integrity sha512-2tQUbJF3tQ3CY9grAlpovaF/zu8G56CBYMaeHwMBHo9rAmsJI9i7LfliHGS6Jygbs8vd0cOCPT7vl2CL9T8upw== dependencies: lockfile "1.0.4" "@verdaccio/local-storage@10.0.6": version "10.0.6" resolved "https://registry.yarnpkg.com/@verdaccio/local-storage/-/local-storage-10.0.6.tgz#be485a8107ad84206cf80702d325ca47b7f22f68" integrity sha512-YEImOMUL56lziS/N3o1YzoOcVGZXpyZclGSonw7XQ1lKQEvEhU06V2+tIdjPgtqIOuH9ZKdPeBsBuN7ILa2qzQ== dependencies: "@verdaccio/commons-api" "10.0.0" "@verdaccio/file-locking" "10.0.0" "@verdaccio/streams" "10.0.0" async "3.2.0" debug "4.3.1" lodash "4.17.21" lowdb "1.0.0" mkdirp "1.0.4" "@verdaccio/readme@10.0.0": version "10.0.0" resolved "https://registry.yarnpkg.com/@verdaccio/readme/-/readme-10.0.0.tgz#f9627c32b309ace311318b98b2c42226823f6cd7" integrity sha512-OD3dMnRC8SvhgytEzczMBleN+K/3lMqyWw/epeXvolCpCd7mW/Dl5zSR25GiHh/2h3eTKP/HMs4km8gS1MMLgA== dependencies: dompurify "^2.2.6" jsdom "15.2.1" marked "^2.0.1" "@verdaccio/streams@10.0.0": version "10.0.0" resolved "https://registry.yarnpkg.com/@verdaccio/streams/-/streams-10.0.0.tgz#8b06e1d6f06e906ebda0f1d4089cdb651a533541" integrity sha512-PqxxY11HhweN6z1lwfn9ydLCdnOkCPpthMZs+SGCDz8Rt6gOyrjJVslV7o4uobDipjD9+hUPpJHDeO33Qt24uw== "@verdaccio/ui-theme@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@verdaccio/ui-theme/-/ui-theme-3.1.0.tgz#21108f3c1b97e6db5901509d935e1f4ce475950a" integrity sha512-NmJOcv25/OtF84YrmYxi31beFde7rt+/y2qlnq0wYR4ZCFRE5TsuqisTVTe1OyJ8D8JwwPMyMSMSMtlMwUfqIQ== "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== dependencies: "@webassemblyjs/helper-numbers" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/floating-point-hex-parser@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== "@webassemblyjs/helper-api-error@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== "@webassemblyjs/helper-buffer@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== "@webassemblyjs/helper-numbers@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== dependencies: "@webassemblyjs/floating-point-hex-parser" "1.11.1" "@webassemblyjs/helper-api-error" "1.11.1" "@xtuc/long" "4.2.2" "@webassemblyjs/helper-wasm-bytecode@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== "@webassemblyjs/helper-wasm-section@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/wasm-gen" "1.11.1" "@webassemblyjs/ieee754@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== dependencies: "@xtuc/ieee754" "^1.2.0" "@webassemblyjs/leb128@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== dependencies: "@xtuc/long" "4.2.2" "@webassemblyjs/utf8@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== "@webassemblyjs/wasm-edit@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/helper-wasm-section" "1.11.1" "@webassemblyjs/wasm-gen" "1.11.1" "@webassemblyjs/wasm-opt" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" "@webassemblyjs/wast-printer" "1.11.1" "@webassemblyjs/wasm-gen@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/ieee754" "1.11.1" "@webassemblyjs/leb128" "1.11.1" "@webassemblyjs/utf8" "1.11.1" "@webassemblyjs/wasm-opt@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-buffer" "1.11.1" "@webassemblyjs/wasm-gen" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" "@webassemblyjs/wasm-parser@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== dependencies: "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/helper-api-error" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/ieee754" "1.11.1" "@webassemblyjs/leb128" "1.11.1" "@webassemblyjs/utf8" "1.11.1" "@webassemblyjs/wast-printer@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== dependencies: "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" "@webpack-cli/configtest@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.4.tgz#f03ce6311c0883a83d04569e2c03c6238316d2aa" integrity sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ== "@webpack-cli/info@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.3.0.tgz#9d78a31101a960997a4acd41ffd9b9300627fe2b" integrity sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w== dependencies: envinfo "^7.7.3" "@webpack-cli/serve@^1.5.1": version "1.5.1" resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.5.1.tgz#b5fde2f0f79c1e120307c415a4c1d5eb15a6f278" integrity sha512-4vSVUiOPJLmr45S8rMGy7WDvpWxfFxfP/Qx/cxZFCfvoypTYpPPL1X8VIZMe0WTA+Jr7blUxwUSEZNkjoMTgSw== "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== "@xtuc/long@4.2.2": version "4.2.2" resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== "@yarnpkg/lockfile@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== JSONStream@1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== dependencies: jsonparse "^1.2.0" through ">=2.2.7 <3" abab@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abstract-leveldown@^6.2.1: version "6.3.0" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== dependencies: buffer "^5.5.0" immediate "^3.2.3" level-concat-iterator "~2.0.0" level-supports "~1.0.0" xtend "~4.0.0" abstract-leveldown@~6.2.1, abstract-leveldown@~6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== dependencies: buffer "^5.5.0" immediate "^3.2.3" level-concat-iterator "~2.0.0" level-supports "~1.0.0" xtend "~4.0.0" accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: mime-types "~2.1.24" negotiator "0.6.2" acorn-globals@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== dependencies: acorn "^6.0.1" acorn-walk "^6.0.1" acorn-import-assertions@^1.7.6: version "1.7.6" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz#580e3ffcae6770eebeec76c3b9723201e9d01f78" integrity sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA== acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^6.0.1: version "6.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== acorn@^6.0.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== acorn@^7.1.0, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.4.1: version "8.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== dependencies: clean-stack "^2.0.0" indent-string "^4.0.0" ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" ajv@^8.0.1: version "8.6.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571" integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" uri-js "^4.2.2" ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" apache-md5@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/apache-md5/-/apache-md5-1.1.2.tgz#ee49736b639b4f108b6e9e626c6da99306b41692" integrity sha1-7klza2ObTxCLbp5ibG2pkwa0FpI= argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-includes@^3.1.2, array-includes@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a" integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.18.0-next.2" get-intrinsic "^1.1.1" is-string "^1.0.5" array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array.prototype.flatmap@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q== dependencies: call-bind "^1.0.0" define-properties "^1.1.3" es-abstract "^1.18.0-next.1" function-bind "^1.1.1" asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== atomic-sleep@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: version "1.11.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" bcryptjs@2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== body-parser@1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== dependencies: bytes "3.1.0" content-type "~1.0.4" debug "2.6.9" depd "~1.1.2" http-errors "1.7.2" iconv-lite "0.4.24" on-finished "~2.3.0" qs "6.7.0" raw-body "2.4.0" type-is "~1.6.17" brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" braces@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== browserslist@^4.14.5: version "4.16.7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.7.tgz#108b0d1ef33c4af1b587c54f390e7041178e4335" integrity sha512-7I4qVwqZltJ7j37wObBe3SoTz+nS8APaNcrBOlgoirb6/HbEU2XxW/LpUDTCngM6iauwFqmRTuOMfyKnFGY5JA== dependencies: caniuse-lite "^1.0.30001248" colorette "^1.2.2" electron-to-chromium "^1.3.793" escalade "^3.1.1" node-releases "^1.1.73" buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" ieee754 "^1.1.13" bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cacache@^15.0.5: version "15.2.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.2.0.tgz#73af75f77c58e72d8c630a7a2858cb18ef523389" integrity sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw== dependencies: "@npmcli/move-file" "^1.0.1" chownr "^2.0.0" fs-minipass "^2.0.0" glob "^7.1.4" infer-owner "^1.0.4" lru-cache "^6.0.0" minipass "^3.1.1" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.2" mkdirp "^1.0.3" p-map "^4.0.0" promise-inflight "^1.0.1" rimraf "^3.0.2" ssri "^8.0.1" tar "^6.0.2" unique-filename "^1.1.1" cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== dependencies: clone-response "^1.0.2" get-stream "^5.1.0" http-cache-semantics "^4.0.0" keyv "^3.0.0" lowercase-keys "^2.0.0" normalize-url "^4.1.0" responselike "^1.0.2" call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" get-intrinsic "^1.0.2" callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== caniuse-lite@^1.0.30001248: version "1.0.30001249" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001249.tgz#90a330057f8ff75bfe97a94d047d5e14fabb2ee8" integrity sha512-vcX4U8lwVXPdqzPWi6cAJ3FnQaqXbBqy/GZseKNQzRj37J7qZdGcBtxq/QLFNLLlfsoXLUdHw8Iwenri86Tagw== caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== child_process@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/child_process/-/child_process-1.0.2.tgz#b1f7e7fc73d25e7fd1d455adc94e143830182b5a" integrity sha1-sffn/HPSXn/R1FWtyU4UODAYK1o= chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== classnames@^2.2: version "2.3.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== clipanion@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-3.0.0.tgz#efe35d17f46d9cf71be5eb11c36e9a05caa6cf68" integrity sha512-Ae2/PVkgMM/EbiGugE0p0QcjXyuh5hP+yzpJYaIElQ0g0kkuQKEHuYkLyk4K8gNkO1KKlHZ6UWgxRaTOMtmiLw== dependencies: typanion "^3.3.1" cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== dependencies: string-width "^3.1.0" strip-ansi "^5.2.0" wrap-ansi "^5.1.0" clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: is-plain-object "^2.0.4" kind-of "^6.0.2" shallow-clone "^3.0.0" clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= dependencies: mimic-response "^1.0.0" codemirror@~5.61.0: version "5.61.1" resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.61.1.tgz#ccfc8a43b8fcfb8b12e8e75b5ffde48d541406e0" integrity sha512-+D1NZjAucuzE93vJGbAaXzvoBHwp9nJZWWWF9utjv25+5AZUiah6CIlfb4ikG4MoDsFsCG8niiJH5++OO2LgIQ== color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^1.2.1, colorette@^1.2.2: version "1.3.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af" integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w== combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@^7.0.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== commander@~6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/commander/-/commander-6.0.0.tgz#2b270da94f8fb9014455312f829a1129dbf8887e" integrity sha512-s7EA+hDtTYNhuXkTlhqew4txMZVdszBmKWSPEMxGr8ru8JXR7bLUFIAtPhcSuFdJQ0ILMxnJi8GkQL0yvDy/YA== commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" compression@1.7.4: version "1.7.4" resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== dependencies: accepts "~1.3.5" bytes "3.0.0" compressible "~2.0.16" debug "2.6.9" on-headers "~1.0.2" safe-buffer "5.1.2" vary "~1.1.2" concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= content-disposition@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== dependencies: safe-buffer "5.1.2" content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== cookies@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== dependencies: depd "~2.0.0" keygrip "~1.1.0" core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cors@2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" vary "^1" cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" semver "^5.5.0" shebang-command "^1.2.0" which "^1.2.9" cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" crypto@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== css-loader@^5.0.1: version "5.2.7" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg== dependencies: icss-utils "^5.1.0" loader-utils "^2.0.0" postcss "^8.2.15" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" postcss-modules-values "^4.0.0" postcss-value-parser "^4.1.0" schema-utils "^3.0.0" semver "^7.3.5" cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== cssom@^0.4.1: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== cssom@~0.3.6: version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== cssstyle@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" csstype@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== csstype@^3.0.2, csstype@~3.0.3: version "3.0.8" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== dependencies: es5-ext "^0.10.50" type "^1.0.1" dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" data-urls@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== dependencies: abab "^2.0.0" whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" dayjs@1.10.6: version "1.10.6" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== debug@2.6.9, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: ms "^2.1.1" debug@4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" debug@^4.0.1, debug@^4.1.1, debug@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= dependencies: mimic-response "^1.0.0" deep-equal@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== dependencies: is-arguments "^1.0.4" is-date-object "^1.0.1" is-regex "^1.0.4" object-is "^1.0.1" object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== deferred-leveldown@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== dependencies: abstract-leveldown "~6.2.1" inherits "^2.0.3" define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: object-keys "^1.0.12" delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== dependency-graph@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.9.0.tgz#11aed7e203bc8b00f48356d92db27b265c445318" integrity sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w== destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-newline@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" dom-helpers@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== dependencies: "@babel/runtime" "^7.1.2" dom-serializer@^1.0.1: version "1.3.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== dependencies: domelementtype "^2.0.1" domhandler "^4.2.0" entities "^2.0.0" dom4@^2.1.5: version "2.1.6" resolved "https://registry.yarnpkg.com/dom4/-/dom4-2.1.6.tgz#c90df07134aa0dbd81ed4d6ba1237b36fc164770" integrity sha512-JkCVGnN4ofKGbjf5Uvc8mmxaATIErKQKSgACdBXpsQ3fY6DlIpAyWfiBSrGkttATssbDCp3psiAKWXk5gmjycA== domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== dependencies: webidl-conversions "^4.0.2" domhandler@^4.0.0, domhandler@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== dependencies: domelementtype "^2.2.0" dompurify@^2.2.6: version "2.3.0" resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.0.tgz#07bb39515e491588e5756b1d3e8375b5964814e2" integrity sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw== domutils@^2.5.2: version "2.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg== dependencies: dom-serializer "^1.0.1" domelementtype "^2.2.0" domhandler "^4.2.0" duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= duplicate-package-checker-webpack-plugin@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/duplicate-package-checker-webpack-plugin/-/duplicate-package-checker-webpack-plugin-3.0.0.tgz#78bb89e625fa7cf8c2a59c53f62b495fda9ba287" integrity sha512-aO50/qPC7X2ChjRFniRiscxBLT/K01bALqfcDaf8Ih5OqQ1N4iT/Abx9Ofu3/ms446vHTm46FACIuJUmgUQcDQ== dependencies: chalk "^2.3.0" find-root "^1.0.0" lodash "^4.17.4" semver "^5.4.1" ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" ecdsa-sig-formatter@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== dependencies: safe-buffer "^5.0.1" ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.793: version "1.3.802" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.802.tgz#0afa989321de3e904ac653ee79e0d642883731a1" integrity sha512-dXB0SGSypfm3iEDxrb5n/IVKeX4uuTnFHdve7v+yKJqNpEP0D4mjFJ8e1znmSR+OOVlVC+kDO6f2kAkTFXvJBg== emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= encoding-down@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== dependencies: abstract-leveldown "^6.2.1" inherits "^2.0.3" level-codec "^9.0.0" level-errors "^2.0.0" end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" enhanced-resolve@^5.8.0: version "5.8.2" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b" integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" enquirer@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== envinfo@7.8.1, envinfo@^7.7.3: version "7.8.1" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== errno@~0.1.1: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== dependencies: prr "~1.0.1" error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: version "1.18.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.5.tgz#9b10de7d4c206a3581fd5b2124233e04db49ae19" integrity sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" get-intrinsic "^1.1.1" has "^1.0.3" has-symbols "^1.0.2" internal-slot "^1.0.3" is-callable "^1.2.3" is-negative-zero "^2.0.1" is-regex "^1.1.3" is-string "^1.0.6" object-inspect "^1.11.0" object-keys "^1.1.1" object.assign "^4.1.2" string.prototype.trimend "^1.0.4" string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" es-module-lexer@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d" integrity sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw== es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" is-symbol "^1.0.2" es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.3" next-tick "~1.0.0" es6-iterator@^2.0.3, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= dependencies: d "1" es5-ext "^0.10.35" es6-symbol "^3.1.1" es6-symbol@^3.1.1, es6-symbol@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== dependencies: d "^1.0.1" ext "^1.1.2" es6-weak-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== dependencies: d "1" es5-ext "^0.10.46" es6-iterator "^2.0.3" es6-symbol "^3.1.1" escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escodegen@^1.11.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== dependencies: esprima "^4.0.1" estraverse "^4.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: source-map "~0.6.1" eslint-config-prettier@^6.10.1: version "6.15.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw== dependencies: get-stdin "^6.0.0" eslint-import-resolver-node@0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== dependencies: debug "^2.6.9" resolve "^1.13.1" eslint-plugin-prettier@^3.1.4: version "3.4.0" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7" integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw== dependencies: prettier-linter-helpers "^1.0.0" eslint-plugin-react@^7.21.5: version "7.24.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz#eadedfa351a6f36b490aa17f4fa9b14e842b9eb4" integrity sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q== dependencies: array-includes "^3.1.3" array.prototype.flatmap "^1.2.4" doctrine "^2.1.0" has "^1.0.3" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.0.4" object.entries "^1.1.4" object.fromentries "^2.0.4" object.values "^1.1.4" prop-types "^15.7.2" resolve "^2.0.0-next.3" string.prototype.matchall "^4.0.5" eslint-scope@5.1.1, eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" estraverse "^4.1.1" eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint@^7.14.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== dependencies: "@babel/code-frame" "7.12.11" "@eslint/eslintrc" "^0.4.3" "@humanwhocodes/config-array" "^0.5.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" escape-string-regexp "^4.0.0" eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" espree "^7.3.1" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.1.2" globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" progress "^2.0.0" regexpp "^3.1.0" semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" table "^6.0.9" text-table "^0.2.0" v8-compile-cache "^2.0.3" espree@^7.3.0, espree@^7.3.1: version "7.3.1" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= event-emitter@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= dependencies: d "1" es5-ext "~0.10.14" events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" get-stream "^6.0.0" human-signals "^2.1.0" is-stream "^2.0.0" merge-stream "^2.0.0" npm-run-path "^4.0.1" onetime "^5.1.2" signal-exit "^3.0.3" strip-final-newline "^2.0.0" express@4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== dependencies: accepts "~1.3.7" array-flatten "1.1.1" body-parser "1.19.0" content-disposition "0.5.3" content-type "~1.0.4" cookie "0.4.0" cookie-signature "1.0.6" debug "2.6.9" depd "~1.1.2" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" finalhandler "~1.1.2" fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" parseurl "~1.3.3" path-to-regexp "0.1.7" proxy-addr "~2.0.5" qs "6.7.0" range-parser "~1.2.1" safe-buffer "5.1.2" send "0.17.1" serve-static "1.14.1" setprototypeof "1.1.1" statuses "~1.5.0" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" ext@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== dependencies: type "^2.0.0" extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" iconv-lite "^0.4.24" tmp "^0.0.33" extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-diff@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== fast-glob@^3.0.3: version "3.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" glob-parent "^5.1.2" merge2 "^1.3.0" micromatch "^4.0.4" fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fast-redact@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.0.1.tgz#d6015b971e933d03529b01333ba7f22c29961e92" integrity sha512-kYpn4Y/valC9MdrISg47tZOpYBNoTXKgT9GYXFpHN/jYFs+lFkPoisY+LcBODdKVMY96ATzvzsWv+ES/4Kmufw== fast-safe-stringify@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz#dc2af48c46cf712b683e849b2bbd446b32de936f" integrity sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag== fastest-levenshtein@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== fastq@^1.6.0: version "1.11.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== dependencies: reusify "^1.0.4" figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" file-loader@~6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.0.0.tgz#97bbfaab7a2460c07bcbd72d3a6922407f67649f" integrity sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ== dependencies: loader-utils "^2.0.0" schema-utils "^2.6.5" fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" on-finished "~2.3.0" parseurl "~1.3.3" statuses "~1.5.0" unpipe "~1.0.0" find-cache-dir@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== dependencies: commondir "^1.0.1" make-dir "^3.0.2" pkg-dir "^4.1.0" find-root@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" find-up@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" path-exists "^4.0.0" flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" rimraf "^3.0.2" flat@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== dependencies: is-buffer "~2.0.3" flatstr@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== flatted@^3.1.0: version "3.2.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" combined-stream "^1.0.6" mime-types "^2.1.12" forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== free-style@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/free-style/-/free-style-3.1.0.tgz#4e2996029534e6b1731611d843437b9e2f473f08" integrity sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA== fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= fs-extra@^9.0.1: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== dependencies: at-least-node "^1.0.0" graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" has "^1.0.3" has-symbols "^1.0.1" get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" git-hooks-list@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-1.0.3.tgz#be5baaf78203ce342f2f844a9d2b03dba1b45156" integrity sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ== glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== glob@7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" glob@^6.0.1: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= dependencies: inflight "^1.0.4" inherits "2" minimatch "2 || 3" once "^1.3.0" path-is-absolute "^1.0.0" glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" globals@^13.6.0, globals@^13.9.0: version "13.10.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.10.0.tgz#60ba56c3ac2ca845cfbf4faeca727ad9dd204676" integrity sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g== dependencies: type-fest "^0.20.2" globby@10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.0.tgz#abfcd0630037ae174a88590132c2f6804e291072" integrity sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw== dependencies: "@types/glob" "^7.1.1" array-union "^2.1.0" dir-glob "^3.0.1" fast-glob "^3.0.3" glob "^7.1.3" ignore "^5.1.1" merge2 "^1.2.3" slash "^3.0.0" got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== dependencies: "@sindresorhus/is" "^0.14.0" "@szmarczak/http-timer" "^1.1.2" cacheable-request "^6.0.0" decompress-response "^3.3.0" duplexer3 "^0.1.4" get-stream "^4.1.0" lowercase-keys "^1.0.1" mimic-response "^1.0.1" p-cancelable "^1.0.0" to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== growl@1.10.5: version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== gud@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== handlebars@4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== dependencies: minimist "^1.2.5" neo-async "^2.6.0" source-map "^0.6.1" wordwrap "^1.0.0" optionalDependencies: uglify-js "^3.1.4" har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.0, har-validator@~5.1.3: version "5.1.5" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== dependencies: ajv "^6.12.3" har-schema "^2.0.0" has-bigints@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== dependencies: has-symbols "^1.0.2" has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== dependencies: whatwg-encoding "^1.0.1" htmlparser2@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== dependencies: domelementtype "^2.0.1" domhandler "^4.0.0" domutils "^2.5.2" entities "^2.0.0" http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== dependencies: depd "~1.1.2" inherits "2.0.3" setprototypeof "1.1.1" statuses ">= 1.5.0 < 2" toidentifier "1.0.0" http-errors@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== dependencies: depd "~1.1.2" inherits "2.0.4" setprototypeof "1.2.0" statuses ">= 1.5.0 < 2" toidentifier "1.0.0" http-errors@~1.7.2: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== dependencies: depd "~1.1.2" inherits "2.0.4" setprototypeof "1.1.1" statuses ">= 1.5.0 < 2" toidentifier "1.0.0" http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" sshpk "^1.7.0" http-status-codes@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477" integrity sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ== human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.1: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== immediate@^3.2.3: version "3.3.0" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" import-local@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== inquirer@^7.0.0: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.0" cli-cursor "^3.1.0" cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" lodash "^4.17.19" mute-stream "0.0.8" run-async "^2.4.0" rxjs "^6.6.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== dependencies: get-intrinsic "^1.1.0" has "^1.0.3" side-channel "^1.0.4" interpret@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-arguments@^1.0.4: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-bigint@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.3.tgz#fc9d9e364210480675653ddaea0518528d49a581" integrity sha512-ZU538ajmYJmzysE5yU4Y7uIrPQ2j704u+hXFiIPQExpqzzUbpe5jCPdTfmz7jXRxZdvjY3KZ3ZNenoXQovX+Dg== is-boolean-object@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" is-buffer@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== is-core-module@^2.2.0: version "2.5.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491" integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== dependencies: has "^1.0.3" is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-number-object@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== dependencies: has-tostringtag "^1.0.0" is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-plain-obj@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-promise@^2.1.0, is-promise@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== is-regex@^1.0.4, is-regex@^1.1.3: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.0.5, is-string@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isomorphic.js@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/isomorphic.js/-/isomorphic.js-0.2.4.tgz#24ca374163ae54a7ce3b86ce63b701b91aa84969" integrity sha512-Y4NjZceAwaPXctwsHgNsmfuPxR8lJ3f8X7QTAkhltrX4oGIv+eTlgHLXn4tWysC9zGTi929gapnPp+8F8cg7nA== isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= jest-worker@^26.5.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^7.0.0" jest-worker@^27.0.2: version "27.0.6" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed" integrity sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^8.0.0" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" esprima "^4.0.0" js-yaml@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsdom@15.2.1: version "15.2.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== dependencies: abab "^2.0.0" acorn "^7.1.0" acorn-globals "^4.3.2" array-equal "^1.0.0" cssom "^0.4.1" cssstyle "^2.0.0" data-urls "^1.1.0" domexception "^1.0.1" escodegen "^1.11.1" html-encoding-sniffer "^1.0.2" nwsapi "^2.2.0" parse5 "5.1.0" pn "^1.1.0" request "^2.88.0" request-promise-native "^1.0.7" saxes "^3.1.9" symbol-tree "^3.2.2" tough-cookie "^3.0.1" w3c-hr-time "^1.0.1" w3c-xmlserializer "^1.1.2" webidl-conversions "^4.0.2" whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" whatwg-url "^7.0.0" ws "^7.0.0" xml-name-validator "^3.0.0" json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== dependencies: minimist "^1.2.0" json5@^2.1.1, json5@^2.1.2: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" optionalDependencies: graceful-fs "^4.1.6" jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= jsonwebtoken@8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== dependencies: jws "^3.2.2" lodash.includes "^4.3.0" lodash.isboolean "^3.0.3" lodash.isinteger "^4.0.4" lodash.isnumber "^3.0.3" lodash.isplainobject "^4.0.6" lodash.isstring "^4.0.1" lodash.once "^4.0.0" ms "^2.1.1" semver "^5.6.0" jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: assert-plus "1.0.0" extsprintf "1.3.0" json-schema "0.2.3" verror "1.10.0" "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.2.0" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82" integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q== dependencies: array-includes "^3.1.2" object.assign "^4.1.2" jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== dependencies: buffer-equal-constant-time "1.0.1" ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== dependencies: jwa "^1.4.1" safe-buffer "^5.0.1" keygrip@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== dependencies: tsscmp "1.0.6" keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== dependencies: json-buffer "3.0.0" kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== kleur@4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== klona@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== level-codec@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== dependencies: buffer "^5.6.0" level-concat-iterator@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== level-errors@^2.0.0, level-errors@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== dependencies: errno "~0.1.1" level-iterator-stream@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== dependencies: inherits "^2.0.4" readable-stream "^3.4.0" xtend "^4.0.2" level-js@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/level-js/-/level-js-5.0.2.tgz#5e280b8f93abd9ef3a305b13faf0b5397c969b55" integrity sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg== dependencies: abstract-leveldown "~6.2.3" buffer "^5.5.0" inherits "^2.0.3" ltgt "^2.1.2" level-packager@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== dependencies: encoding-down "^6.3.0" levelup "^4.3.2" level-supports@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== dependencies: xtend "^4.0.2" level@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/level/-/level-6.0.1.tgz#dc34c5edb81846a6de5079eac15706334b0d7cd6" integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw== dependencies: level-js "^5.0.0" level-packager "^5.1.0" leveldown "^5.4.0" leveldown@^5.4.0: version "5.6.0" resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== dependencies: abstract-leveldown "~6.2.1" napi-macros "~2.0.0" node-gyp-build "~4.1.0" levelup@^4.3.2: version "4.4.0" resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== dependencies: deferred-leveldown "~5.3.0" level-errors "~2.0.0" level-iterator-stream "~4.0.0" level-supports "~1.0.0" xtend "~4.0.0" levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" type-check "~0.4.0" levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" lib0@^0.2.31, lib0@^0.2.41, lib0@^0.2.42: version "0.2.42" resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.42.tgz#6d8bf1fb8205dec37a953c521c5ee403fd8769b0" integrity sha512-8BNM4MiokEKzMvSxTOC3gnCBisJH+jL67CnSnqzHv3jli3pUvGC8wz+0DQ2YvGr4wVQdb2R2uNNPw9LEpVvJ4Q== dependencies: isomorphic.js "^0.2.4" license-webpack-plugin@^2.3.14: version "2.3.20" resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz#f51fb674ca31519dbedbe1c7aabc036e5a7f2858" integrity sha512-AHVueg9clOKACSHkhmEI+PCC9x8+qsQVuKECZD3ETxETK5h/PCv5/MUzyG1gm8OMcip/s1tcNxqo9Qb7WhjGsg== dependencies: "@types/webpack-sources" "^0.1.5" webpack-sources "^1.2.0" load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= dependencies: graceful-fs "^4.1.2" parse-json "^4.0.0" pify "^3.0.0" strip-bom "^3.0.0" loader-runner@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== loader-utils@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" json5 "^1.0.1" loader-utils@^2.0.0, loader-utils@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" json5 "^2.1.2" locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" path-exists "^3.0.0" locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" lockfile@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA== dependencies: signal-exit "^3.0.2" lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= lodash.isnumber@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= lodash@4, lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== dependencies: chalk "^2.0.1" loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" lowdb@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064" integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ== dependencies: graceful-fs "^4.1.3" is-promise "^2.1.0" lodash "4" pify "^3.0.0" steno "^0.4.1" lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lowercase-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== lru-cache@6.0.0, lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" lru-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= dependencies: es5-ext "~0.10.2" ltgt@^2.1.2: version "2.2.1" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= lunr-mutable-indexes@2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/lunr-mutable-indexes/-/lunr-mutable-indexes-2.3.2.tgz#864253489735d598c5140f3fb75c0a5c8be2e98c" integrity sha512-Han6cdWAPPFM7C2AigS2Ofl3XjAT0yVMrUixodJEpyg71zCtZ2yzXc3s+suc/OaNt4ca6WJBEzVnEIjxCTwFMw== dependencies: lunr ">= 2.3.0 < 2.4.0" "lunr@>= 2.3.0 < 2.4.0": version "2.3.9" resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" marked@2.1.3, marked@^2.0.0, marked@^2.0.1: version "2.1.3" resolved "https://registry.yarnpkg.com/marked/-/marked-2.1.3.tgz#bd017cef6431724fd4b27e0657f5ceb14bff3753" integrity sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA== media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= memoizee@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== dependencies: d "^1.0.1" es5-ext "^0.10.53" es6-weak-map "^2.0.3" event-emitter "^0.3.5" is-promise "^2.2.2" lru-queue "^0.1.0" next-tick "^1.1.0" timers-ext "^0.1.7" memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.2.3, merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" picomatch "^2.2.3" mime-db@1.49.0, "mime-db@>= 1.43.0 < 2": version "1.49.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.32" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== dependencies: mime-db "1.49.0" mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== mini-css-extract-plugin@~1.3.2: version "1.3.9" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.9.tgz#47a32132b0fd97a119acd530e8421e8f6ab16d5e" integrity sha512-Ac4s+xhVbqlyhXS5J/Vh/QXUz3ycXlCqoCPpg0vdfhsIBH9eg/It/9L1r1XhSCH737M1lqcWnMuWL13zcygn5A== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" webpack-sources "^1.1.0" "minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== dependencies: minipass "^3.0.0" minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== dependencies: minipass "^3.0.0" minipass-pipeline@^1.2.2: version "1.2.4" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" minipass@^3.0.0, minipass@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== dependencies: yallist "^4.0.0" minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" yallist "^4.0.0" mkdirp@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== dependencies: minimist "^1.2.5" mkdirp@1.0.4, mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" mocha@^6.2.0: version "6.2.3" resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.3.tgz#e648432181d8b99393410212664450a4c1e31912" integrity sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg== dependencies: ansi-colors "3.2.3" browser-stdout "1.3.1" debug "3.2.6" diff "3.5.0" escape-string-regexp "1.0.5" find-up "3.0.0" glob "7.1.3" growl "1.10.5" he "1.2.0" js-yaml "3.13.1" log-symbols "2.2.0" minimatch "3.0.4" mkdirp "0.5.4" ms "2.1.1" node-environment-flags "1.0.5" object.assign "4.1.0" strip-json-comments "2.0.1" supports-color "6.0.0" which "1.3.1" wide-align "1.1.3" yargs "13.3.2" yargs-parser "13.1.2" yargs-unparser "1.6.0" moment@^2.24.0: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== mv@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= dependencies: mkdirp "~0.5.1" ncp "~2.0.0" rimraf "~2.4.0" nanoid@^3.1.23: version "3.1.23" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== napi-macros@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= ncp@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== neo-async@^2.6.0, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== next-tick@1, next-tick@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-environment-flags@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== dependencies: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" node-fetch@^2.6.0: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== node-gyp-build@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ== node-releases@^1.1.73: version "1.1.74" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.74.tgz#e5866488080ebaa70a93b91144ccde06f3c3463e" integrity sha512-caJBVempXZPepZoZAPCWRTNxYQ+xtG/KAi4ozTA5A+nJ7IU+kLQCbqaUjb5Rwy14M9upBWiQ4NutcmW04LJSRw== normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" resolve "^1.10.0" semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" normalize-url@^4.1.0: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== normalize.css@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== npm-run-all@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== dependencies: ansi-styles "^3.2.1" chalk "^2.4.1" cross-spawn "^6.0.5" memorystream "^0.3.1" minimatch "^3.0.4" pidtree "^0.3.0" read-pkg "^3.0.0" shell-quote "^1.6.1" string.prototype.padend "^3.0.0" npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-assign@^4, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.11.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== object-is@^1.0.1: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== dependencies: define-properties "^1.1.2" function-bind "^1.1.1" has-symbols "^1.0.0" object-keys "^1.0.11" object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: call-bind "^1.0.0" define-properties "^1.1.3" has-symbols "^1.0.1" object-keys "^1.1.1" object.entries@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.4.tgz#43ccf9a50bc5fd5b649d45ab1a579f24e088cafd" integrity sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.18.2" object.fromentries@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.4.tgz#26e1ba5c4571c5c6f0890cef4473066456a120b8" integrity sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.18.0-next.2" has "^1.0.3" object.getownpropertydescriptors@^2.0.3: version "2.1.2" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.18.0-next.2" object.values@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.18.2" on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" on-headers@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" fast-levenshtein "~2.0.6" levn "~0.3.0" prelude-ls "~1.1.2" type-check "~0.3.2" word-wrap "~1.2.3" optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" word-wrap "^1.2.3" os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= os@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/os/-/os-0.1.2.tgz#f29a50c62908516ba42652de42f7038600cadbc2" integrity sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ== p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== dependencies: aggregate-error "^3.0.0" p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== package-json@^6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== dependencies: got "^9.6.0" registry-auth-token "^4.0.0" registry-url "^5.0.0" semver "^6.2.0" parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" parse-ms@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== parse-srcset@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE= parse5@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-browserify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== pidtree@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pino-std-serializers@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671" integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg== pino@6.12.0: version "6.12.0" resolved "https://registry.yarnpkg.com/pino/-/pino-6.12.0.tgz#2281521620d70eeff519039467352d656f46735e" integrity sha512-5NGopOcUusGuklGHVVv9az0Hv/Dj3urHhD3G+zhl5pBGIRYAeGCi/Ej6YCl16Q2ko28cmYiJz+/qRoJiwy62Rw== dependencies: fast-redact "^3.0.0" fast-safe-stringify "^2.0.8" flatstr "^1.0.12" pino-std-serializers "^3.1.0" quick-format-unescaped "^4.0.3" sonic-boom "^1.0.2" pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" pkginfo@0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" integrity sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8= pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== popper.js@^1.14.4, popper.js@^1.16.1: version "1.16.1" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== postcss-modules-extract-imports@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== postcss-modules-local-by-default@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== dependencies: icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" postcss-modules-scope@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== dependencies: postcss-selector-parser "^6.0.4" postcss-modules-values@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== dependencies: icss-utils "^5.0.0" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.6" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== postcss@^8.0.2, postcss@^8.2.15: version "8.3.6" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea" integrity sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A== dependencies: colorette "^1.2.2" nanoid "^3.1.23" source-map-js "^0.6.2" prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= prettier-bytes@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/prettier-bytes/-/prettier-bytes-1.0.4.tgz#994b02aa46f699c50b6257b5faaa7fe2557e62d6" integrity sha1-mUsCqkb2mcULYle1+qp/4lV+YtY= prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" prettier@^2.1.1: version "2.3.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== prettier@~2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== pretty-ms@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== dependencies: parse-ms "^2.1.0" process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== dependencies: loose-envify "^1.4.0" object-assign "^4.1.1" react-is "^16.8.1" proxy-addr@~2.0.5: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" ipaddr.js "1.9.1" prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= psl@^1.1.24, psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" once "^1.3.1" punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== quick-format-unescaped@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.3.tgz#6d6b66b8207aa2b35eef12be1421bb24c428f652" integrity sha512-MaL/oqh02mhEo5m5J2rwsVL23Iw2PEaGVHgT2vFt8AAsr0lfvQA5dpXo9TPu0rz7tSBdUPgkbam0j/fj5ZM8yg== randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== dependencies: bytes "3.1.0" http-errors "1.7.2" iconv-lite "0.4.24" unpipe "1.0.0" raw-loader@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" ini "~1.3.0" minimist "^1.2.0" strip-json-comments "~2.0.1" react-dom@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" scheduler "^0.20.2" react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== react-popper@^1.3.7: version "1.3.11" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.11.tgz#a2cc3f0a67b75b66cfa62d2c409f9dd1fcc71ffd" integrity sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg== dependencies: "@babel/runtime" "^7.1.2" "@hypnosphi/create-react-context" "^0.3.1" deep-equal "^1.1.1" popper.js "^1.14.4" prop-types "^15.6.1" typed-styles "^0.0.7" warning "^4.0.2" react-transition-group@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== dependencies: dom-helpers "^3.4.0" loose-envify "^1.4.0" prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" react@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= dependencies: load-json-file "^4.0.0" normalize-package-data "^2.3.2" path-type "^3.0.0" readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" util-deprecate "^1.0.1" rechoir@^0.7.0: version "0.7.1" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== dependencies: resolve "^1.9.0" regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" regexpp@^3.0.0, regexpp@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== registry-auth-token@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== dependencies: rc "^1.2.8" registry-url@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== dependencies: rc "^1.2.8" request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== dependencies: lodash "^4.17.19" request-promise-native@^1.0.7: version "1.0.9" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== dependencies: request-promise-core "1.1.4" stealthy-require "^1.1.1" tough-cookie "^2.3.3" request@2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" caseless "~0.12.0" combined-stream "~1.0.6" extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" har-validator "~5.1.0" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" json-stringify-safe "~5.0.1" mime-types "~2.1.19" oauth-sign "~0.9.0" performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" tough-cookie "~2.4.3" tunnel-agent "^0.6.0" uuid "^3.3.2" request@2.88.2, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" caseless "~0.12.0" combined-stream "~1.0.6" extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" har-validator "~5.1.3" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" json-stringify-safe "~5.0.1" mime-types "~2.1.19" oauth-sign "~0.9.0" performance-now "^2.1.0" qs "~6.5.2" safe-buffer "^5.1.2" tough-cookie "~2.5.0" tunnel-agent "^0.6.0" uuid "^3.3.2" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve@^1.10.0, resolve@^1.13.1, resolve@^1.9.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: is-core-module "^2.2.0" path-parse "^1.0.6" resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== dependencies: is-core-module "^2.2.0" path-parse "^1.0.6" responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= dependencies: lowercase-keys "^1.0.0" restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" signal-exit "^3.0.2" reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" rimraf@~2.4.0: version "2.4.5" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= dependencies: glob "^6.0.1" run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" safe-buffer@5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sanitize-html@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.3.3.tgz#3db382c9a621cce4c46d90f10c64f1e9da9e8353" integrity sha512-DCFXPt7Di0c6JUnlT90eIgrjs6TsJl/8HYU3KLdmrVclFN4O0heTcVbJiMa23OKVr6aR051XYtsgd8EWwEBwUA== dependencies: deepmerge "^4.2.2" escape-string-regexp "^4.0.0" htmlparser2 "^6.0.0" is-plain-object "^5.0.0" klona "^2.0.3" parse-srcset "^1.0.2" postcss "^8.0.2" saxes@^3.1.9: version "3.1.11" resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== dependencies: xmlchars "^2.1.1" scheduler@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== dependencies: "@types/json-schema" "^7.0.5" ajv "^6.12.4" ajv-keywords "^3.5.2" schema-utils@^3.0.0, schema-utils@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== dependencies: "@types/json-schema" "^7.0.8" ajv "^6.12.5" ajv-keywords "^3.5.2" "semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.3.5, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" semver@^6.0.0, semver@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: debug "2.6.9" depd "~1.1.2" destroy "~1.0.4" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" http-errors "~1.7.2" mime "1.6.0" ms "2.1.1" on-finished "~2.3.0" range-parser "~1.2.1" statuses "~1.5.0" serialize-javascript@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== dependencies: randombytes "^2.1.0" serialize-javascript@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" send "0.17.1" set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== dependencies: kind-of "^6.0.2" shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.6.1: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" get-intrinsic "^1.0.2" object-inspect "^1.9.0" signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" sonic-boom@^1.0.2: version "1.4.1" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg== dependencies: atomic-sleep "^1.0.0" flatstr "^1.0.12" sort-object-keys@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== sort-package-json@~1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-1.44.0.tgz#470330be868f8a524a4607b26f2a0233e93d8b6d" integrity sha512-u9GUZvpavUCXV5SbEqXu9FRbsJrYU6WM10r3zA0gymGPufK5X82MblCLh9GW9l46pXKEZvK+FA3eVTqC4oMp4A== dependencies: detect-indent "^6.0.0" detect-newline "3.1.0" git-hooks-list "1.0.3" globby "10.0.0" is-plain-obj "2.1.0" sort-object-keys "^1.1.3" source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== source-map-js@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: version "3.0.10" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" bcrypt-pbkdf "^1.0.0" dashdash "^1.12.0" ecc-jsbn "~0.1.1" getpass "^0.1.1" jsbn "~0.1.0" safer-buffer "^2.0.2" tweetnacl "~0.14.0" ssri@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== dependencies: minipass "^3.1.1" "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= steno@^0.4.1: version "0.4.4" resolved "https://registry.yarnpkg.com/steno/-/steno-0.4.4.tgz#071105bdfc286e6615c0403c27e9d7b5dcb855cb" integrity sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs= dependencies: graceful-fs "^4.1.3" "string-width@^1.0.2 || 2": version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== dependencies: emoji-regex "^7.0.1" is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" string-width@^4.1.0, string-width@^4.2.0: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" string.prototype.matchall@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da" integrity sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.18.2" get-intrinsic "^1.1.1" has-symbols "^1.0.2" internal-slot "^1.0.3" regexp.prototype.flags "^1.3.1" side-channel "^1.0.4" string.prototype.padend@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311" integrity sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.18.0-next.2" string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= dependencies: ansi-regex "^3.0.0" strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== dependencies: ansi-regex "^4.1.0" strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== dependencies: ansi-regex "^5.0.0" strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-json-comments@2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== style-loader@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" supports-color@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== dependencies: has-flag "^3.0.0" supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.0.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" svg-url-loader@~6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/svg-url-loader/-/svg-url-loader-6.0.0.tgz#b94861d9f6badfb8ca3e7d3ec4655c1bf732ac5d" integrity sha512-Qr5SCKxyxKcRnvnVrO3iQj9EX/v40UiGEMshgegzV7vpo3yc+HexELOdtWcA3MKjL8IyZZ1zOdcILmDEa/8JJQ== dependencies: file-loader "~6.0.0" loader-utils "~2.0.0" symbol-tree@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.9: version "6.7.1" resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== dependencies: ajv "^8.0.1" lodash.clonedeep "^4.5.0" lodash.truncate "^4.4.2" slice-ansi "^4.0.0" string-width "^4.2.0" strip-ansi "^6.0.0" tapable@^2.1.1, tapable@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== tar@^6.0.2: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" minipass "^3.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" terser-webpack-plugin@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== dependencies: cacache "^15.0.5" find-cache-dir "^3.3.1" jest-worker "^26.5.0" p-limit "^3.0.2" schema-utils "^3.0.0" serialize-javascript "^5.0.1" source-map "^0.6.1" terser "^5.3.4" webpack-sources "^1.4.3" terser-webpack-plugin@^5.1.3: version "5.1.4" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz#c369cf8a47aa9922bd0d8a94fe3d3da11a7678a1" integrity sha512-C2WkFwstHDhVEmsmlCxrXUtVklS+Ir1A7twrYzrDrQQOIMOaVAYykaoo/Aq1K0QRkMoY2hhvDQY1cm4jnIMFwA== dependencies: jest-worker "^27.0.2" p-limit "^3.1.0" schema-utils "^3.0.0" serialize-javascript "^6.0.0" source-map "^0.6.1" terser "^5.7.0" terser@^5.3.4, terser@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg== dependencies: commander "^2.20.0" source-map "~0.7.2" source-map-support "~0.5.19" text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= "through@>=2.2.7 <3", through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= timers-ext@^0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== dependencies: es5-ext "~0.10.46" next-tick "1" tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" to-readable-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" to-string-loader@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/to-string-loader/-/to-string-loader-1.1.6.tgz#230529ccc63dd0ecca052a85e1fb82afe946b0ab" integrity sha512-VNg62//PS1WfNwrK3n7t6wtK5Vdtx/qeYLLEioW46VMlYUwAYT6wnfB+OwS2FMTCalIHu0tk79D3RXX8ttmZTQ== dependencies: loader-utils "^1.0.0" toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: psl "^1.1.28" punycode "^2.1.1" tough-cookie@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== dependencies: ip-regex "^2.1.0" psl "^1.1.28" punycode "^2.1.1" tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== dependencies: psl "^1.1.24" punycode "^1.4.1" tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= dependencies: punycode "^2.1.0" tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@~1.13.0: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== tsscmp@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== tsutils@^3.17.1: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= typanion@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/typanion/-/typanion-3.3.2.tgz#c31f3b2afb6e8ae74dbd3f96d5b1d8f9745e483e" integrity sha512-m3v3wtFc6R0wtl0RpEn11bKXIOjS1zch5gmx0zg2G5qfGQ3A9TVZRMSL43O5eFuGXsrgzyvDcGRmSXGP5UqpDQ== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" mime-types "~2.1.24" type@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== type@^2.0.0: version "2.5.0" resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== typed-styles@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== typescript@~4.1.3: version "4.1.6" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.6.tgz#1becd85d77567c3c741172339e93ce2e69932138" integrity sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow== typestyle@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/typestyle/-/typestyle-2.1.0.tgz#7c5cc567de72cd8bfb686813150b92791aaa7636" integrity sha512-6uCYPdG4xWLeEcl9O0GtNFnNGhami+irKiLsXSuvWHC/aTS7wdj49WeikWAKN+xHN3b1hm+9v0svwwgSBhCsNA== dependencies: csstype "2.6.9" free-style "3.1.0" uglify-js@^3.1.4: version "3.14.1" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.1.tgz#e2cb9fe34db9cb4cf7e35d1d26dfea28e09a7d06" integrity sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g== unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== dependencies: function-bind "^1.1.1" has-bigints "^1.0.1" has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== dependencies: unique-slug "^2.0.0" unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== dependencies: imurmurhash "^0.1.4" universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== unix-crypt-td-js@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz#4912dfad1c8aeb7d20fa0a39e4c31918c1d5d5dd" integrity sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" url-loader@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== dependencies: loader-utils "^2.0.0" mime-types "^2.1.27" schema-utils "^3.0.0" url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= dependencies: prepend-http "^2.0.0" url-parse@~1.5.1: version "1.5.3" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= dependencies: punycode "1.3.2" querystring "0.2.0" util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" validator@13.6.0: version "13.6.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.6.0.tgz#1e71899c14cdc7b2068463cb24c1cc16f6ec7059" integrity sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg== vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= verdaccio-audit@10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/verdaccio-audit/-/verdaccio-audit-10.0.0.tgz#d3304d923c7f2c28c173a02425208c941f25217b" integrity sha512-Epsh+C7ZEdq39PR9QeDBTWktbeqc0zOQjMzWte6Ut5Jh6fPLZzxGF8VK8O67B6mnTwLvGy50A1aPVM97Ysh5Rw== dependencies: express "4.17.1" request "2.88.2" verdaccio-htpasswd@10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/verdaccio-htpasswd/-/verdaccio-htpasswd-10.0.0.tgz#7a7f44e8ed4db40c53deef0f5101f2a16dce4ff1" integrity sha512-3TKwiLwl8/fbaTDawHvjSYcsyMmdARg58keP/1plv74x+Jw0sC66HbbRwQ/tPO5mqoG0UwoWW+lkO8h/OiWi9w== dependencies: "@verdaccio/file-locking" "^10.0.0" apache-md5 "1.1.2" bcryptjs "2.4.3" http-errors "1.8.0" unix-crypt-td-js "1.1.4" verdaccio@^5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/verdaccio/-/verdaccio-5.1.2.tgz#d8bc0792302cd08af16184828ad71f51e2a8b7bd" integrity sha512-Cyi1frVJEzl75q34CrYHOo0bT7KwUSoV/b17J8NnJo47CbF5SUF/Y+qX46ZFhf4eQzipVYBkgMfI6khjvjpWSg== dependencies: "@verdaccio/commons-api" "10.0.0" "@verdaccio/local-storage" "10.0.6" "@verdaccio/readme" "10.0.0" "@verdaccio/streams" "10.0.0" "@verdaccio/ui-theme" "3.1.0" JSONStream "1.3.5" async "3.2.0" body-parser "1.19.0" clipanion "3.0.0" compression "1.7.4" cookies "0.8.0" cors "2.8.5" dayjs "1.10.6" debug "^4.3.2" envinfo "7.8.1" eslint-import-resolver-node "0.3.4" express "4.17.1" fast-safe-stringify "^2.0.8" handlebars "4.7.7" http-errors "1.8.0" js-yaml "4.1.0" jsonwebtoken "8.5.1" kleur "4.1.4" lodash "4.17.21" lru-cache "6.0.0" lunr-mutable-indexes "2.3.2" marked "2.1.3" memoizee "0.4.15" mime "2.5.2" minimatch "3.0.4" mkdirp "1.0.4" mv "2.1.1" pino "6.12.0" pkginfo "0.4.1" prettier-bytes "^1.0.4" pretty-ms "^7.0.1" request "2.88.0" semver "7.3.5" validator "13.6.0" verdaccio-audit "10.0.0" verdaccio-htpasswd "10.0.0" verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" extsprintf "^1.2.0" w3c-hr-time@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== dependencies: browser-process-hrtime "^1.0.0" w3c-xmlserializer@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== dependencies: domexception "^1.0.1" webidl-conversions "^4.0.2" xml-name-validator "^3.0.0" warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== dependencies: loose-envify "^1.0.0" watchpack@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== webpack-cli@^4.1.0: version "4.7.2" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.7.2.tgz#a718db600de6d3906a4357e059ae584a89f4c1a5" integrity sha512-mEoLmnmOIZQNiRl0ebnjzQ74Hk0iKS5SiEEnpq3dRezoyR3yPaeQZCMCe+db4524pj1Pd5ghZXjT41KLzIhSLw== dependencies: "@discoveryjs/json-ext" "^0.5.0" "@webpack-cli/configtest" "^1.0.4" "@webpack-cli/info" "^1.3.0" "@webpack-cli/serve" "^1.5.1" colorette "^1.2.1" commander "^7.0.0" execa "^5.0.0" fastest-levenshtein "^1.0.12" import-local "^3.0.2" interpret "^2.2.0" rechoir "^0.7.0" v8-compile-cache "^2.2.0" webpack-merge "^5.7.3" webpack-merge@^5.1.2, webpack-merge@^5.7.3: version "5.8.0" resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== dependencies: clone-deep "^4.0.1" wildcard "^2.0.0" webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== dependencies: source-list-map "^2.0.0" source-map "~0.6.1" webpack-sources@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.0.tgz#b16973bcf844ebcdb3afde32eda1c04d0b90f89d" integrity sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw== webpack@^5.41.1: version "5.50.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.50.0.tgz#5562d75902a749eb4d75131f5627eac3a3192527" integrity sha512-hqxI7t/KVygs0WRv/kTgUW8Kl3YC81uyWQSo/7WUs5LsuRw0htH/fCwbVBGCuiX/t4s7qzjXFcf41O8Reiypag== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.50" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" acorn "^8.4.1" acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" enhanced-resolve "^5.8.0" es-module-lexer "^0.7.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" graceful-fs "^4.2.4" json-parse-better-errors "^1.0.2" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" schema-utils "^3.1.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.3" watchpack "^2.2.0" webpack-sources "^3.2.0" whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== dependencies: iconv-lite "0.4.24" whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1" webidl-conversions "^4.0.2" which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" is-boolean-object "^1.1.0" is-number-object "^1.0.4" is-string "^1.0.5" is-symbol "^1.0.3" which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which@1.3.1, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" wide-align@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= worker-loader@^3.0.2: version "3.0.8" resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.8.tgz#5fc5cda4a3d3163d9c274a4e3a811ce8b60dbb37" integrity sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== dependencies: ansi-styles "^3.2.0" string-width "^3.0.0" strip-ansi "^5.0.0" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= ws@^6.2.1: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" ws@^7.0.0, ws@^7.4.6: version "7.5.3" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== xmlchars@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== xtend@^4.0.2, xtend@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y-codemirror@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/y-codemirror/-/y-codemirror-2.1.1.tgz#e841fc3001b719d7fa457dd7a9748205e2874fe9" integrity sha512-QXHaOkvEJs3pB82dkW1aGfWUd4S1RA1ORtXWtprHClbqBiCOY19VKiojScSTyl8rTaOZ/zblEq+SNH2sd3Umiw== dependencies: lib0 "^0.2.41" y-leveldb@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/y-leveldb/-/y-leveldb-0.1.0.tgz#8b60c1af020252445875ebc70d52666017bcb038" integrity sha512-sMuitVrsAUNh+0b66I42nAuW3lCmez171uP4k0ePcTAJ+c+Iw9w4Yq3wwiyrDMFXBEyQSjSF86Inc23wEvWnxw== dependencies: level "^6.0.1" lib0 "^0.2.31" y-protocols@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/y-protocols/-/y-protocols-1.0.5.tgz#91d574250060b29fcac8f8eb5e276fbad594245e" integrity sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A== dependencies: lib0 "^0.2.42" y-websocket@^1.3.15: version "1.3.16" resolved "https://registry.yarnpkg.com/y-websocket/-/y-websocket-1.3.16.tgz#0ec1a141d593933dfbfba2fb9fa9d95dca332c89" integrity sha512-538dwNOQeZCpMfhh67y40goxHQZKubjoXtfhQieUF2bIQfHVV44bGFeAiYiBHgwOSRdwp7qG4MmDwU0M3U3vng== dependencies: lib0 "^0.2.42" lodash.debounce "^4.0.8" y-protocols "^1.0.5" optionalDependencies: ws "^6.2.1" y-leveldb "^0.1.0" y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yargs-parser@13.1.2, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" yargs-unparser@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== dependencies: flat "^4.1.0" lodash "^4.17.15" yargs "^13.3.0" yargs@13.3.2, yargs@^13.3.0: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== dependencies: cliui "^5.0.0" find-up "^3.0.0" get-caller-file "^2.0.1" require-directory "^2.1.1" require-main-filename "^2.0.0" set-blocking "^2.0.0" string-width "^3.0.0" which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^13.1.2" yarn@^1.22.0: version "1.22.11" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.11.tgz#d0104043e7349046e0e2aec977c24be106925ed6" integrity sha512-AWje4bzqO9RUn3sdnM5N8n4ZJ0BqCc/kqFJvpOI5/EVkINXui0yuvU7NDCEF//+WaxHuNay2uOHxA4+tq1P3cg== yjs@^13.5.6: version "13.5.12" resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.5.12.tgz#7a0cf3119fb368c07243825e989a55de164b3f9c" integrity sha512-/buy1kh8Ls+t733Lgov9hiNxCsjHSCymTuZNahj2hsPNoGbvnSdDmCz9Z4F19Yr1eUAAXQLJF3q7fiBcvPC6Qg== dependencies: lib0 "^0.2.41" yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==

    ;c2l5O/O{^pZ JҸڃ;;n(B$@$@$@E"zdON Y[Œ|6ayo[鹔痡?qHyko@ 4`t3zK`6am4~E@0FҒ1ErۓiEVNcċѡ;b+*Hksʇ+;/i)eAY ћ9\'bh{'tX8`<3壷LLPtWrvR#k#܍B!Jcf& NoG)+횔ӬG"$+"eeshbMQ;%16:/28fsQe'&N Q +ըre'->[a Hl?r+qg-4sM'-{N~Z 62OhP./ѻm;jOuڊ]05>Ė+'u2V?@qiXժ gCk+aTT,Y!y 4Y4\HHHH51ϼȧŬԮpŐfCV~Zf,$lwMgҶmG,c9uIqԱ-`PFBI#+fpTV;'.KAd4V^Ϟ1.\\K7ԸGOL#c}H6@xt7D]xWMQ%+ax{˕kMZxھضOZ76>ӭƔz%DY-93AqtF   9`!+9Әs"')-yg.Rt`U]J  Fv=Hgib:1x0`?ܪSA_dw'QU, BdQDD\m}XP BU!բ"RV-Z1Z MDBH@67C$Cf2f2|s=sύlyU'JHSV bʂQCGW},}[S]7Y)|┖HKZ)ͺ7zK;gF'ޛ,5B).UoҮk^HQVM}o)|ewׯԎ+FeFނ\zA鬧oG"9 nL1%eDZy] ~3J]A2,d]v;1a9/Y# +3ê>Ai$J;eHWC 7۷(:ZQYFZQzԽsyɯr'v]1n?)UuZMQOSRR~urM~kn~ŔbMnK,qe?)J^R^_r2퓔#DqGyXp*T)S)ӮRzċM2@|Y̹Ly,qn2iz=jի?ZWm(7KQGr4KoV 3W4z:o Vo_LFD^F}@@@YE9d+kcRM=YrZbqkobǟM])q7*Y)"a+>Q]d/+.\Q7^rn{UĨ_ؖcZQyН2ܪ}i*mRl<>/)[9gҚy?[5ؖ޼tA͗]~h4y5?3ݹ2w~M)Oo7hخ9k>X@@ ,߾lڴ&qd/kQXT_o!hD3 @ɍgPƒK,ֱ׾֭P9\tN+רԭjf^rVjuj>kI%[à(c[ކ?t)zz_hv0_2چ  KpȤq64E;dsf쥣   vXi Y OJ'   v4~n׭[W[[ۿٳgwbv~Voח!  vׯ_+~aΝy#@@@) 3ehڻWIk׮[zwy;wM0_{E!  $] FKJJ233ҥKnnn(1ܛxcj@@@μ3n555r~llw&Ra4  Ъ{Z֯,'l7r]]֮3d~gFFQ^lgm4]cޮ̎Ki3#  @d>| i!a~.3Ȋެdmks$;ʯ%_a59qcG_"yhv_Qh4FWe]~G(vaaaϞ=]&y a.뻭a.@@6#8mw]n7nŊC INN^|,6>`}df?ޖ7fA@@g}uY[ ;6M`o~ž29geV`NvM_l|*wpNNpdvpmu8?iDҸS5u?6pϟ6;J@@{ 8}^LL p&d[blO2iaO bNٳ}(d{A@@ MjڴKOjs4mabT[   !) 1sI!m0u6@@@^!2o.g]lB@@ px衇o>#_ve3<'#֖z>ŕg֭/ 3Y3i{Sq_i<|.پq  @ l|K.7ocWVV_mw/<Ǐ;m~2oل^F&@@DF}%Ɩ\yE?wyG_\{, IDATnٲEF """K/_H2tM'N۷rܹscccP~~}edd83; w)Yn_|˩4+:t |СC%gnݦL"YڵkGyf!玜8{l)m'\z1W5\@ W.Mg" K,ׯwu 7o/WAǏmIvȑGsz}%q}eW^ƍYe;e[$~'IܱcӧOovgug駟JQs$;wާsѣ$u߾}O9g:^ h4V8=Tc&i  + gΜ))2\2\feK t;Cn 2d휿,7nOHH;fK~###߹S?ԱcG V'OOOww%!7 Gm760_`  A@eYҒ---MI&"rKLMM۰]rꮷ wL 7L2-G%<":|pNrJF(wb7t)섕ɩ 5>^u_>:1l3kL  Y`} 뻭Mvd4O"ϧpZ_`֬Y2~UW~Ԉ@ 7\) DVgm3i @?#l@} C4@ L[n E@͛R@c]mu=ao@@qF4T@[%{@@@ 0WN C@ 4,@ڰa egjt T& @gK 5@`'N& qi5kQ#gJIgJz@@@ڠavt @@@L 0iLS/ mVpYwOxΝI FKkF5]쎅;3 J K@m }ۼ @V~핯_{yRiUU3;?9=V~p˛9 OCN{i3x(7(:d[ih^4?[K/7w_XxȿR f a_{j/W9#Fi"<0w⧒:vq9hn+)(.Vxlã3}q+o%}Vb=+l-ۤd>Y 3<ܼv?Q)BmYbݛY>w yF% qRMfսs.zF)ʪ IZs\ƴSӇ]8wJjsn="o ~-O j F!i uŶX[EcdggWWWWUUUTT\_gLt=s䶻n+n6ӷ8 uFC ~@B ߏqA>dU,fc1ơPN"q>mϽg>aTJ}+|[+f!h5mY}GNGm]=qHhL_RHjVy^s<PhӇW\\՞yuug5*j>tP)#**JӍ5^hHWG_C2^s@RF␭nO~:G:msA9W'l @ f5 @ȞFk?uRv11ʳ,)ڈUIkΫHۇfO;u<7TRI߷6V'D#aHYnOkj4Zv1:ۚkb+2ikU{ƃFd{nWT?Dvl޽tP4H)rᅱu-RRFo:̗{xJ>vl]ElVf7U9nR=hrI0;mA@ յ/\Dr9\!CEyy+ᄏq[;]3{n>oc,?ܩ' dCPjj#KjǮ5_x _۪YK5Lѵ>6&aEE*97"Bw7E赲/?#՟:G6%.B^3bG~bWkw(oD~/wT^EDW5 [E  A{ih ! `,(K2icVw}ww0C&)s /ThtQ͉}mvzyh.XLoO)}5) VuzƮhLVb)N,̸0dCLf|dJE'gkwPe2Clgi9Vh%`n>@ isFbg1hpJJL!l6'Ox76*1֜Hy̹9pYl;h6n&{ :E6ZkX͆9Sez5bU-jO[ESd;-[sWMxiiir 9nG f=Ƚ3zxD2 .8#qܟ裕nav] @8cK2Eǭ,]OJJӧDeVKmttiKMgڴ8.~xܢ'ZgEַ\-{L6XeZB(֝C^a0߿ۆ_efqcu];:-򪕗bZjL^}䫃/!.bW!ܯ}8GhV=yX\O<ѻwoY{<6rO&L8~G0^UV+nqf4ܑc4Qe#f46ቤ ,@W! 5ykxTQmɼ.}I]?k߭>ZXV\璬RO=jΜbkTD; Ҿ,Kq111GDDd)oȬqI4TUu}¹m}vkxщc.or7۪l-ܙ_$+6QџsY}ntnw|ӱ;v4wz/g[b~8v`KC/AJ~kZ˒ ?ӿ.GDbr6bK0X_e2@ljuT{eֆFGhn֤Fvu^O,]vl_+owsNg zV鑚ܮl{;aŪ~%!wgrn\qŦH-l1YlVS;k'  ph !&0lG>PUmՆ#G~1Po+朾Sg׮`SD&'oO O/L/:܌>4fZOwj}~W|^gb?]Ε]ZV:.)OXQaq3]Xc-3~B֟a=}lH[5ќDE;SO[̮>K:h1ldq~C9BJ0;.A@ 4z]rѦ^ZVjdoͶb6#u[BJ%S;{*fGE3O;t|vV=R'6/2ZlP-+ F.)KاcF(7Wi< eiR=%ˌkjOo>I!"As@f1y|b$ҖW+IJ AxQh ! 0ם<넺IUOêNV)IQ{S/mNNbHum0]"j w(ӬeעqKmU% {KMM9su뎟M⎝n߾}nHbFnZkfE:>S>c0[e |J˞Z%uZY^v" 1`4@ Pʤn2]h-aUGloȫnӟh7J0;P @ fGaJ}fֈ{[]a4gҤJ/dX}G^52AkGǙ++ϊ+4GGݹ.D5&kFl߹KRWzY,+͡<+Wq񿞻0syE#'yTx,7@hM&)>=ՇK}ri+ڈH߾2!(@݊T @[ժݒLՠw[n4[fm1gC:ZϺQfa6twPyn;!]tqut>dS4NPK\b䌮Iw{7L)*R>fРt_b?myJݣ3۲hKZmɰSUWT4,=E{q,tgtoV8k>.j?$󜄨;/~>@` +C@YdKjEWdM.=Fڵ'y3nhs@ wjzB:n4UpKR܍uko5*aÏרgT'h5WoofUuzM-ľ~(С ڻw!S!I!j+Tp@   D@YR-7 ˓k"m&g}[ͫ`1I-획8;}G2Ð.Ggvt;[M۞UF*M9TF;MweswHь8Sg>|SGC0;<3D@ `VEcۑ7cK-&$%+q5FFZԛ$7h=(ޜq#2W[JNlA-k} k|?js۷Oix@ lq@#Pl??+M,IMUW C<;az` hZdGnƞM[u>  擀 -xxҵ-:y'H7ޜ@- fI_@@Kg}ږQxDeiFI h-avkjS !,SxeiFI h-av+S *t3t-"f7B 4`oCR8SsMbK@8\f`@< Ȋߞs ΄1τ:u"I@ @@@@loȋ   Gl<D@@@loȋ   @@AÜyf@@@H=F3f7B"    xHE@@@+nqz HA@@@17FQHD@@@M-l7"   \fRƛkJ>@@@h40k@@@ 4cKz2(   עDIDAT (bl   >R:^@@@ LNc7uo>L& P`޼y-,@;)"VW Ծ+s# @F8gHgj$qO:C@@@+F"3 a'2aA@havȅ @X ̺7F@wl8@6/ۈ6G: 7ۿ   a}<   jw̮IENDB`ipyparallel-7.1.0/docs/source/tutorial/figs/single_digits.pdf000066400000000000000000001215071412533144600244100ustar00rootroot00000000000000%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x+TT(TH-JN-()MQ( X(NU5Tp1 endstream endobj 5 0 obj 51 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 800 600] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 800 /Height 600 /ColorSpace 9 0 R /Interpolate true /SMask 10 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream x Mǯy $LW^sհaZȁ/_nܵ]C@]Yw"  Y25 KMMTR|8<'O?vP_E\2?!;z(dɒW\qźuB q„ u1۵kW`.(gɒ%M4]%o@yWK" 6{K/y)_|Ν׮]kgΜ.@ڽ{[kc &7n\P!k~Gkv>5 (B 1GԴiӳ:;( ݫ(X=܃8!= )]4EY !CuP ._^[бjpK+7C+s @7Tzg142e7|3=#Ñ#G_=p,0,WG%0ՀW_yLܹsVVpi۵kW8p.]ff䴮2nݺ.\kPVZP|+0hwH>d\EX& iYS ^ M`͚5rBp(MD}] tЏ8qx -j㏑>pѣ(^QLaq\r6o\\%۳g %Ju7jfQOf}?. \ر8j(d3i#Ȍ~'.@V21b3fy'(\Ddf6\)Jw:tM7!믿n-9ڰ10_xËZ2D|   P*3 02RѿBWA!pѫΝ;ך/@(o Cɓ'… CC:qEXv]w~ 8_DQ6mVaW_}cӁ3. dL( R͈^ʸq Il\p ]߿fȵac:u ^Ta$F<~$  `9a#!3$xGl+xef3_xrT =Ya 3GQ+L<ԫW/'Y[al*JuDo(\Sf63<3|#nCXyB|$ ֭[UDR~'`=Nߚ\p d}F68֛!׆=!)#J ? j`1!ZDZ[+V0>bXƂBt(!P('f0}G\/lJHH$0rHX%nfȲj#[HߊƯ%nXuh>~u~1~YVQURk{)8i$Clp¼C$@$ c3%_CCo :nQ\GkuXtC`WwyY.;WXcug#u.\c B_YZ,G!r=vffdW1 'ED5 )9Icǎ!U`thj܏ˏ$@$@$ Yf}֎Ƣb5J̿2\pڎ~ zyQ+̋!&-[*1{8Q!7͏"jؽ{qk/zuAT_E!JBe  R. 9Ѻ| V`D0h w/FilV*p}o\`/"ZfuHIWPٺP>0G   {O¯r㸉>1 z( L .,LjM1V"!")a82}u c?  ?uN=eĿ4h5yK {y:!:%DDfį2,(Pd3'D͌}W ! .P $Nb%EQxL#FP8VAp'--*FuHIWW^y%_!|}(TR!p{CG  P]#G#'#VDF~밉s};nb駟k s#aY~xx].x Vq`b 9y0뇡Q#VKFvDE19_zʨ 9i"N"3bLDn;v,37lbw'[#Fư< w,(zdR#   T+_~V&ڵ3Ow]|9^1ςf3 QwgXHHH4#_mذɓ"5N^G}0o' gO`3[F~DcFNs˸xx%ֺxM$@$@$@&_'̾V[oߎql8#'w|8?1m6Q\n>%jȀkݺ5ƩE<gڴiЎq*ӫW/,k owQ/O*8?E;^U-%ѩut-EuU;:+uJ5I P r2Pd9N.hw'SN_L]+چIHH vCcg8% PHWmC6F$@$@`g?BcQf  UԔfP*   UyK$@$wCނOp-K& ЛP7h$@$@A&>4ȭ]wFt>|J$@$@Y`ާmHHH 1ChAheH$@$NPգLچH-HH'>}~ᗖ$@$@`Z#mC$$@$@">_妴 7i.  թ5Յa/OF$@$CjJۈ A}(-!+> D'>4: ?m; @2؇&COwiz/# pPdچ[ xE}Wկ~QB  5 U]TB+P  ?`VsGfچ;Y ~؇צviD۰$! AkmΊ9IHHJ}hV&  Ur6ԗHH.C"_9 ڔ C};X mÏFIHT >TVPSچBHH'>T6JBچWY/ { :'?m9,HH@oCndm$C  ~ti) dE}hVdxA   M[ޢm# ؇:AU2iz#  pPFچ_Zr F}j-< uڂ P6ܤͺHHt">TִWچʕ0ALb`Z-w` C}=u,cR'צϟzYƿR(L6-יN}h (p&"W.1~|&-oQ.,~%M~ H}hD,   ׋+V͚3 pCÙAAK `ذA)&L"G1n\GE$`%>JV + ^@p\wXQ:YyowI Ci YmdEI@c/G&MRE]usGdH؇"@Ȋ w+Ç"wn1xp<|F'>4E}F8|DZL^#s2 `{,Z6b<$k;^cDF3?d9H ؇cT#(f#=|<bf&Q/G">4Pp13 @΢FLM'N.ْ% ^ K}(M!+> GϥKivhNF"^dnОP8ai $;:3XUoںU/.z=' ռPcǎ޽{ 6nxٲe֧5mD Л@⬳:UQ0,& P-_ԢE};>|U~H"͛7_xyիWB;wZ 6ģ9sͧ++W5kҥKTۦMZ1FY E bY;Wؚ\JNiI}h =&i1ab&D"_|S.WHKCvݴ ۇґ@|p ~"f'W/qi"c'"RpPWq2چ’@6ΕH^ڳGT(.ԶіIkCnum6$Zٗ.3Z( #U Ue}PoHOm*.b[/> Pے6&H^hΙ)R/P=kK>$>ԇȴ @pkի&׮rab\$[C}t Np1+  ` CF:8_S5#GDժkܯ9g̐N f"լAmTa#LE.xy#' fɒb۶ $2*6I aʕE LѱD`MSڮmv,!0i1YiZH Cݠ:hl7Jt uÍ7…/LlKY`ާmH&L9s}&޽rA~f0؇jЈ@p,%ę;W%/_.5Ǎs M`jUD]Q`xiyt?y~rI}!h!@'p([VpbF+|M}Qie$`;'s`W iy]؇ڎTi4% K}uu(DJt%>4Mⴍl1 C`9oH$ǎFDb $-WvچʭCHJ`>ymYz&QZP `֏;m#:>%u`B ~]lQ}bb$>3K  8D,^\qnpav2_ Ahtm$ƍo}T'nuZ7Rp{p.A`j/OJmԚEW{E=誟x59KH_؇ܔ&mEx?[ݻQ,-2!Chhf*gXtiC CMZ,b"`ꗖr_NچY# E``^ef",!v2_KK/'m}欑b'k(\XΉ^p&|A}/!i`g$#EB"==φqݝ'>T6JBچWY/ dKnAͨUHs L'>T6JBچWY/ dKӂEL-̝[|E#>mĴ H( z(}8\Q8tH؇j۴I+FH! Gw ˰{#_T%&>4UyFT<|H@(QEX cEbŊ 3`z y'm;$pݢX1cq͚Uζf"5 U]TB+P18y뽀^ uMUjO}m䕄 ȳ^Ȋw%8h/SePwɊ؇zI_>}RPf'kQ=#6o{,£`q;2ed5 SC}_e;vԨ2%eUJIMI9kܡQnU*%07m%KΝys@ @eCU&_ Vr<4oPW'3&go?3$2=P:=l#WVnY.-pS8^d@0̟2:Cᩓ$z\Ysʖ:5uљO?\ēOc2!ZEŊKʼnwINzvaYƯRR0%;O "ph,0eˊ{.Ə^J@=P_]{mꫯ뮓c=={3ΝNd$8M=l "*"} Gաm`iv•4A\ɣr?_:Z8tqcϹ#)xFX\tA%SI(ժ%.P=X%Czꭍm,_Zr"D.Hk//nQ`wpXz6l9s 'kʱhK/ hӇz QӺ c,TH:G;9">P<][:Zs-Ÿqr! H55Ԭ\,8ZL$-mPo1jY6Ѥě_ĉCvp!'oH1fg6I"D!gTiЦaXj4_pp\,8Zpڷ='1u+ 2Erw&}5Zxe )B|?񄌷CYŲe ֯? L 8vKa"h xE@>+zz׫m`%,};Wt\ …Qh.cR䫯ժq+\@k=DУj`XS3 s0bHgR7kYǗ_JnV # xB@>nAT@tR}vsab–B/u_vdԨd>!VH}C HDa#8bҀ$&~Z0%Ig3mK$NqР[g lfMqͱi|./u&5t(A4/׶2'|0E%x;jh*%$:└8]ӔhVԭp0'Y|?.CG߿6mڔ*U*G7ZjUpbŊu-6l֭[T _|Uӻwo^`ƍ/CH׶r_#){K+WN=\I,aD?jQ>D9n2?dM@__믿qjѢE߾}_ >*?PH͛/^x޼yիPND\:&LPf#F|~Q£>jpaRrYf-]ʓ'ϊ+ 慯mEz*@#WV }d9xNDp4n_5Uu&RڵkWյk2e`˨hy0`tL1B E(Siĉ(sǏcaÆf~¿JMUYe? Q2,_.[s޼@*q1k%>4T?Y ;y~Ym׮ƬwB!۷o7nnݺv GF?Î@mWj5kҐ%Uz4ܿ*B'O*3g#)alٲ'3˕+׽{wk L'Zoڿ1xجw.# R=>b $CLCuo_rJBU{ 7*룩Se712Ajժ}G,i߾}F8]f|Qܹ3Vd}Th#õ3G+5lU̘X.㌸O}Tjժx@(QdX!jhѢECa:ַwڍqJ, OI&:7F pR7>|ѬiG,:s{Z@UZ91}gN?]w_b-n*`~mp}X+37^cC5n Tm>tXYV];i\nmg,i'Ns-Z8]ˏFYf"@,|ׇƢBwSO;-ߖ/ r[, sUwvak ]ΑGQHG.08vpvK V  N_}(e]HAR#QR}j-Ru?_JN'>Tq#~b(Z >u_.xAs97G81C`pQ8tH(&|ԇjB?j6_ `7Pӗ_J 꺿luT3Pl T >C:,|dw-*U nl:lѭ y"W$e9Esv0|ԇګ8K˖_l~j/[0nSH_"W.mpA T3C|D$~١ ާOjj'M7_WFƭ[nGԨݔ!s27as+9؞/H L1 $I`ǎ5jLIY$a/=N0Y |%L<8Q^aۧOu:\{sWG_!P=j'N[oUU Ʌ* ξW_\ utHI9 [F(5kTLbf%2bƍJc%Шʊ"PfSP߿zAQp S#&؛oGW fHr]%C੧D޼ᅮ)ǯn1课U4 d,Q,޶Oͭ?=[!3xYI _pW~sI`;HO' o PWoћYk.X\~yoEni))븾V>NܹSW&# &JW_;n $`|ޝd1|=vH/X0*fEymE6Q#qLĽ1v6mZ$_D<ӓ/%1ӌ\b`3D OIy5;3K}d`Eh\\vYŰ:Un^Upm *W맟kπ( ˾!ӹ wGb ]BHkE>؃ĶQCg+,][ LjKqmbEp,qo}$`5vؾ]>` p 2p e+*[VHFϴ4/[L|7V.TnPIFߧd!ڵgXB LjK_Rg!p3 T!,ntf<~+Nuˊp S, cbr6e2%qosP5:݉Y!6N25'ХhLlyA=emV2H)$+%JG+ۇn"iq…19MqeҀirp6 T هǂ5eM@Mۘ2E~WrI# \S&|^}%j`ɮ 4`-  N'EqCcׅ9#mtԶsW|رiF7$N?]9H%IۋK.I ,6wه=TVA8xP43FUd խl"@w >_g&ѣr3cW>4- …r 1LX@&' "Wql4(6.oZ%fWWnPV^m_?Q'braUD; E{S_Q4iK)0rUCg#P6-R%q] Ÿaٓ3DŽ 2Vү/kXyL`j<WB+c=zX$R޽ro61@t3?&SQ.?I@55J*W79hS֥'`s{Ι#ul.ũC+EÆCI%|*qUU\ R67;%S[/ l!AѢ-E ̚%M*<ʈͤZj (e8 _xs*!Fl!'L& '0|Mlj8&3TI2~R1cxVEFS21yF 9];/VJf9J@yJIDa"M1qb&C3I^P6v]5-NvӐ6p0槟ԗzC`!vLԱ 9ribphQ?~gaZ(*ʖy*+wj GR+!DrC}HTsձeO?k]8Яjף;䚜#ІZdO'Lկ}6&L:}hd5ulQqr.j1B`R( <ص+ٸgbS\`GvΜ ӇUE@`K߾q $fŊ 1%FQ[$9˖s^wޑ/֤N*?{M@F^Ыy8̔ɓUvN_Zqپր;BGUdE L)F}'yl,2(Ea\}I_M1)/Ks 7D^>4d5EleKѾ,t UW騘:dWvFaԓyG񅀸7I>4\0ȑc͚5![.'N*ƞ="wРm 3h<#'UEӦ*B< ЫSǃzYxCW8#<ЇKٝڵk_eFX~6#3M>\/4]y"`x]i|Lu(TVE*2[F^xXapR6k JNL9)X|e=5wSu\xa T sumR,?jժժUuL]qsB% 3hs8)0ӱX25k2bW(#&ЈRqW_T+մiSK.Æ ۃE 'mcJiar![w!^!S ,Z$* \=b(f77ozn XLQ2'P pXqo'&@Xg*yއf%{Ns+(O???Jt('b~!uM758E3/}3V*\4y`?z1c8#sRYMom{k_T#"J2e\h,\A/>LA({p  "𴦯rX<+ʽe4K,RDL=L*Wl߬crƒMoꛉi(˨C3ɪ,nQ_3'6KUƣl_af飑 Yg1ֽP}_ڭ a)z]M{ohi,_||xқqSOidj]IgB5&0 ,[&a>Z+' }h4zhW~t60M|̯?O͛ƍ5cIOrJl ~{E+>4Xz=4T|l;^uS@dn#}@k ?L$%?2hI{q(UX~iӦTRII!|];z ,ظqei)yb۶/I sQWGko 'PAyuNt(ZʾzO+G-Z%fDE2`!+8Hl?C"EpfŋV^z*TعsgȑX5dȐ+V7^-87#>| Y.]zUWɓ93Oldl6+ `gA:k)pׯj&}Q_ЬY x1jT,>^,rtAP,܌]])Sc\ƻ7oΛ70gx%vYc=w7L8e^xu6lo^xb;&MLx6 {ݮW^|Q!  D'j4NOy&Ol>F9]SJW+P@~/k׮fF3g5۶mxY+[]ѣG#ßa%ݷ `KVy-[.Uf5ǫ[WtꤦtJ-ʕEd$4@̋n"~Y}W4h]pjÆ &Ol}155#TǴ"J.}uwʕ+1},ix>ܓbw! UWqEF3w_rm,hF\~yvN=wId’'LEW4ܿZr%|_"ƦpsX^,F.8^!_iiix}2qm`3~ f̐oy(U`qlbz t$$ VPxԅ}hj޽[+h&f͚u$*rѝU&p`A1rRxV6/τaž `|wq7/+I!1_|޲zϔxYss%]w/_8?x!kQ`©Ss~bjszZ:ӳZs=۠#eTd5[>(^<ֈpoTR%q9g[PZ2ve[v,b72m6v,e3Fdv C$Gu-$\j6org\ 3̒%>KY3ѥK/܇*"_6Fʖ-aРAݻwl0gedx exjeۀ^q~^{F|pwx&WTbDF`#)37?)qpTV4VoѢEs̙>}:|nݺ)+VD+٨QX~뭷OЌ/=;wƊ#m֭[q & `-6MPs;R8 y'8HwߕqZLe͗/XXrSFrR 0hHƲeה:M'WfEtU,U"P2s͠U5q׬JgamwԶ88j F`p 5΂<$pOwO,r3 q 3]_[r K 'wl㧟d>o , 'ya۾'@BKj%)4Jq_'Nr-F_Ĥ:e 'wl1Mo -NUy1bIJ<$VnXף%0 ̙#-[Ý>4>4͍&M1bwlM&e ,X*+o| 3wn$QP!XLvQfڸӇ-/ '/X \w}qAAx~">4zAx:es7nT<+]SNUr=fBQ V:3f<+ }UgO߄hIKKرcq *tWZ*\|QI\rʰ*;y9~܎+N?]]d"G =yG ;] ,IGeǏo?p DDpOK+*ڵ˖S)*7VXM/W|$"oqMP lcBM2R`D`Aa8e8 G/)F\|q4CoA9OTj4sR<(XЯ;!A/N%Urӧ'>"`,)G(\H|Q@*Uw߭#>7⥗{KL lg"4o.ڵs6Vd4q2QÇѓIxC:/) l.TN{E6'Cҡ>4h FƢK-E)X壄Yi䷥JnD&>41rd6٢?v^)՞hlBl$◄c1xh_䥜Z@8e aءRz+fHKKJK'Фp64Fs&_@,uQǔԩ'7d)h vK&O!+A`hѤqMJ 'lD4PFC (DE J}w30sY[&m l a N)M29ч&)_WI>~#)8B_2s-\9nHrMS۶%Y _wְtܸd벽MV mcR9hHA ~l(•W%_>w +qβI+=j,1rއ&Rq\]-VQ~}qJIACEB xK}{Ѭ" &zH`Иg&?6Vը!0Sä g%|ځ Z#p֭V"_rMCmE(C^02Q `1 vEIX&E/X,b&Sq1^2l0CmE(C^x yLI'hDQ0!nb@ǎ9"reF|M{fn k-ZذUm`Awя?ܹRkU2+H`j eسN>(ell c" 5L8xP"5 &ĄuL${n'TG"JJaCѣG>؇&&R1{6) vMZ5{~%++q / pU=8c(ømCE&`msMX4+''j ?j'ԒҐY_IBE '{dG lˌKC(|*UD߾\a6c,|2طO;6M~PJlUŰ]U6Ƣ"ChQX~ Cb@&{"3wodM  lc@Q pCAB l(]_t9>c.=Jc!'byb!kl3bSИ*c&_6ճHFbgܩ,"Q Č@[o)+gՇz;A2NX9N2"wÔM`{+k'l -"p3Ȗ>NX2l SIoZW矗+ՌwPo1Τڵm>!ז>T>FeVlEK!?K񰓱fMѥ2nZ #FĒy rmL6â!m/|iut$x-e˹]I9 -- JgOqٮʢxU}{,q?JJoKdOH)(S`9z ?9bD0ӧ}ǻw۬]}Ұ8$iƙ)Geqȑ2v"6K,)pNEE|Ix)&o 9rbQ}LX /ݻ;* Woɟ/l%=Cݫ5]C"g駋x5WC)L 0bhBn7jȗ 7H@,^,|%QxF`hS2}°@$cӦ0<NuMc1Ö-nTx([n:H@TA3'CFXҐLV dl㪫"%+QCʟ_<_L$Sc"IG*OuD  mq] G+ q׊:u2ۯuX! GolfrCJrjA}X%K7^JUg*qXPZ+:!;&-涅_?BCC g$lw)Tq|B;Z).\9q=*ղhЀ,d[oUҒ] Z̹8Ce*E 1*|Q@ҥ f}㍀c:XLY. 3͡X0,V)Ʒ/ '*dK૯% BC KU(c"8~\)#zL~|{#;>)iXJ CG[UCY'p9{sHb+xY'f$o* ,frڵ7ʕNUX4,W%FӦ+URxG'چ={D⎄_VH1Xپ>!nk@*m%ÊNH^T:L { 8l !C+oJ0EX[BT{Yڶ;(F}Ұh$`3gʟ`S]&0io3F+mTFnyi,c:X[}Ұh$`=z8r JT(K|^}U?[Qr[}M7EG$K~*X͝I} Z<'m`X1# N"p66f̐ߊ}g[,! 2@`paaZֹo$,Y5[N5=(nAԨaODDW;lqkI!ظvIb78'o0,\)FjfQa>H:޶l~=Y d"- ZE wak8o#;k֬i۶m"E .ܲe˕C=C5jț7i֪UZG6J*;묳&L`yeqY\=^rW,pHMXL$%6XnMKRjR:}Hn}B*ڵkZd… [h?Uܹٓs?裟|4i'O\|x'XbŠArcO͋l6/H?˥IF5¿#,Ń mY|#ӿC՗H]v+W>mTҁʗ/1x Yڮ]p瞌v[||2-R`ݻwgqru2$~9IKgD<,a(/lHtqK b 9>}X3b*GN:|0*Ɍ+W\Qt*9wؑ\` wf͚e1.ⲍ֭Ev!# GAѩڲE-p= M*ի}Kk=2Շ:. +&1dػwo8Hd߿-Z[p %KܸqJ=ʖ-k}Can0"0p(Qs[| Q,#XtuZ#;^zf'NԬYٳ͛<&qbŊheQ6mj׮m~4.2vxMiR*?vNxbv+W$1/ߨKh=XW0>KB`~,4ߎoWPqaoŝ:up=^Yf(LӈӕuZD% D"h3^AvpN4|[$LqF-QO_X;=SRRBm%0fgS9r4mt04[a2&#aN:Eh`2e2'9?؈+R^G$0wUou {G,7I@OX-ۘv9r^rr_Aف:Q6cv\Ϝ9իuٳOQF! h &rb"`ŋ -+_KL&{uBkƯ͛-> QXGBlRhQLopycǎao܁WY33z뭘OܳgyǸ6zH.z@~r0* (_[S#M ١i,ewȹWwR};°X b,{ĉ+UԠAl4=ydƍ !C>wE,8TYx߾}>x`<=zԼ6]w/H eˢ>3,gl>5 hN`PԼ暺^:žuS}MjcVsCYJw^ĿV¶#XhŐ%aDz>u%WL{aSbxM3όc1esuc" u|5B tâ8D,@maAGEBU68 _}N - `_6gllYDWeeG(*>4kFǎy@&PQCϒF?F '(J87C ҇G [ŸqĚ p%mlT֕ &>֚qpyZ+r8x讻ܫ/>=QXb |0@\M9sf/aWL8ѣnJyoɑs7lPO8Rdk8QF =V|&&|8gq QrbHJ_E+Bz9>4ҘY'm{tҘG{wV1/>=7s$1u3'(:g^"# sd*9z)+?@t+WD3 E`jα OU[ MᝀWp5.SeRS*/r1Ln#Gʍ-<Kw\-8Q8"T4{vWܼExqQؿ_w=oFhu "65W_ Tvpb\ Z6H~%`4adՈn}.D;BA,d" |Er I@C]$:wP/TM4nZmVu[֧(Kҿr- b`(=0TUBwkBZjݻ5'I 8xB' vjKCYf,eECcyy4&6زaCUj.@{[+DiLH/؏K(}]U6GZ[!Ҏ@S+N(_^>!%W\,>^;cC}:@ +XT&Hq9H8>o^1ztEАO?sWC S+EuYY'A!m{ $2A4)=%%3:()G={;\''زE1{+hQOYKU#6VL[oUMXK;v^eJʪ;RRNFt|ٜaMC׭~駟zX>9Xz"Ɔ V}W=q) zʹFe/ } E&g <<7jngkѬ1cŽ؇j$F m`0Na![!1reN~HN~$OH"ډ.#Y#6ϢK m`W'J5;Ysp_-A) Ays)F39XzBlOD JHH 0 K&O‰*gARHWA!1`<3 xHJyr*'P!Y!}h*] FݺO]u^$@$Ӧ@X8) ƍEnQ;(u>VV9 Kr0ASPk> YP;tb{5! h^4k_|B9&iEڇFG$` D n: 2 Hॗ֭*ʦLBq򤗲XP/`0mc~uxDD$@$H8Ms ZΪ?_C}K0m㭷@O?GtJJ$@Z@S `͌i{ܼenVʺ|Ang )$ O`ǎ>Xr?~O| ?o77fMUam[yrsL$@$@*[;EѳYa9~,_O(5 hG'Ie\ɁNv&5|s"/+{yTaybQ4.$@$c83ç2+y>&ѷo(fͲ$<_5lv{\SG$@>"(kҿR!=(Gm|>sfIP} P_Ei}E:Q{Va9r|kD$@$/g80A לHII+X*fXَ*$W*2/?g5T$@$@N73& n>vlIdRyf998wn^}y5l#%e]J #\H  '\hƱA̹п -FU>ÿ0JQ! gEŊB_/;ϓ#TJ*:E *4  Pᅨ%嗋'!p3p7ٿbd4<%- q|@o$uE+EBA12WR( xA+--^*o*ҿR%ԓ#W%" 0XդTIW3ot.5RHCW 5bdW?ƓkC$@YB%DǎKib`ԥPc(&aժ1b CqHH ; HO<]>]wRPc(& mC8$@$Ebժ8^ogyҀ}B(  @qcQr b]uh"8.dedVAiQl 0༘EN/:~\+&Wه*ICP5( $Bw¤'L]gRO>QK^jJ6Tj B$@ HM VNu_ #GԒ}Z4 Z @ Tv!evkCci~oAO$@~ BÅX c(Ckem(H%`,zdQ+⫵kUKUILSP > v$@$ \azH*%N{ 4"6mE$@B DժbϞ &MD׮^ EC[J  m EvȄ=:T(IH$Fa5b<$&H01FD6D \ (tv͌Lw~_^uwխyu_խmn V^bK?}:¿2T1mD s/.wRWދROZJpJ@F9@,XoUl㱍bpo9MXuכPsunɰtk!DN_r?*H-WΙ=ЛFd҇#6T BAL&\lyc.؋>Xդ]0l#*@@1©Tyxj 9ʉFWw~רQK./<8f̘ʕ+תUSN7o9p#իWJm.\пL`A!$ڵ)ukg@Bjz _$jO=ԥ^*7iݺurڵkנA)S O`V7몫,ڵ˝М7hЇ„5jtT?6N^[nǎg56mL8f͚Aojz}ڵǔ6 # P\^}li>t ȧշo_9H+WTޚ5k?~СC5 XR%jC^xQ7ꕙ3g36 # 8rѾR Ssz9IFjR;lD5kL7 F)k׮y+|O?{΅Ν; Jc@&P V͚'NAMe~чTfϞ-whԨQ{ݽ{a*V~Xϛ7Oi ϼwոqc-!>{B#S$%xLjb uwݻ8?z˞=e'(M஻R(]_|ѣvijAVo>x  ?ewзӶ*b%&c6mڴ]㤎p,[G4kɾk&={2$ @ K9|iSg>ڭ)ˆ Q (QC"cK;mB_`Րsa/G 6T8qٲeZh6|fY'R&ڱ  @H`7KS?݉'L%> yRyƍS N:U-/vZC;tRyS18< P,d@+K[N=KӔu8jNCS:6A:!G@QOZ# ڸѝmdi8 TÆ9+;z'+t.$^ }h&l#]JA@X377=X_|dg;^ }hzP+a@|6ov/@,$ 5kklk-#m$X4 @,\9ڇ*UGcч]6a@4u^4=Kv^MtK6,QbBڴqcǜ՝ b.jCCBDFA(;bըv;)~}XVaƐ@|,p}3q8? d6SZ@ DCueNϞ! GQqPlN!5 >b5km ½N15*jC"rA{﹁X{Gj; yi#lZ!8 ̟?3gFU1NQQM@FH @``J:vt~r!MPqFIS DH@XӼs@ȵiB.6Ccli؆Cl@x]w 9k2wQ[wчZ6bCME@ࡇ\_l-8쳅)_eч&`n)4 zS|۵sߡ5X9i HmuNb}S3kmNKjbۈ3@Hw - m۬Cjbۈ3@H/N74ib+P[5F@2 TVZK/PKF @" :;*K 䠆,C-U\ bc1@ @I%bS-ZW~X}hU`؆A0@V]K#!}Zrl#j”@ n ڸ vo}90Ml4  (ukeKS}wkSxŴGCMӈ9`I Kj՜~N+=JjF̑0GH@jsZ 6mnCjۈ.eC,4 Q#禛Cjۈ.eC,jX[]|C_cQҥl@G`&7KY_3f8*8wCMP6Ǟ!$9ؔ/wڷ/CS:6A:!$b5B/_8GzӦLPF @F)JwikŊ;ЇZ6bMU2sצ=<9]ԇMΡ5Y; H/j `:PTGիӹs<+?҇ZX6bL%2@>n vwǝlgX?}ʋXtl#b@ (K}9a;sIB-Fl# @![n ր8qȑ$Mi WJ / ̚1=:UT޳z={|)P;F@fصkw]ee`Vq6bEv]\뱍p ѪΕyVa2Cӆ U'i\ʷmV/RᱍHR8 @f=V_Xʷ}Txl#R@"fi2M@95UzS; L uZ-AjZf3Wz@ Nr4B\mߜAC4!6B9Cхi`iy @h*~9S# $}h2E+(R& dLrڈmoAC`E! P<d]l#O!@,CB/b/ ЇF 5 ."o~؆:BB@0}z1A*l-  H>F#3gj MNjIʁ L#@i/y{I@@}h l#H4 @CK*Ӟ62M"@䕃m$O!@pkY~}O?5jt_,Ç+Wnĉ;v7nܸr-ZM/}?)ci]vfԎӖ/J+Aѿ Xǣ);Kءqb~0HÐLbꧢ0j\X\*HNN69y2_~~I4}c;`9$9~- }IwyWĉ h7t{2:jM*;K[ءIo׈z(姱zo4ŒjCo؈ʏXo9ɟz+Ҫ)=Ou֢h+u=96&v:F=9m_{zzX{>Y 1@ `2Ͽ?rZZ޽;_+_y XJPJmAwҡJyFɩVZΝux_։<w/T @@_h+vUɿ A6$Mj>^֭[j9W1G @!P5Bպuk%W6mtw=)Կ3_H@ !ԩ_M6Mޔ,_|B+ @2@Q+TЭ[7 _wܽA4h* C (ʿZh;SOBǯV#%@@(Կ* fggݻW|c=VLVcI@ d9s[N,Y2rZjuYWӻwoo5k TJ~W^(? @ &?rtU\Yku>΄ +:&?X/"9NJ+{XǑC5h +S_%ؖh @aذa| U @ L#rǯXBSNW^84̀B "^zC:P׭[W+wb@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @9eF endstream endobj 8 0 obj 37194 endobj 10 0 obj << /Length 11 0 R /Type /XObject /Subtype /Image /Width 800 /Height 600 /ColorSpace /DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >> stream x1 g @a 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0`u endstream endobj 11 0 obj 2117 endobj 12 0 obj << /Length 13 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xMHaї$T& R+SeL b}wg-E"u.VDNC:DuE^";cT03y|URcE4`λޘvztLUF\)s:k-iYj6|vP4*wd>,y vڴ=S԰79 ڸ@`ӋmvUl5`P=Gj)kP*}6~^/~.~a2 nײ0%f|U 9l7?j`l7"tiNf]?uhgM Zʲ4i[&LY_x {xO$̥߬S]%֧&7g̞>r=g8`候 8rʶ<dWT'<eL~.u"A=9뗚]>313X3-$e}u,gmg664$ыEzL*LZ_j_]Xy[?Xs N/ ]|msϚƫk_WfȸA2)oz-di2|m٣j|5ԥej8ɮeE7[Q|IM%ײxf)|6\ k`Ҳ䍐.> endobj 14 0 obj << /Type /Catalog /Pages 3 0 R /Version /1.4 >> endobj 1 0 obj << /Producer (Mac OS X 10.5.8 Quartz PDFContext) /CreationDate (D:20091118223113Z00'00') /ModDate (D:20091118223113Z00'00') >> endobj xref 0 15 0000000000 65535 f 0000041199 00000 n 0000000165 00000 n 0000041052 00000 n 0000000022 00000 n 0000000147 00000 n 0000000269 00000 n 0000000358 00000 n 0000037750 00000 n 0000041016 00000 n 0000037771 00000 n 0000040080 00000 n 0000040101 00000 n 0000040996 00000 n 0000041135 00000 n trailer << /Size 15 /Root 14 0 R /Info 1 0 R /ID [ ] >> startxref 41341 %%EOF ipyparallel-7.1.0/docs/source/tutorial/figs/single_digits.png000066400000000000000000001075671412533144600244350ustar00rootroot00000000000000PNG  IHDR XvpsBIT|d pHYsaa?i IDATxwxe B Ei麀TEQu};*R\VDdArQI`ޤ5@=ɼ<&`3ܟd=C89yò, 7; """""6*@DDDDDmTۨQ""""""nDDDDDDF q """""6*@DDDDDmTۨQ""""""nDDDDDDF q """""6*@DDDDDmTۨQ""""""nDDDDDDF q """""6*@DDDDDmTۨQ""""""nDDDDDDF q "ON@@*VHJr%ӡC`t1h 09bwr*@D#ҬY3/NPPEVZ׏ f8pœ^Qw]?OY@\+%%:|rJ*Exx8ʕl۶>L.]ҿ'<<ƍSti{WݏoݻwSHLhn_ݻ)P@ƍz7y)[QD˩3{l/_N Xb!!!? 6dVpa .ΘU#00+}eaY-7k۩t*E%KD<κu8p`@4k,ö<8<>,˗'TRz+׷,SF :t(IIIYaÆQreOɒ%ܹ3}]ϑfĉԬY3CǏgyƀ,_J(Axx8۷oϲkQVc@1c*U" /~)Zx͛eҵkLŋy7]6!!!)RxO?49W\I@@Y柍y.\"Eо}{vܙ\={xG([,AAAPjUORRROVc@`ժƯ7 ݛ~)G.\H&M(X ŋGǟ݉kDDs֯_ϕ+Wȗ/_lڴ6mpi:t>Ǚ7o? ,ѹ΄ ([,=,Zu]7ӵcΜ9<#(P^zQL֮]KӦM]vyF… ٲe #FhѢ^eY 83fJ)]4 +V`ٲeԯ_˗/ӦM֬YC͚56lΝcܹӇ͛73z?s:8N:wٵk ,`Æ ܹ%Kg7n̹sܹ3UV%99{t:9r$ŋt'Ofҥtޝmےg}֭[ɛ7ogW_ѵkWZl͛?>qqq[.SK?wDD<̷~kɓ k͝;:xu?' *X޽VR"EXW\I߾zjpXjղN:V˖--aUTO_ʕ+VXXUHp|bbf.]ںxޏkZêVuĉ/^6mLO-jX[np/h9+ :|pZ8p`o9 Ĕ)S,aӧ3r8Vxxx~ͪTp85k֤okWP!lgkYz뭷2l޵?r8֔)S2tR-˲բE Fe9+44ڷo_}駟i`-[,þ'ZjժU7o."r-uӠA̙Cҥ5k={rʔ,Yݻlٲ:`Wxq:uӧٻwo.KP1v%K8pÇ^z3obŊ?=V|1nܸgZhNbРAZ;F'b„ 8MFB2/UTߧMFLXXk׮W_t;dСwED.X"5]tg 66寑*,B?'555˯^~+,2kVwm*iOv3w]z| ´V6^x1ao=9u}G&55'x>-rF6v nݺXի3ۼysZ,7n ynTz,+WfڷaΟ?ԭ[7L/_f9(O<PV-ۗ4W+T+Wf߾}YN/V|]т?g:9z=9֭Kdd$gLAIXj:u7 "qfϞ_cǎ1exvDD`e>}:}˗[^rs΄1iҤl[jlْ Htu/OP"E0m4nݚa_tttר(Q =Of3g2,+C0x`RSS>|8ۏ;/H| Pzu .̢E2\Krr2ÇskߴiSdiK֢tL~&NȮ]hѢwqMDėi xƏOҥX"AAAHͩWAAAϬ_={믿?랫I&<L0Zjѭ[7Xh%JL29j(T&M_~4i҄={Rti֭[ǖ-[YfMu֌;!CNpp0Ŋ1C oa̙ytԉnDbccׯFgaٲep=syΝ˱cLjI&SO=ETTգk׮nlٲ.i5޵Ϙ14k֌5jP@8@LL wi! ;vs΄_|A% ȴHŊ3&** VZa{jjZժUg+W:tdZuk[Y瞳jժe(Pʛ7U|yK.ִiӬߗ'ZիWO4l0ԩSY^2-[jҤU@XbV.];vX=zG9[IJ,wIϓZ$3k,YfV"E@ozamܸ1q/^x VZVppUpaiӦ֜9s=1c0+o޼V #GZϟdYYqk_~5dVZVHHh+Wݻe˖ߟ^7W˺SV^'fɒ%VƍXbV33bY5`LĈ eB'jt*VH>}5kq\ʲ,N.^xݪL>A1}n""1 ""8zhmW\ᩧȕ_ri.]i{DD;h 5ƍiٲ%J"))e˖q!:v>[}ҦM*UĥKXv-~-+VL!""[T\ul޼EqqOժU6l "QժUUV@1b/B OG\q51 gϞ%226mJ@@Yi&ZnMB(VݺuY{a Drț7/eʔSN;p]vXb*T6mڰyf^7رc|\rp,wMINNfܹL6{ҴiS;۷S^=vɸqXr%&MʴXѣGiڴ)?1}/^yݻ7.ZDDDDăMǏJTTJ}={dժU߿ f*UѣGfʺu "##yٷo_zqrhٲ%sɥ+\>rdN'ݺuK/> lѢ ,H߶zjlˆ#[|,X-[fh)T]veŤx)@~.^wߝi_ڵ|2`  Ү];)T;vdϞ=w89/^<ӾŋcY'NnK_x׏_|_MuVJ.͉',+swޙa߱cX|9+V$88՗*""""… :tmRdIx%/@nDZ-Z0a„5k֤f͚LɍA聁tؑΞ=ȑ#ѵkm= `ٲeαc9BÆ ӷˏ?̙3ЩS'֋O˖-c޼y,^0¼y7o.\ ::ӡC ,X@)UO?t)«Xr%s̡SNTPC3PD ڷoϢEXl:tDEEx sa>w\Ν/_UrJFI UV;6Xz"E0~x>c *?ѣ)Zhq%Kog$''ӤIV\]w{ """")|9x`[._}U)99|\+""n*@DD'V6{S/""DDD|R5oEDTO3&_߷("oi_8?ixED'r}8_'5լRt bc?eP;Q""">B7mϸo`1ZJ'"KDD|kAReP$)."V*@DD|/. ӧÚ5Q""">נlY2$c5_ݗMDߩk#P /'S""">%: c燙3a}""Tؾ> ^z ԩQQ0z4j<AhP 948w.WTOز̓_@1~}6w򉈈DDD|Bt4A~7Uo&""P"""^o&XZ?m|"" ""L ƣy#xuI4 j}èQf,ǭ*W&O9s̗ gϞ%226mJ@@Li&ZnMB(Vݺu=Ν;ɗ/lܸ1D @hh(!!!4i҄X\a(V zv9{^᧟\w^رc|\rpGvMINNfܹL6{ҴiS;SRR4h8LtZ"..?nzիWbEDt)Bq'O6  s34V{+r ?ԩSʗ/O-X`AٷoFbҤIj ,Zj@""b(_K^&0ry݇:u"bٳg3{ N5jDҥpu""g XƌpgGڵa83EDDnO,[sqb̛7Ltt4קC;wP"""+5|ռ9hawuapiDD< X nNr}o8M""= ""RS!*ʌx\_ѢfjX84""Mxa6oHӪ<$ vٝFDsbZ?ڶ;Mν&TpiDD< 8sΝ@96o7ް;gR"""%];h47~}x%x5ذ4""Gxٳakڋ/B:fz N#"YTHNW_{Ns` 8|{4""ExY`>nHS:mY4""Cx+WLGx xZIӈx ""f΄t_ O5BDDDx˗ͬQݻw۝Ƶʗ L5iDDDDDl7}=jIrG~kcڝFD^*@DDV.? ={BZvLy_ ew"[M~ ?\R"""xѴ~<򈙶upiDDDDDl3u* IwdIRRN#"~*@DD.o@߾p]vq…͠5kL1""oT-LDxe_fS⋰miDDK0z4DDwڝU)z/_;8~^z$ɟ,Ns'DG۝FD}T[;oBvW:eZ֭;{4 N4"#AYӈ> ""6g10x0T`w3f##N#"TL`^;gRƎ5cc4""KC_ wawC۶f$ӈ ""Ǜw$ဏ>aPӈ ""NqSrNʕɓa%"TH{=t F;z~;\k>P)cw0yYp`,ӈ UɚbF/ӦٝFDĵTH9~ >:n4塇̘g}N#":*@DD$׌k=IرZDiE*@DD$W=j|I ;w 1'$ED| o v'nMF͛N#"rTLÇCv~QQP&g*f*@DDz ੧N慙3`_;ȭQ"""./@bvk믛W;S""".5zYDoiӈ ""2?SEڝӧñc&"K̛oc|$rex]#X4""7NxG2pԬٞU;Qf{|GMDq|YpaC2Ĭ""M rD4ocΝ |M/?%Tx70ǰav'})jՂ6o8#G{S|Qm$2RxC`4xY(T4ti3f3EP"'!a'0 /"4·;,N 7P"'99|\+L$"׳?|1<.jw?D qƌ$, HO "G#'oan#M6]cܲ^n$PDD+vɞ 8|ѧ@ w䮻:QFG 4o<иqYyT(Sƌz\93gp}%k1?vLkfROu@#[JHʖA˰v-,YK)BiShڷj4wzUnHظ_7q۝HD$3Gr:oV޼Т ;w0~yRf IfХKyu;w⋷T\N3= vLxc >:tpy+U2cE,$ghJ0-#&}m_ waZųp."iTY o.^#8؜D2spaZFW6S[^{YDͶm0w7i$+իf+N#" 8N'{HƊp~}IVukW#hX ;\O@˖IӈAx+W/\F.lSͬZ6n UN-(MKxBfOD<ʚ5日Sl)6I_h 3/oVG9""@xtS$Y+Yy,kA߷q#,\oүi}1Ӓ+"b7 QN3#Ud^MZQ|_TTbp_=Ճ|wW7*mMWXA->w7[c]}oay@0jYSKhy@tEYEDD<ƒ%/ue{b͚,dA7jT{۝DnVǎEv$"b=t ڝ*U2Сfe8%0yYIE ];s=Jdv41oǛyf6x{|Q7C;II{ Nզץ-خfnS,]jLf4kg 'iҤ7@*;w&7T$DEV=N"DRRzz/{lgI6m % ,ݴi[PB+Vnݺq ٳCRV- (@_>ӧO򜉉 0PBBBhҤ L嗐l>p.Xۿ0m$2r}!>m3*J`ȷg|9v~!W\!<<ÑݻwӼys;w.ӦMc޽4mڔcW-W__ӻwo>s,YB:u4hZs^tVZmC=իs½ jՕYv =hw5$!a;c5 \3z/{l+r ?ԩS5o<ڷoOpp0ԯ_:sq^yJ*O?~5kХKʔ)?ϦM2V͚5)T bҤIуѣGɓٷo_ƚD*@nNÆP:Ѫi,Y6W_ß8dT8y&":Xp8,adÇӏ۸qպuk+$$*RյkW*}vi6n- nzwޱ;:tt1tYysӈYu<|iD v ~* -r#̓!V^E%ӈ/S"ntEU]'"v͛N"$%mۚ?DzS/N""L AhI|ǃmi0ܘ>3Z?$M͚KݰD$7tKOXܬ;оI|_Hy=c/f _7Okղ;xʕ~}uHLLq^Lލp:MrN""`~;x>W^;x=@3gN"b#f7FU*@$%'òe~N͚V4]hfN#ޤGxID앐hhw Dr]|<8ĝo_إKv}!Z?UiQB͆%m̘H^Tx= , :_9s!6O}͚T9uY [Ы|'BCCYS pvz*@$W9м9*dwT6sI~N'G~,fn4nKEܴň ڝD>gφrDGowDr͙3r_٭_?ذv;䶬?c_8jeX3n]x? k \U+?< /rsz4Ǐ۝DfFςNTHq:LLaav'o 3gBS~ӭyY$"gY-[ڝ7\jg4]w]ܜn3c(ݻ_U 7oiX ˷]o/rz4P4X3}ٝ7\tqM؝D3}<6ފ$okO {1c" '>kW~2ID+6<+P$A ~N"ig5/۹3˗?cQ#wՉ5:2`@ jwDr fj0㡸O?Mv'U MŒv0v,Ԫon,- 7tiӈ侭[!)I+D\nR3OCٝDa'i&~xT|H 7%"6av S".xUIZ=zAtIy,קI/>nX?bc|N;TK]`k+TtlaY*^_ gO> yڝFAϞ7v']WU~j*@ĥLsEDeIU&N4cٝDE.fyN"6n4q- RN'TdV@Ԧ *eZA;'Ð!fkw(ZڶբP!W$EeC dAA#0k$'۝Fn0|I gw ͚iYWS".mEW/"/N""% >*V;Νܹ͠sN";.]5kE  2N''Y \`<5w 5E *@eN3@y>ô,\NٝFneoCpvի|-:dw׋5N}I| qG͓u>j5wZƒ"vW7,MqqU>-nIJelvv'*WZlXjX3۝DYBоa9w-@ZhA˖-Jw=ǎٻ󸨪aIM%5 $D3\2~fY=fKDV3*Z *r!4aΝsg&9s.]<Ӊ'|]_}NrsED!; i؈funU9T;Bur8z(_ĕ+Wiii.;zprr^/^im۶x'1|̞=3gĈ#0sL/0uT7O=`ٲeHNNF\\:t@L)kٲ%"""wއ}YŧXիNBӉ7ƑkL+*J,NBT> X3I-YȠF;pN<ի޻k^.??z,oݺu 6۶mrssZP7n,>a4iҤ[[[+y -7WlHW@NXE4i"; Q[ӰH] hNvQmr78q""""ZOFNNZhQ͛ԩSy& )){1T^ǒzM%ε;wYY,@'Dez۶xN42&0__." ѣ|4k\]](Ji\prr*uK ==׼q-^/>jLv)$Ξĺ̟x{su ~@jPH\`._vmȚoÆ pqq܌7ժU+q,88 /o_ i׀@Xz50e4A1 :?_dڵݺNCZoQQQ*q,##ij RV 111;C:nnnENN*TP\oo׽5oܻ裏ЦM{2q9ʕŦ' ),XxzMKOe0_mݲ>>x ڶm[鬟j X??#FxhРʜΕ'|PqQo߾ . -- nԼy^@sNq`Qc@?'~ǤAA9_@4exoccO/Ŀ~ F$ ~a,>h4bmK *T+J\cŊtx'N@||||^>>>xLz{wK(Q׮@ZlF7?$DӪа!W"n2Yn[nEVV_@>n:@>}Hx{{o߾4iF0aB\\\0uTL6 ѣߏH >Mn[?^ç~cpwwǒ%Ko' N:=ؽsIH-lmW^S(>5@H(#1c6"(ȧ}!$@^gwH˗)S(>>>JFN:)ӦMSNSt:bccS={{JJU* PΜ9S5-Z4nXqttT<==H%??y/^T^}UMqrrR:ưP5kPIHMņ Xs^Q[v";rDټYvҪICQ j{ftr]$;w:uꄔhx"=:u`Ϟ=U&UԔ&^~UJtmE47&|p7o={_}%; ) %6'cV3Yt4h|_m,j{@&O EQp!:t۶máCpawޑѪ[r+*ې!Mb=h9DRƍb/"sz8pٶmfΜYjG֭͛[%%ӆ={}>6]HXII,@\y ؍\ 1tR^{ oD"11&Mœ9s0l0btN{ZXZvpn?|D(@23md'!|8rӯ@X`,XP#F sbme'!5sro V{efN``IԡIE )aӐĈO ?FiiipssCnШQ#^]e`R`.WvJI=ΙNCAAYbge!kg4?.; 3K.aؼys&M ,, SNEXXN<"--MbRuӯ~t,،~o|"\$Ddewi$2)jO?ő#GpspQ,^،ɴ<рNoll(ڵl$k׀?xREv"uihFL"*O 8PU1|p}f=FM61vbe'!K1dxӲebz[oNBNoOf$d͌FOj EUɓ'niKah@+z :qVY>7r1Q#;&29hQCvTV>:4푗gDrp zpC۷.N. L ; zկx{sSB*_F#_ 5kرczޱcc! б$di[[ *JvP`@˖[`hD~]vF.]=TUtK,A~~]g}~^ ܣLbcBNGL pxm` 7dT>FhJ.PU Ǐܹsn?wy8qǏz;:WBBÇDIa|1߸GI'E`U&; Qg-Z%Km۶Wǁ( >3hBrZyBsW/zu3ܹȕ,|5NCd Q$QQ "; NU# 0|pƢgϞ8rGwވz$d_V dZADg MN":y8 j:vM6ڵk8<Ο?W|||ddz:O?q=e'yQ+2"??<4ai sgIv,@jaB];Im7/^, #d'!<+0ʕe'۩!:MNBN 6hs9Lπ9a 7NB֠ SQfY2_#jNb~˗׮WDj|})!Fb"DXh\b"_~ES.秽iX…b%'rĴ4I@ bgR ׋y5 SvٰA;a$Dm@1:QT2-I!.,@4Nze'!k2pf$(ybMi,EjX(;9JXhXj*w/_9;iX";M۵ ؿ:O/],':Ѱ[śgQHp ;I?ѣ0@,iX 1ż];I,,@4l&}{1Mdjݺ5kZ3ùT.~p=,QNBeaQ7o۷s[[$oT~V~<KY@1Iܼ ݰѨXQ *O!!mKz 9Z ٷOGbQzU+IȚ5o.Ǭu֧vvoNBd}\]=)!=8pqZ)d!!IL Q &(UΝ, r< >Wd@A5~(ƍze\Nv7n-8JXh^/6Mv҂]Z4`` ^=iWj@@}AgфtucAzdbEIH+BBSd'1O"rA$~$d F 7 +`qUVNb彽e'!~='V[Vv=DcoGX999DQdy4{qq 2*U޽9 ݵk,@4Fթ#; iMHݲ<ƍ>}d'!Ҏ@NBj+>de~,@4ز$OObԑҽr^yb,2dpؘҤ˗o)>%"  tDwJK+KDCzg3(Өca}T-,Lv"mG,ft*KL?,  )ӯH`V%I_v6x1 T.; 6U$OrKD#6oĎD2~(IߪUi[Pfs$6G?, .]zD2O1-e5B`aCiworePIǏÒрL_Zɲ;^|ʍsr;⫟Xh?7o!xY2:u:v131Q|MY5<rDvIYX" Vlt^]HX*:%Ev!7XHLQCv"nr\ KFxiIAbzػzuIn@XFvaE|NDeo հ` T,; =( V*'GN"RE^RBsωsHŊuNBVX?, +s',CHp7۶= e=II,@, +׋ߦMe'!wݻ=Z%7b8,հ`w;v +(W@vvv/|ɐ sCdInNBd4:*NB:vL2+$Cr^=яBDKb7%Ԏ| &ӯ, +׋d'!-[-ZY1cx1CD4XV4&;tv%cbzGK ~0~X+4ԼKDw$dP-; =, V&- سӯ2Y|,] ŵ,U@͚\ K+ WCٶM A?$DfMgON774cisܼ %`&@۶@Z=!Cӧnӯ  ؽ[v*Ot,@H^+d/ B^] SEDcGvmNòvPЪ$(XX={|v d*VS)V*ߍEl<اU~CDac#FA֭ db0U>mme'GĊbI6=1+.D Y@ 6Vv*_Y> *U]vŞ={JiӦaÆpttDժUѽ{wܹԹyyy'*Tz /.׋Osm_,/Pn6ϟ/t) "A঄iѻt˧/`ڵ(֭ޱkٳgW_?UV!;;=zK;j(̞=cƌ;п;|A°a0}tڢ]vرc.]Tǣ޽(Yπ[` 66G@n0,kl߾޽;>c"&&+V,>Oa۶mx7tRtڵkK/%K0i$|'h"L4dSRě*N"kס'@˖@nED(V۾]vzX.ǎq]kSEm۶HHH@6m/F= ..fH$ь9ŋb$bZJ#"ujLz$0^4ƒ_4?bzY|6 l`[][)BDb n<j@-  uXكӯH+|}l5֬ƍ/@f&m$0 B@N ҖQx;w P0|x""jhB,MY6[# J47| os_>1Zz6d9FY.@" }&VT <<^JL;<وHEٖ-Ѓ0֭~gd]XXG~EZ$'%&?XP|Y:5liM -Ú@z=Ph%Қ=5݌b EDl,FFI~MN,@,^DZcgX*?̛'6lHRz$t?FVl7@ևt طӯHBBŽo !7."Rzooe) }{Yv*,@,֭krsԲxҷ͟/n 0."R ш~$t/bӯ  ׋O#(iN'FA6n229~0+QibI^Rd 55cbAno+"@s5`yHx4,3GGcGI bs5 D@Z@u႘[QYmJpr  kDB*~mwíۉJ4HLٰAv< 7[v,@,^/vvXQv"uϷ?#wu;Qi5k\ Kmnve'ddH= (wDDe ~ |Yv*?Zl.H=ڷ.u;Q  pz @ժ@ֲPycb6mZ+w0wn4 @᭣Р̝!/հ`Ml9Q|`V~Et'wwwEcFW? qqpwwT.(Hlzw$ WZa';޽@z: cyc05 X9RvmMl@^s=l"""S^]0iX NӦ9Q9^4;w.Nmmfw 9ӯKy:Iu >ZD6o=d'!""N,7%'6Vl7D;X^tT";  ;p;';6@@ÆQLWDDD;;NÒ`:$d.,@T꧟rt,@W^^*<<;Aϡi> }Yt!NRz=Ф Р$DDDҥKE0'Oرxqcrs'(Ck8BDDDky8}zEsl1W^8 0ćOʲa8;;c%ƾ}LEvGUj k_ 4_"553g… ;wFxx8̙ڵkT-][%u7}ڴihO,o>>0-ych۶DAS ""iiiHJJٳgtT\EQEQJ{hǏ/5)h֬Yy="""@͚~kdyxw,@nԩDGGcpttԬY:uΝ;]| tСXu֕x+Vv%%"""*IM~ӧE OJNNƍ '''$&&bhܸ1f̘Qy>,"""`\t }]yzB=k׮AŽ;ft:s?M"""R eˀE?HXD`lmgL# ؾ};ѽ{w| ELL *VX܎; < ǧĹׯǐ!Cwؿ?[6] $6&78tHvg4UNBjSjj )؄NDDQ99@΢$!r+ caYӔ_{t!"""R hE`9v tXLÆ_B/d?t$;  """" >,;Ϳ3;ZhJaBDDDR O=A ;I!"""R  .o~#G+WA=$t) ~!"""R `H୷h bAI!""" Mү_yy/p? DDDDB`Zyu8de """" Q% QAM~!""" /(F@ƌQG?tNB) z0,@,XA\ `XYF`29F hLebBDDDd#D?ѣ|A~ewBDDDd>H{L`>NȂaaٵ gBk `j૯P}!z,@/Ço $&C+"J|-A\+z8,@DQ?_o?/,@!""""M|y<ԫxzɺ!"""2/ &AL}XYE Mr(jMwM DDDDVbE`Z2z#J5i|r%bii0֪e!"""bC$'?A~}II~= DDDDVY22.]L<YA eKzuS&#aBDDD!!@h( ?4Xi']?YУcBDDD!*~?ƌ -[nH#Xid |9whR|cBDDDA *A SbBDDDQƍw8p" 2  DDDDUw^ `otlldXiXӦ䫯U>`:v=* DDDD7t#dH1v@L /#Y# DDDD%K'(rp ?tXQq?[os`e#b';CfbeaRq\ٳ)ȑ^;7ccBDDDDʕ_Dl,sbŊx!..E=N""""b&Cf,> 21W^8 ,@X|1wkpv⟑;ܺ!"""bvvZxvo`]nwv͝ &Pxh!84h0sF GVQ1wwwE#"b.g"?vvh sr ^zt,@www,_>Ov REDDDDDf̆ """""2 DDDDDd6,@lXٰ!"""""aBDDDDDf̆ """""2 DDDDDd6,@lXٰ!"""""a >>R ѵkWٳFF`cc =//D SOaZTTפ4&5)Gi|MJkR_25 /`ڵ(֭{M6 7ntR5 gƘ1cccǎ|P>OĊ_i|MJkR_zפ4&5!S@iӦ6m 6Dxx8vU>Xx1֬Y=99_}f͚ &|}}3gbȑpqq)'FDDDDBٽ{7vZ\|@Ŋ={… %οy&BCCo]ve^sƍP%";;۶m3!""""/@JE%%%83(JLJJB5PFǛ7o@i`yyyj8/,,D\\ --Ç1o[o񁝝`0еkW)ZlWˎ"Err2ѢE |pssC||\s#)X =z@XX]  ** ;v5k4I֭[U*Krrrq֧O899ɌgV ,ѫW/{-q7@=z4U"-- ׯʕ+鋎ҥK˗/-|}}%kС_>ڵk*U̙3?>Ο?Jv<)w}bԩP }ԩׯ:w,;47nĕ+W4=ǏG~񈌌DӦMѻwo.997n7ٳgqƘ1cxI$(;VY訴jJK:OOOE):N)Ϟ=+;Yx ngcc#;˖-S:uꤸ(ﯬYFv4:t,;fRZlT\YUjԨ 8P9phRegg+&MR֭+ʔ)S7oʎ&UϞ=ggg%33Svnݪ+UTQ핺u*aaaJjjhR͏ aP(F #@ '455eʛ7o&ё_Kw^;wh N===ٿzzzdɒy׮]ˆ $Κ5khtҔJ$ɡCݝ/_q030{-[,+W}>|8ٱcG^x477(JS_,epBFGGs$S/C9Ԥ-O>͢E>Kۗ|u2 ciۼys***&~Ƣ=}}}9uT200]v۷?8ydחǏO766P8iI]]]^z5[ rtttI466f۶m?ŋ!I~iO %Xar$Ν;'oٲ%.f7i{]FFF2)$I'WTTd޼y>f/i}v|uM.I~/'iѣGS[[NN[[[oݺmsIjmlBzy>~O>M*7nܘ^KKKٓ3gΤ2k׮`Ν;s˗/O?>7nLyyyTVV1P,|;&}UUUMKKK;L eee?{ʕ455ҥK̊+ۛ[vٻwo=z/ĉI/_f…}ώ1ٳgYjUz{{{. Gܽ{7O<իS]]tvvT*?UUU3]"3,R7o+cccϦMrY_ P@ @a0P(F #@ @a0P(F #@ @a0W* IENDB`ipyparallel-7.1.0/docs/source/tutorial/figs/two_digit_counts.pdf000066400000000000000000002220251412533144600251450ustar00rootroot00000000000000%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x+TT(TH-JN-()MQ( ( NU5Tp endstream endobj 5 0 obj 50 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 600 600] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 600 /Height 600 /ColorSpace 9 0 R /Interpolate true /SMask 10 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xxTU^I!!J;(EWD Hq.REEED@pwDWV"Q7'Νɝ›#رc]AN>Tt~OF{e1c ٳgt֭ׯP‰'sӧҥK,Y"Ӳ`XVP8f >… ҢƄ л[$qu/X J h3}tX;Øn6XTaXV`DC pW_*y[nFHS^?t \">&kGw%Ť G,"?AVwРAhQ={VZ + @40aÆrɠ→ByAjC/O*UH2"kV3Q|I)ʪU<=׿`=zHZj`dT59zTgXV`t e|P@M)qQD46?cCg}V?^sNDxW`뮻S ѣ" /_^M.n qT#YV`X~1}EaXZh˷~+Q\+,B 2UcΛ7#$8ҏSDP.xV`XV@UAr<@ >K}hKG7>PLD 7܀@RZf Jݻ7~=`v(VX& cw|Sʙ3?#Js=P-rQ=+ ADaOzd&Hyt tbp5٥)_Yf#!B1FƒND щ 䓮]uerΜ9`ѷ|L{%'Y0vNeaLtf/ ф,VB4>D|pH?u}}#<BԜZ1Nw Q{ ڶkN+7T=y7ĉBuĮ8~pߝV+7T=y48ĎU5kք禛n2&Uu]X{t?!w,ʕ+uJҨ |u$pCڵvlg6 UN$ZCr株*NHٶmљ5?om`DTnMJ17F$ؔFV^_hb_J|{/F3/Lo%ZCr_ gNzI& 8iA8 ãEu4={|%ұcGl0hpr|.R'ot m[t_˘ǪU5j{O^Z1n<Oېȧ;r_Crtt4NRdQk8 (=^dggc>L5܈–S|„ ǎ1u*K "/G$Wޞ[<8Zb-[cʰg>9<،so>ϒH0X Ŷ]R毾kn!Hl|!d;ǖl'1A^(^)VGz =Þ; D+V RFerr2zS SVn]Azb14Kus˗@Jdל~ET,FXf͚5ڢ8p rC큣RDd t.a<(PLcE a3f -XD-k1zp**[u̙3QQ}"2F'RU5f(,DAZq*6C2vq8~H{)Ŀ;<-~S9!PgIӪ9 )&S 5dBܤq4[o!Db2!NUG0 kςݧqn~ڴi2?uӑ ߈_~YD0+#" %/''DZ()<%J~SP,2|⛅ @Q -~:H |Eh5$$I׭[ʢ2ȗu6rCBm%!w}'bb{Dxk ;`DPFc}6dJͣC4PFdUբ_3ijTiw%5v G̙3i'OK`֭Wa(0јS822ѾL 8D֟oqIfUhZF-[ʬ, znrշ/.E 9Z"ϳi%ahə%v8Owy'AH44 M7P ɘ:]H <~ Zg(芐:t@*--># ӺQekyܦ\MmXaѕ?Ht"^Zp8Lm@taBa ˇ@o.sćjHN|a h?T;CT#袄g ' Ka'OT*Lߵe c­,.}N`j N#ZCr`&\(|}б֚1:DR K%LeJJ *:4LCaįnB-vwihdf,єKsYܳ ]PiӦM2gA"WͿԕK4%ɋA|: tKLK IŽziǥ4+rsye9X sR#Yn4Ѫ]>" bFXv.O BdS28 /Aˆ+P0… awWoxY7 #;w7A=`xv,23j(DSElJ4Q(GN'nXC$Pd{V݉&Bϒv2AHŜώBb97h]R4ꍿ VDn^rSd*Ak1I\I1OFŠ;G'H7OFPDA{A68(:<1,4i":͛Un$zAlj/tĜ79Qc .D]x3>&]uAn?>"x~݌k.]+5A7D%f6+/./ tya^Z{A/o^jg|㮌HBr]Y"|Q]Lq)H(6?!j|̟ 7t,Őq sQddÏ%],Y"1D,J?~Pa1-Zoj|b<EyDL0w *[=8hə* 4밞+1 K*i oy/Zgɨ&FwuƼzđ9u7^11Lkz 1E|͛èN@u b_T68hzh[G49QdiC*AhƀtP^EQ;/T4! W Bsꨊ@TE Q%(¨4R~4  jޠ %ĥ u RჁ.q1 -YV3\h cd<xxуqa~7 eFݶa=h*2W7+%CəguQ_KSwʼnd5jhbGn2))М0  ĺcƥ+ j,ճ}v=JP˩=h`l#ZuOFM.YCP!-fMd`1* }5nGכ_ :ѵ|M57>,CO'>cu'( @Dq7S IPʸ'N@qW Jq5UhYBʘYWW'IODØxP=X(½a/浂 ?62w~2M$4޶inGzhb;BWjRD9V1<eGe{:~үzD\炃>|{9X_4ߞ `#vU#TAj~T@720sm:9DzJ:+m!9s?N``f]b0&2a,ɸ10å\ [` F1vtMc(A6+#lT 14l|&VyIaA*bP=zȭ8[Pr3F!E$z*YV`J8lbəm=+ WeD|pH,`XV4( 8U3K{`XV(8tiəU+ WCr`ix|+ U@p.Cr`q+ AA6XV`!9ss| p(̔)SM=՛6m|aH͢g)q>MwJ#86G  l}?z8 'M}$)>}zYWp.!9sP Gp*pR=3pNY68ݻ/p+8aſ9_p$yq ~ E][c8TGB^z O@Þ={Rsp)NX+9AΝ;Qq j,~&իW/ZTڃ;mRO/oJ}hoSqX!,8IZʞGp)C>8$*{^TA|] LϢ1(+KG"~ׁ1CʢqT4ڿCXdh|pH,͚5kڴz'G1ؤˌ}'±A8c͚5x"lZ^8c p믿C8,1[!C|7ēypW|q2S]^:&!q1ppŪQ`/3OnZ8$gz#xE1]M\VE`SYm/iiiӦM1oh1̙3;Bpz8Y_~ ˒%Kp ˧X#-e#8>8$gzcbq .-D//W]Lz}ʏb4O<#/]TZhOjհU}_xGyD51D9X>qv؁͛=tQ/^(-W3bnޕ ,ĺH?*U .xoũC6,ef'x4T, 4re.c{ ;^}-[=z(93蜩]|',NpE9ȷVXUKOOnj 쫦 c3T5@Kx4#`}O_뮻 Uby:fTC]vXËcN0ɓWS_!bΑYV`J8K3K `XV(;!Gvl@LGI&㏺,\E=7o4bDGG7o޼,щ+ % ?Crkw:#*NXuVs ֪U }a,XAtcك>bF Ō&ޢ'ڵ ;)#`XVN>8$qyu?biׯ|)=:>`Qӕ1NDp0ۗRe+ +`[At2ؾCr z,oテ4sY (U#YV`X w;?䓠+"3q;ɝ*T0p@D ]U#KwM)k?Uqƽ8&9zQ@`rE`G4)n,Q ?9&李}\!87~yؖPU/srr?آ [8ToUC@^+ %PWr0Y xu .r-z?Pn]S| 栔 x8}$jm#U(^c0u{ DVcuE DqvU& \c O{֍"Ds5/H:c!9z'^<2 4@G}&Qbf#/*Vc,Yl۽Ir[pU״ I)n 9gIb|`Gؖi+ʚHlrgPhOq+BFX@ y^@+&>ݝc5:9d!5؂t ˮs옃x 0Q]1Y X=Cr#=) rG==ޔ1C"CSWF?5_%#jdఐĺZqu9xz8ME)2ꗣMa[֌Pb8QJ$L) 5Ӻh XLAqɊ ;b7[뙄Ka\/& J\vke!צjbΧ$PB)fBqݖ2wPVMT;jОh#s܂.DFu%m[f]YUHnDÉPETYu(/VҞ߂h| }c˩ôk[v ȧ$nCh9޴0IJ]wh)UjDKϻЏ`u΁>A޿jGWqFg`x c:iqQ]\ )Oh0v5_QP8%O_о|jcʨHECӅ@\P{{kheԩzLF<p_WG rA9ZgVۀ2(#ex":"Zr)mNY\b$ŅKݴs0 Hmt sЦq0.V2%(6\T &S&G:Z) QR]ڀ Qd8w>jSKH#Ď͋Rg"in}M+OmD "CEoުERrA ְwAJ͚5kڴG׭[`h F ^ 8OAQ8(4L9.kTmGB<}/ Iu8}wHψof"bJiJB4(HOhKѵ(>܅z7+Qjڥ_8:b(r\2.?C 9cQ%QzE'`"eb+)GRԃ S~Ql]DaDK(?ZQK%ܨY*REA:XdV[XOQgF)5vKi],2HxBΚjW޵kWa;.^X5zfԅkG:Tq MC^7%O56iBM W z_,+i%gކڭÓZ 0IYyjꂼ}Z{]"ㅛ4 GZY-ۃ*PS̵SZPZ u(14 nPZ_ACBRSѩQ1lܨoF6=Q,`OmD_%Hi[4 1J8S.2P CMuh#MQ.S5I_Ӭ5µ۴º{5OK&}Ϻ7f[WfTKG@{kEiyALe9jo9Wxĉ8LP:9'Vb@1I]LY4O.qDՎ`jԹ >]\0/&VOܵ<#ݍ򌍾ΌNMkM5[1k4UWn*k JcDfˊze8w_=ⷴZjoGQB8"J_4ZՐNTE#>6KxDj=3(6dbN|=a PjƙY ^&!)\ +TnE2 AD\k,up5"&pQYKFNꕩk~S?Ϡ0a7sf%nV]]";e}1`Qi$0X{Q_#~;_QǾCr*G{L6iD}(V\Üruӣ+ b}R MI[:P4J \Z0N[s[6ʗru,KKfaDK τɻL~:.2PKF"&X6GcA{<v?E#zST$m[Eؒ "5m(*ƼJȧԦ̈́GЏALg90b3պWcǎ3ΓZ#x/ĥq'ESj/ڸKӯE]x~Q̖Im3T5/S.GZD13Ճ~&؋C#E\Oatx 4Ёck$$jrP~_Q5HUOIb,ˢ_OdAs ]jkry/LyiDh.\A3qKhV)R-@:f5q E{z ghENQǏ!g}Α UkĿcn޼Y>oΝ333/^(-xıdpNQա@S).ln8_ئaqy2"0' >b >Q)&\3'sy,rqk&. T` ?P1OTk,1iMO<7ÌPwd7n 袲O[saAH.y2 B V28n=O(.| ڀ:91\h0(|,Eę̓z(Ղ4#Z -]lTQPqdwAֿX0 wi}T;?1#44Һ |Hӯ(76_~Q.z)#hń8}(µZV̄[..usw)6٬I1tq ~F#$7 ѳ8eDʔKX8Hۯau!n5X|qxfD^Hvc:A4/"2 ed=F$= ,XJC #.]$- H7DRB=G2 `uVZU.l~kheVM|t oߌW'V-@q;Lo.ۙ3u:JiGLDd?%':E>ק=s UKOOjjPF= 'kD+6԰>A`  oׯKzY Z\z4mLwv,L^Ǫǟ]PqfPpP=^Dk~Ĝh%Z~׏GNKEg̹\-*9hiAXGbkFJ^A`Z%3͜ۃmq4lk90u?_(d2]QLQL؃ۃ.?_(d2%Hc{vsХ A s9X98Gîs4gs9d2JWA s9X9wVf2Vsb W]d s9,!؈ޮsa^uh+ :'{-uN{9*nI u(y:w-w $05Lj$] *e׮s1uA#2DY X@Be(+Dq J'ۑ}htBs9X<2`dj$eapޮst`tKAlv$g΁}u7l)d2b?9؉;G'`▿9d2U~Ob'3s([1A栿اObMz`▿9d2U~wmA s9/9¾st`Ild2*'Hу9X6xUROd2b?9dFw^"Hȗ9d2U~Ob3sl𪤞9d2>5r/9w"Hȗ9d2U~O -csl𪤞9d2>5?sVruGYDS~eJ '4ئYFԧ:}mG8y/wQ1t:2ONnRN4--Gs)2bcI:>No4_R~WyP5v[I[}ի! ]6T/8fj%FDŽVi6|5_E_⧍oI{.B5)"<$=5vJ?EP/Al&3žs `Y!V<7bJ4VykႹOFR\8ͤ}hkBtrKRw1$*1Dy^c5UZ]FPpصS]?|tmZ鵒ucQZ6}QmL*9υ89ޭ9kkEE?:j:l'R<7Fog#E2YY*kqRb嵤1C۟w ./ћ[ٴy8*qJwBcꦏ*su#ŃLKSTI=_ ƇZ^z1x\8qF}Nsnvt- 7oBU:#Ď;O7Iw Yy:G7s}фG nذڵkZ_tiڵ###ccc4irsέ\rDDDnn#<">ݻwbb"Ң_/KpPRDϡф6 sPRo^8x;% U=k{5qo+S=:N|uJA]䍟CTKؒJ)ʘ8Z  qOqpٽBwXO ) SQi#)8gODgax:*Ft̐ >:=+KGE;橃x:wIR@uJOvP8:Nx3eʔM6n;w9MЂ D .p آEov9=|\d Bg̘|}bŊuyo޺u2{J Zy(*&s^*b$GbΣg8(SN9hdoz,nd ROz?5@q;'QksyGp0tgtsw㔻˓k`,jߪD*,-NnByl 1Yd ST Np0)+n*j䄌zUѯewt*S=Y>%KkiyAl.z}ɄzM6YYY¾lٲÇrQo2tرhT2m4kq/8PgORG~:R# ш7c(F )dkT^ɗ3z,اYp whgu HN*VP=莃i{!D剂2}뎀|:_z&S| V.=,ZcKz`(2)S)l϶0""4<:tҾ9HV&ܰSݍ2~օhdIO't1+SrJ*۷wWWو'd²aaVZ׮]e(<ƍ(R<0Hn.Ȼf<>e~;(.-rT#TS.-8Ve?odd)(Pd-#zh" 1O{ x5 A]zY2 Y-B)FkmƴEcA$齼9fp࢓"ƿqu>ni%(2BZtre9Fŋϟ?O?=c+V@͌vl,_|hhhZ~iYi4(--M^s$?{,ZӧOW#\O5(]Њu+Da^Lbq>݂֚ZɡԥU/}jMDeZ³ ⁢"5oD2.TU!e`I=,rA%"r&`Mݚ u:Z^9hmaM꫏V:{ y AQ9OLΙfD3 x`jY44h+۷oСn:^j֬:~x 2h*x!RJE1[Rr3>m>ըBoAj篍R3štIbgc{-Fӻ?:`X:3}pmMHҙPI&:gyQej1?=qiaaTc %"Xs0(ᇫ=f,jLJOnL T)3?u}M>c1_4ze5'kv1܆=ȵrA4P޽{ԩ3gjf̜?.+͛WTI\2,e#1O&lL24OF@+&;w~#L-p 0+74at , ZBQ}eٺe^r08x? U͹C{>-A/ x "9ej1rm@8a`trĝ[0K iڲGPw ,%==G#D>,t奟9_fۃ{^-|~Pom9:t5 h?\/I%uiub1rJgˈ-?OW@U8{3]7tv^ַk r]h,)[{b!.z@W0 h =[&߄K^t7߾s`VaX!F EoȐ!CPDt,Pq3a@0FD$$oĥX71x`q6`xpX!=X2/`e(&@c]%ֺQx" Γ\%V!ʝ@L.lduwYnRJ(.8AU7kudJVȈа;Kyd dMx2# ~++|Xϋ`D7"քE 3ecAX$(Kzr^։ 3-kYAЛ`d4cyMzZPH0V[O Axi]ՍQXY!Sa9,** AGә[ܪDwhDƩVrlg9n1ĉgΜm۶ÇG]#'yb7zmݺ.^VcƌXq/CO-Z8ۮ];3w ` bkj(V[aY W C;j._~GaIO?s9X,z(A o9&KO%wr`2GRn:wX,%8lA19xQSo9d-J?2ΛbeRS3'o#'v1K5.1A/Aи̾s`6E/{m7p`2AOǟaw;0WFA s9_~r+q'sK s9|AҾsLg^.\2A/A6}2xt9d2>5չ*d2*'qcc6K+ Mtgz'9Btެcj; \HGt7(k@8t u{p'We~d7Y(+Dq $QV!(şC';Ub>.s9X,2 JuUOwyAkP s96991BKg;ǽAkP s9,R.$α9xA ssn19p2A`ib|;3vY??s9dv-~5P s9,\Bέc sj'3Ai1)A s9X9;2n2`2|վs,gZQØA ssp9wu;w4hPvvvXXXbbb.]oԂ9d2K;qc%sX_^z޽۶mk۶-{n{; Jʦ0L]h~)p\5(a)uw.L62BPXhhblRF/IY66au؃BCRj2cM;vi:RjvLHXptbX.ޘɋsZ@ΎV3VH Naq]g_'S%A{k-CM=*wNfPra!QmK-T;=$2,,6"IVS:ЪAMAaH](wQ "-`8z_hPeWPJ.hCES\|Z)"b"yMzy<>;{Ӡʎ`J.i}IB144%TS'z|.jrP_ۡ|znj̈́/4r-C*EƘklC3:$ {\XX,eׁL{>c(yK4eku 0*O[P/#v:C%MH|-( ԘJXAXk91uUz9dȐ_v88gLZ}5MTC5.kuc97Daq9gN}hԜtON68CbRR9«gOhV>B5TWѿ^ az"u0nqX%)$$|2|뀚?2ş4Z04E Q}!Ưm՞ܦQ1TBCT5J꫷sjL.AA&rRB mUHh#I_!tFG>l>-k<©o5-Ɇ.P$ ".g栮˫YVZe ;7D40 삼4]]9b6%fJc?CC総RZ5A`R-:NY_.(<тPA ?X٧Fh>$+44yӵ ]LS IF*"ڈa ` ppH kF0)4DYds}xۃƺ̝;\hcrJk^l0GX7Ku_U9ثDEgaT OPT!5?q0k`Wm-:5<_v׆&RAT1g1&U d1F^"Adn|QW~m"TX~j- nV'rU?aǼP\D =^4ּJLD]B[.鳴_*`\ZPI&A [6 $‡"-A$ЈNM%鰥DܪvEkM"}ڴТh3v.r+>r`L@6yϟIl?W*vZTDSPE&, ms- R#t:YB*,EyًFZi)X[aGRkn-<]Eud[4{wXS/!olU>~ eqY~<`ڢ[, ($9>apg8*, .Be΄|6Bը=0BŹ2OM CPu so£Тw|,Ȫs#< &Z1~ ]_EaČF|.qT=9wPZg϶xbpP@"y͓6'S%bJywٯA0`N@+&>x$߂Y2H@ŝ')Y4"SVCMyS[K ,?j4$nwU6R`P56rqP@|s̡/44*D*ࠀa.C/nL̂.73ɱe~+ŅӨZDrrg9aZ@,-YڵC*guyp&Vg ŦMaS$5-8٘ ɱ<wDvUmBDn a z4iR~Oc%_ހEݵ'ɀe婞Ҕe:#L=Jg7^b!bb$-*L2i01Å!CAc..:j)Ƙ^DƬEPQZG75mXwOT.Ţ-97[XDG&Crp=9f93urdB{.K\NWw,rkկϤsƂuh"'$MޢV5+ՔVҸǩuth!\Pzj|6)n";|R-U]]whuq0w4Mj+'}kQ}<65NopE$gВzxuuV}E42b>>Xa6*۵ECOT=tZΗ/7vfkm:ծ=OR葄24[UZס01F:yg"z3c,'HӬ2q]%tC{)h6 %Pn&RDZtpyD4}cc Z8> ص H9A:i_?zM ޏUn ?9G;栮K 6:|\\p}:Rx}հiqtwƥ2v9d@:' , n 8f2X2`(/ZNQ52"KDԴaVҔh҈i0Aگ!dՃhsɘ> %q=ۆUN ҠS^󗷍S-B6>^,2Uӳ Ą]^+L=d4^ÅꂋR( tvkWiMrB/ iTOdݓ hZ ߅$bdݡjXdgn$c#sХ ^qp2=,XK$T`<:N UBoJ38Uҗ,PZqCDοwMA]ϗ. 0ǒ.O-"0SK)9\_"SzVM9>.>_`2Uy ߔV%gq/M8eM1CD@O; A]ϗ. 0٧F RJy94t3]#Å2sР+\`2Uy ߔV%gq/.ᢐX%nes9}jO-gsIO?sPL|/3MeT9T1ѯ*0~SZQǩdʺSmܾslcBAS#}j)%<ӜK|uq!_%U栵W{(s9bΣ_%T`*9S%} 991eCAS#}j)%<ӜK|Up!_';&}>s5+VԨQ#""";;{޼yϟW#8qb)))ȼyoRs9bΣ_%T`*9S%} 9U;&ܳgϔ)S6mt;w9.X@'|׶m;vlݺv'Owخ޻w>Ŏ}Ν;$YYY6lصkW^@[ļ.z'g&>^rg_HGy88pHsT] DYҽ`p [+.r~o9vpxWmڴ˗?}ꫯçO..O:9~x},~²j*Pg .ԪUiӦ2>{JA#2v ! A`= A`@E"Sp=QQQ&LPΝ;끹Ç},tرf͚2E!±cT#KA`H/(5Crwx"O?=cha駟Yk֬Q;@~BgϪRSS ",*T8pꫯ"ɛo_J`2jӿsк&{oӅ;$wqO }`͛7Ǐq̘1UCGkK.ꚓ.c8R/KA s9X~=ǩ["׺[~רw=uԐ9sZf6A ss;iݵo 1e诿& /8d2йwH {9t]b'F Oױy2X-(s@L<L:ud:OiJ9c2Rҙ_l;$+(!DhͥM̘1C`M(1E̱QA bV_)Od2 :}.ضCr#'N8sm۶߿_/ \!Q:z<#)j uXu6 J:zݻ7f۷OF`OR9d2K9 9㏷h")) c]vU+gԍX`Z*U˫žjʕCj8d2a?_f`i~.A s9X9#GgmH_4s9dr<匶퐜9Xat9d2K9w9wvH)E3A`)NGqgmxpᑕI "2SB#bVY;Y=} 6%vҧeϟTCu~χ.M;B5)1B(<*5kRM~NAA4竢 7':Y˔ߒ2+PXEGQڴsR$-4"M~SJ%SD&Qp45EѪ-,Ja)ҋ}Xjv5Ųl*R)z+ tiE:RAYW ҋֲ .EpQY4&77/{d>sϽ3ͭSϘM&'b̚g!>mT3td/WlqyO:gI7f?c>kQuCKL>?O<(u1W!(8RÿiY/!To2aI !n&2c rE|%ԫ2<BʔK#Gwmrx!f@+'WC_pU>_U9?nOHEζ!-NNiz`\ ЪiuvXj.o%Htg0:qjN֙y 8<~+nSR0&ٞ[VH$'Zk$:w['V(q@0 >;ظ0̌Xwz"GQ1b̙QJOU ^tRuz1{y_yf⻒u%9%`p[n4Ѭ{ry&gL T[^1äCsEB+yu&nhya+7$ک$!eEC^E-g|)UCr |FCxa"`Jʜ_*6 gBN #:.PҔCzֆ1w8EGPj^zpM4s=ڽ/[ zg4F--pe>8(-xZ6i১봠@7ʜ1ۍ=b̢_ =Ϛh T]~,kUCʙ^aH߅ z(f [+^(&3nb0 Z%47 8cMWQWdmH_ ~F/]>",,L¸D,8Vk]ֈb3}=+,9^~Bϋә 6rpezJ#S17P ) 5d3~Af<%ݿ?jIK]?JSn+yFk ?)(aܒ'(.InN<:lL_^Č|rk r˽&Z;=uMQC8Xiޢb:'V"zz_ʹ Or RQ:^^l2ƴUftdnsxO5IETI.7^lZX{F*4@Ǵ'j۬ 쫟$\JQspb1a]<#φ '@fJ QCF|cu[8)S.9q]zmศ{;CᬫP),7CaLF0CĺJJǡUod<WQ.PJظp{>)nx)/hCwMQ*ucqUzϾ[1_%OyEoTweW|q0nl39cŌEF9-Z.gg3FE])N0_ɆkMt$:Sb\w-E;ֈwa CZö|W['L z4b rkm n >-k^n:17z1a?3b=-X/^<1N5N l藩첷΀e6n/􆈙E#|&7aU̕%e?}bP'Y "q]ovHibI<ϖg y0j"XT9-T\pPBTnӅbDw-E-ܴ3ɂהc7w"$|ۂpGH_{\!fFr *78}bT'va/\/1b>r!;}*֩s%T4IH 46 X\јvP; ўvm Yn\!f ]'6xWK4*yXT]%~+̉4G[A9ЎKf)0W߅ח ۇI^`QQ$r\!f _xtVknR/DB+$ pz0pT9{UQbU~4u(Z߹pNQϩvU,}喏~!RcJ˹STj*;)&$N\ &}K[AYEXJ_`&Qܣ);o#|]KNG//8=n/|Rb\_'hxVS8LI@Y{/3ꃶ:l;W5t*yAaͭ1(,2PTTa\iÛb_|]O81ΧRs*Nj#ͫb `߄LWgVÔzSwVoκNJ(?5c_F}b}]Jv-{ 85|ɮyqgB|n}=X>uǒV/{T(8jcٌX 9-mA8#Lٯ$L_?&(14s7iEAUkQCc\)%B9Asc0.*Pr E_ AspʩIܜ mZ0.ʇ`I kƙ3*e+c}K( iʝã]x niN]rTzD҇_ZYz](%' hoZXD % K N rr7W \귯Iބ 2Qw Z4jközn KG3g  qqH8-([3- zK.]x|w oO=T||888(j91f|ZۂpTe;sLP3uT qqqqok qPCjx'O.QF:ˏ950222qdku[8-8Vڌ3\8Yk5u[8iW8888X9Z{ۂp.Nmܸ5Cq8_ik qPCjx(L UhxevZ-/Z1jH /u…w"(>^ `Bk<ጃR(AAA2֜lm3K;bddd,MpA ͢rss>͛7ܹ֭sᇒ_ڵk1 addd,km'[ጃҤI8+##Aa[OTTY+ɍ`bpp0Nc gddd,c}mA8㠼8pOO{.33GKEi߾}Ν Y[M#=My<FSݮ1+LҥEok@cb vK"]Ng Q~_]}n KxdO xYpYK#}#*j d>H Lɟ9:=jd;yTG4ʀ7udKW<9ۆ%Q&}9X3\Cʥ>bm%)ֽ'V 0MኘE(a/%-dQ׎ Q(ʥ2So|1PY/ _STsJby 8 q38,)+WĐR_il ;~kY]i/=`vi2H Tk4.p 杭/*%ݦ $"ͯ6ć} t,eLGf;5}6o21T+@?&f/+(AX$RIjYsT8|9ld g uE)$9iAq- !mm5GFw-XU*zܘ : @9 hYm3WPO~ƍa)W x%IiԨoԄ#7Uhۚ}% :26_ltE);㈲sPȬ-s6 Dg î˷_c^W8 ^|5Є7 EgI jQ9[mt5at_|u%ǟ%SLcKTW6n_Qɾ՚ \*΀%T k6w7[6 ĊrN)Zo`9ӥ8,qt3xo[9yA;9K^]E\immAxݻwJJd CB҄"X1rH/> tLPȬY߿/ߣŨ_:$2O^0`={>C (gRoߎW_})=\knzbUUM_w 0NO[,"PL]yȗvuV9X;Tp "3~{T I(W('%U0Wk+5V" & M!~ȣ!NaaEK6_I)lEEFB ך9tUEb_MIK0zc2Őr ^UT88t 7{8fkr`@J1Sx`UO+`13JzՏ0Lj?6K(F~m7jkmmAX_&_ưۮ].@<|Q}饗 /*v͞=[O6 x ܹSJenc LNjH?[nEGGO0FѧO>K~q^bтZCl|ROp)gN&@K,ՌjƣM@ ;V1{`7mThQԑ1?-[D@(NEA_k` @!EKC']d./Xlؽϰg~tt)=x;g(Ta-R>)u=D?1fwk9Vc`9+QT%u(Op0np@iB\QV7QE>) LQ:)ԿiB,[.5*Dm*Y8A/ss!(DNqSo歷u[.r%$$hBlĉ^Xfsh*d3gx9޲rJ9sOo+WR!?S OrS_.]ąpĸ&]?e`Z -_=^R-'&9`vRR`'!o*]u?.C(rBЮW7b<"%3ӿ0ObNɓg˹KA ECq{t|DJb[U#Kh>$ $Q%6fo_WKyu9\bPkk.F\< D)$v/_{e3~>2 ԰?8v1rA^lہE." +׿ h'XbEqQ X/62ݣO`E :F}}}/UXm8!yiЇs|\t ?BT$UH%th2?UЪ獥$?/KZ&f%2_gFEE ~ 60KPn~0gJE!$6?Oe'0Eӭ :XK>+~Y DmzvR7  xr/^(o.>?/;Qh(8+P)I[B]&$oєF}bsm4[u(n8T_+6 S[-Q] )hK>[l8sɨeg F.$NR'tl}5^4`7& ;"[gaPlbTI[(:&vCҧ7?> ޥ8^478q$X[H?yC6DRɓ'믿~񖘘^zwFF;{.]ن,V^|ul _':hkG5 :wز+ץOBt"Fg*^(=tQkILeWMV A֎VՙαB!b/;+S׮MPH\sk+% &RA=7]*eՅ&Ϊ%ZȉРSř"$ {<&6FUa*ź}k,];`nt;ap*[ۂp4(_f_>P'%|'1.*) ۽(jذa NB!۶m Yw bUGy2[ỵEr~H5M0R+Fɾ.S*'aLQFOTP$+p[9ppϝ-oyH˫l5˙As[41΀UhOCG(hz:_.BlM2DR@GW GʑypwHr^%F0k-_Y?AKQ7^sa{sy Ex<İe*Ť[B)*ﴘ}6f^8HeB0AK`1bi0:ovsG>Pȋ@u,p91v Y-Q1z;[1"9232:F>aiQG280Y,[$U?B.&-5J֐W\lmRC9A*4V ŤЄBDx󃌃ƒ {x8hXuIE8‹Q>5GσRNf&!yxnM)tPR0:3xL_WNun}z yY3`9`tԷ1Er(Ŭ#KUn4,\ T :IJ&^'c䯺ܦv6~J^D Igrf"oS'z`;ķcKU1X}R.^9Mԩ3웊QS3^R}I:\7Oc+i4;7982`An$k'Μ9Юb=w?R2`9(N۷("!ƲqQ){>zy2@!wZ=W w%'YC юG_tL] @fo]#&2raeGt {ԞG_q[$tmQD2*G5^"PO83;ыϓA_`ϻ_̜{j8])I'\jaw *Rta%m6mA"?lŋEk ٺv ;&P<b|Q8K J"o%'?Ep4YvAi ة-\iTԐq%)k*$%knmC9h"M6wZQlESIrpHrlYe+?~֭%$]OA)O[T WIaO^S$\RCo{mA8`i0cq%Z4IAA1\Ꞧ|ONL W?"ρORi;6mA8`i0c1l2,]$!IAAgsfyUxiG <Ԑ#}Ƹ-gTA>[/E_ˠA4fAA1\Ꞧ|ONL W?"ρORf}yn 57M˙W,5 AAgsfyUxiG <ԐCϲw[8!5\^wމ'88(ƜKԓ ة!JR'T9IJ֔3mܖ!V *u6'88} rNyڢ,OJ/ x"rIkn g9{_~Ա}]y@E)% / MySlqM!e#=@=a86mAx/+<A ҋpAe:oBuyvAԢZM= ql^ CqP[P27|5Ttጃeddw9Ŝ[`ʷd qP9,n\uDLጃKIIO%.1&8888X9uӶ30;䣏>]ëbddd,k>6mA8㠆fQܱcGPP8888X98rm:qPCjx 68ZMyKl4d&c(t+[ɨ7꽭Os[8˚wʕp055k֬YPPp% 5% aqq[3emۂpA\fIII|/qqv\8 TEn ՟8888X{X-v[8_J:l8vxFDk׮3h AAA2֧ۖ-g/`FO_СCF# kxGe]O?i[ qPCjx={~-88xܹgΜ53^FI8ϤI>>nשE~~n|sK|DK%u!Y*>0ݹ᏷򭒢3`wY2"ݛD:k:O4((LlOUdBg^Q1:#Q ޣ"b@vg{W JrZ$u/>h+g;H$)Wu 5Nid%5sxe/P \r6[s!JmEm͌M ?>z(DHd" O5*lwQz=Rr]=Io$ʞBCoTd:Y' p[8!5Y~NB_vo~Ţe禃}gwh4tl-˫tqʕ"' oqI. }d'#1w3Z׻,B vˆpXzD/Q#-88>[Ev_$Oth Q9; yPXSR̞G` ~f4sMnRWՙ/j}, A,s0w}}<ݢZ@-WPs5A?W)$H` pc =^bȭDjځl! 'Ȑ_ aڕLh)u=N їR:zV-gԐ, m۶e˖EEEU>>>^F95Ek+UyfY@13.sŽs~oE zDmmBo$d0sc=HK(E $Iz7A:jmAI6$EO9 E+NBm08 coB &ZK[ԍ(>XTQB+q砯a(5u;Qzs+z_(M9:qጃREaիWw>~WJar3&$8(^3q`XrI,U>];oL_4,ؿPM;'ADT>Yt|*ER>}f԰WODMh ŠZ}bKOdjljH-'ܚVFi疓 s[rjyvV;;ӈ-&V3 zP[4S7z&;l7R\TG CǭInAR|j|IMn &n~n[,驒Qj)Zv !Zg\LTmyU5>["b)eO{V[(0;HWķWG8L?ZጃK˯h%᠘bбBFVSHg.X!cI^AS;=DE\3"0% $U%-"'s=.++\L=:VPXu|sJjB&jAI+dSI|,}^(D VPTu.3: .p%N U*I1PuvbZTzJX2 D`חȏ@޴{VW[J%2A)Q#ݰ{F\q评'GX%/p0l_zጃ  =~Kv itqѸX|E$tGug'!i$DF URI2'Tw[ vWUr} =~j-$suIϸ˨1/$,g13,qZMɒ ]78:wTWlU IC8D~xwOv! u2<^]0XߐYۂ?jEaը`QRT'we;Է׮#S 5~0Z8:5ጃFڵ?…bK8]'ӢBR̲{Py<}4v*J\Osa[_!bl;ToQъLtR+<.iaYzUZs&nv>+.@4D" İ@R"u9T,ô |oN.!hNufጃBߥ``͛?ވvAXN_6}lnào{Y/'Q,+}k9)Ưf$8?1TWn .p%Qn3_ w(&._QQ45k9|aK?>[Ò W 쾼wTFf)a#5 ] ?0ҠrtFĞ$4C }IC9aqb0oA|{>Ǝ!}Hh|/YStqBIQqnn 57JQŏ^2JAc^i}vl=e Pf=;xqM&Cv8UGRU gdD~^{U R] T}CmSÍZ[BwOiX 3tO1=1v Fk5כ>nāc`LF8¥,΅]Y*Plo"rB*z521zǭ|J4+pBU1C‹q0QQW\0؏0Zc| EQȮa6GWIU\L |3vʷHoKC nMzC|eM5dw䃣4pBC5c._1`wA,32<r=E!X~?ѶHFMqP ST^ ʲ7Eu99&*o'/j?5 +Sd,) s(,Xo (JF?VΩ;̩f 0aD`On>ēٿ| qnEf EQF7*氍Ĝ T ձ5'1D驛}\|g23P+qÌrw`S `b'h+d+'ѱ9""LEݨՙIAIo"K(%ul"$`uNgn YPCjUJ@Vʤ \T0j0222yʶmg}qPCjx(a\uݿIs2222qֶ.mn{mA8 ≒5=ҩo`&/}(R^5_ ^ ['ފ9MaTe88bd㡓 +yB&>q8̶k ՟sY222*tI^ z sƶn ՟sY3>>@͛׌2 =~)O)ysY -yweN1{}*ԱqP5֕ m3??8gۺl$b[KyU<ԘcooJE՛26rޖa)=}xVc; \~%n-[FDDɕŋW\bׯ_ǎX,_<##RRRf͚e }~~> CC^?JQ/᡼׌qPCPz`.㠦&Q!AԢ!ZW׷v[. g> @&n9L1c9w#o yfϞ.ɴiӎ=pBpΝ;UTILLܺu߹sgɄB'8!0z3b}r 0αs0..EB{gשS'ނC ̙,;w\e9sOoZ֭[Wϔ2GE58eAo*[ֱu[QAtt"ݻ@&o׿ h'Њ+Ν }2]N,uٶn +VPM0Pb|ǎɓ'jׯoիػ{n/]z>mL)b\THJzzz^^aÆ3:u l۶Mldzyb~ikt%:4ĿqyA,9qÇ |}}wŷیe_Z] V9(!fuCqڵkafu֘"-۷FeSuAAAA15`*?aN:FLInu2X 7ZR\'4ecdddԊ}r4`uS%Gn x07vX15k[~' gA@8طo_>}^n>zl>z,2dŻԅ}X#-X9#,71<AAAA'.GC&Yf>q[d MP.] 3=_vv5k$pAflgUC999\5)k: 㠘_Zr0Ѻ-ۂpE֘]۴i/X*9K2222j>q9r0T_3z1e-[`o߾8+6l ,g.<Ԧ@2)5f>x?T"Y(" Zr䯱(&:m(0&K3 gK-*d0NWJBM #R#ifG7_gQT %*Dq/x{E >N6 %2>fFN&CUb^pQ`Iwʷ!Pқ($Δ_)4HGI]KxZq2ݠ!f:jn"dS-S )gy}Bb4 ԄCz] )2KrZビۤ[B}&CpjXgy),d)<'](Fxt6>zS#g>8lzp|d$)ANT,7j7Sm_- 76\r 1b][&AO}ċa<X# h. yl T5v’l!ٻi - l3ePޔ^撃[]]Dܮ :s0ERs0|6B"%tORZҗho5J8%OvB:UKwc'j2W :OS`.I*$Z}Rn ;A(` v!_738c:8˕K5(>θp>Xiڱ8yq9 xWܖ٨.o^I'n9w$\w$(b̚gTt-=u}F߬N<1Ն 2dϵ(;|<6'p2U-zws7‡篛40W׬Jir0VۂpAuz1˗/`I8xe tQX;${ pzJ93JpMmWǟ:PyA^Y@TnYA,f_їTa+D3E6%A96Y)Ir2Sjt ꂶRs0 o24VUv9!@(Er 7<9Hk6,U40 W}CGT{rk3nWɬά'L2FԓgCgPUyrXj, \yt Ͼ&& QEhInKu:W 7++h4FEE-!7oޔoI8!P|/oV@ A\ *E Pe_΋!PqCWC@= y`j0 ܄^v`' @QN98.ET"]rE9aq ~AFCEZ F Gbs"(X?REYpaUG=x9vRϩ*!P2D"(#<c8, A=`QT$'1b#O\Qޯ`=4@h(; -'"u֏%^xeN>6l(pYuno U 9߅Q{# /fy8hF4wfq!Qa()ŢFl ~CYwHCp3ʌbTl.gjF G,$` ŢS] L(ż<"%@I2FP}Ez#EQ49(2oPEC)f!A҅pyCi}[7nLUBF#EF|{Qve|XCc7-f !d:CTX7ɣ§DI^ET rR}#:+̊_T@աuߜgȞ9V8!\Qq-5 Яe³ s,9!/RYr0Nn %-$qQ.իalc9ب*u琭Q ;_ вatz)A԰ ]8Xmb*fy8bK!4b-?MO C̈!_3Ѳtz a3B +(\n5\b1 2dr _F/Gv%OH'Ieї,!b=~Z s%4,O`Vn0p@ .`SO }Culח< CiZmbzmη-cN[*e!%:_}H`ɧ7DmX]3qJ%vyRU14#k4Y־MWt0& j>cmt }#3`O0b/(D?uY(B e7HHTt{`s8,3eֈ|Rz TuWn K%BCʔ'P^=PO8W~EX-Z$߳Ch[=>fGf1Kq&4K9Ǭ, 9,ȩQ0-~wt[vX s\e @޾,#jzA -Q?P.Q%$U͗::9LSTuҿХ7 zaA+{yGUGYpq A;Gf\Ӯ?sg𵉍\{-?KXKFrE*Ŝ21’xh`%40F-+6*hCQNlVwATV";-(!ҬIJ3a%`p8{%x_m :*΀>cJ&!DѰ?f}7vmA8㠸 Ν;' `Yx`RbO~ίseѓE#qzA-9ːEvW+s]Y(fIS\]0:p^9"Ey`+Iq< ?t!AOsqx6Oݑ_R`d1#C9,B!Q?}(Ubv-?89v\P }`Pl芴EW[En5j%KSŜvب STbaw 4ZԦGá(F>;RV EEB+q2=#&a ,WIB"9-}ECZD.-g7IrJXPJA6rŃVNi]ˑYb,wal ]Αs2tâ1ω8,8,)%]K,P Pz@ٰCbE ;My.>S$fQ\}U 8rdLQWΙ@q΁H .vXRs0p~Zb~-_YL:CXA*HtEZ F ~iľ-(EU1eJ3l [亄V$f}NA$Ŏ"w}Py  ߿u[8(nPzT8uɒ%NرcG( MIqpH΂6 &0+rpA1F,H(Fre C/ohs%;¹qQg@]Y WAbP?^ fаP,:*V갑ɯQt9}kSFB4d(S$&qX]eދRJtY0 ă/p1bVN c`Z9%t=-cӕfl$@u􈨚qEQK`~ԛXGP W(^9#xES ^AII5x_dbb"UKNNƻݻ'p*,@Ed(cBa=NڀK,)IRޞRp߁[*%hn;aNBbC;@3?.H1.߷I8%Ov` DZҋN1|$Fl9"XԘ܁@h1n<;rQ!=#` >tѥ+t࠘~Ǩn;aZG DHTE b 4bC?Y:rt)( u2-ԊQ%a B@8 {۶12 MdzMI, N0wth2M:.zNб8ʜlJ[TtETR 86ɒv,߱"2~BlDVݏ>IӰfr]B+I\TEO^fy6žSL.~ñlFRr0߹-g,)~%wir/J␗M#X" \t A UX{dJUI~h߽B8dxGlq.ʙ&0P5 NkGVq`v+oG5nI!O/ጃf'PBJ6KT\΀! l)4Fv^ QvhQT SQQ]C<3JZ~?Ai8XzKZ<;2=M=yep-9xP/?-'ŭ>ӥOqqPaTw6 %甧-bTyTאۂp6,-zBgÂ=M=yep 9h:rrۂpAQT`dddK+]C5]?ጃҦEOqqqqP+ѐ~ qP3UJא' qP 0222j>q9r}J 㠘_Zr4];8(iYRb-9,] >Ӌ?AAAA1ҵtp-?KA*>\cڕˆ#+͛b{*E6/KۇޔnM2ڼ,(=} 73}#ҫsAe:oBuyDuX-9 ,%~ERAmA8(nzaJ_3.=AAAqS[̹,q)RzJl 0rOqqqqP>Yzۭ}A8 #'8888XzKВ۬}A8s 0 㠜bhl02ԩ>AAASO^l%}A8ddddS9?8\> `'/AKnYptł?qqqqPN[+/g#sս3222zV:is_8d? 㠜bhuV:ns_8G末{gddd,=%hV:js_8d8X5H HjNPzWOW|D~s܋*/Af2_4ɷY.V ^Rr=$?S1X|,?:˟\'/GHgg"PRstN,h*Y󀏢nM!@B/R#-4E"@ ySB3궋:CKBa:<|)!7%ER熔Y4gpp9yrmKA^~r=A W 7>! O*'pJw֑fw]cdzc(U%?/f]#tjFᡔKqKAQ#p "g.'ׅ$vOԚƘzCIY3DD +4t(u$if5Spi O*Oy'=]SjˮG|WOdw^P#zr0 A9/_)A׭єUJ>4ü g )= >7P;!T|;ᴃB]C>&jGw JoIste lI ؝?F~uPiyxJE<ؚmY+pSUQ ƐV+xUMNҦtTWؿ i܀N>)~!,s6Q=־.MiAǻi5ui*{j^$y39TO{ 4|;>%||{S< %I+O Z\f}HKݧ޳ > w7:[&m J:>p‚YGK8XiuAA?CkSPR*;>+_.x$c'^]eYꪄ>@6:\[Յ{3KYևHK ~$n,b(ܺXxT(TaZZAL۷dGĜʒGa0>z)p| Zr e?#OokaQ*[y+tt!sUy[ERW_z{FfWV{0`aZiYګDmvj 3IiI}H{ uwuGbv%1͆LJIq < Ù\ s>XS.9|o01Cvf Q?GysBIqpr|cߴW.SP$dX5*9<6ƻSKp!7tV[9|t/9x# P><.iitSe{:GX'us %Y.2ǿ9StV8@?U{)+dy+-Pޛ#FԫZ>,j5F15oZ:ynB &bp\#oQ8l2nG£r< Ù\ 3K|J3d.֩$,8XHfm+[?JAAQy+&J)DX$a R?EOShpjB!CEa9XH93۵ ̇`xa61fof0Sva 1X52*?Zy7) PuHm+kuzN]qTv-s`PfAɤ=Jȭ0jHC"Q`b&邼rwyce3>zp&_6U?ƍתU0|LN*=Yf5o<44#!!aԨQ /NMMtӦMs<য়~ׯ:iРl*?Zuǘ=W~ 1vq?naL"_J E 6Dwmq7WRl0TX c= 2SorEA y0|zaKYi1FWd|Ji'F*)Vz <%ڏ*;v>~RXZ+wgVkm0Wع&LʨmaKZcɝC~H* ['NlVU mgrp58^\v2 D 4h˖-G]lXZ7oJ1ӧOh4'O>tмy@!CH ߱cGRJ{`1sd<|I5ǘfE|Ͼ=;wfyIqQN vLTYeI@"Y 2t[KQh1EG0npSejsnt;cqB.)< n&& j7 J9rlՙr6B=VPW=jNYԞ1"/f#pptmQe~htC]3O^ï=T_W:L 8(=*;v fb+46l̙3/R?yx_Փ(U '\ZwTh0Qf}I[)ğWA99ܘO~!Tt"Q5WA+n-]{Y8PnX8 Xn$ 8b.▊m/YD[5)OH"]xncPj2eW? o!8W3ņT})_*cOQWSo0Vk `D!D`N{IN! lAw>[ؒXU?^X5-jդ>iNgrp6cmsʕ+ ї.]Bٳ*N"x@Cѓ&@+{R=p0w{fpU'~Q#7 KhJ^@[& 3XHVz~L5fpUoܠvi s{,"7 36F,mJK{ӁgDZ5 [ɛ mMn0p:,㧥* ZQӕCjՙJRݰDfIUQʘU0e Y)᝹>#*ߚ-5T NY=$ߪq95ojiX*$}Ӭ2{Dҹu7'퇤Mn=a3aX-fS]֪ΒG4isC:(b,=ipy8vZP&L7^zݻkŌr߾}r'ۥD{ Jwc[\=%e/^5h+MVCaBAD).W۟_igdS2IU+EWX֫41F )<B}W3-b̆]^ 씿u r{%_^[$r?4QP*՚@ف ԨR{WB[!uUMk|`Ƨaĝ 1rSVW+6"KzuU}fb2X{4ӈ߫M0iV*c,!Z9+ק*j[\%3ka{EЙ  /_ȐژRĸt(+WmOOϼ\Q+T Vi:grzV;t& 5j úx0 Q|)s:Y+CׁOѳ398MO P6P1bLڵ1_⸨|(jEBqQbf29c*\,[NPuT=fp"cO?U<Ʊu2X #Ffgg[]'44(d2398EO 4G RF|ľJ ^|b}M=A s9,|VO/hn>vʯY֭l*P[.7Ɯ;wО1cGe3Cjoݺ%N:aÇ6JA s9X98YO/hnIII hq,;*w^5`V|U96lؐ߫&A s9X98QO 44_ 0A`ix=-38^М9xSd2xAs`YF s9,|ZO 4g/Ɣ2A`i=hp9s,~]cP2崡B ~Kh+r$ ACFIo(bXJ%\hʓNO9xQ>t:"K8WIB*at% A$!s0q hD9,Ds.{")sйdpzzxAs}Z s9,|LOc 4gW?~<;;;((399y7nPd2=Aţ|~:.##c֭G1ch{ s9,쮧Q 3q8i$77˗/K;zhx^*y`Y)*zύ:RuI]z,MU:DF%SFA+|"%> }(;ӝCi|+RW5]B/vjѤs'_OjLo*[ AT9HI!ɔ9f07]H䮣hjG/fR߫MO# 4gʟ~@]ɓ'kk׮IE`QH5J-R cs `WOͨ.jw,ub bѹe$enSft skv񒻟ҠFͶ BUeĠjѽo.Bzn:mBFĀ9#vl3#4LM[Vw+{U-D1ᛞvTpJqވDs^!faS" w?'em%:J4x &؁>Dd (ը{|O 6}k%G͈ 1۠m9dVܢCU^ܗ:tNS=(l;V~N:AӶ£4p@Æ"yHciOH[14@-td&|v v; A=r= 꿕F63V-ht,i){*8B]^!PCsЉ좧<͙~ϟ?ѫWK.aZpaaa#FPP$V$<#;\6hJzJ"aTeP_͉^*_M{xj^OFg_Qd})XmaZN˞rdtux_(y$cBU)u$POqH2u3z<P:)4~Z貌nO ]5zAUpJq{R4$r!Hxv\  Z--?UU-԰, PC.:<[=7O[sVL198fn.x<1j̔ճMl!ki.cFKϘ#\JWEJDv͙G9<}tRRBPH|w3ڪN&7-=z!7䙫_#cb "~ z q=RWeD35i~[gVtJjL>/»vJ]ibшty𼿆ν=dҹ^5UD0ByV$CzD$Zh"Dcz39x(%n0&!!#I: kznN s4"6_4^uwK=q]?OAԲm$L Ma;J1+LL`JB9iOҥiÅoD-&ޗx& x'I/jg/ ӂwo5!v0{(n_¯y`#Uz`p9sP/m۶}t7nDbw^  kB1ͥ%ڜ&$cna\ Bh:&8i_cﶞ$ ~ĢA+~I'ssAzE`&Qp!I) xL ~O}U݃ܣpQ'Gr̀BA4b -#:b:n-!zBnP>d}6>Jڂk =# ZĖOSDjgi37"<^fXY$7)>} PxAs_n씔={\dYqQ@P.mZڋ3XH]A@0$\S;684 XE 2ᏜgCzo^pX䏊*šRj 2vGFqqc`CyO>}d'5 P^{A|h5Xcz(4&m M`P +@#XBh_=hBPhƖ"}! NZ;iI긘PTXG:p*"׋HxЉ#zcp9sP/7vVVVLLL~~t֭o.y`1m;6w 6 {]1.ڴOTay#PY7JBgƵM s{V{GI \vYiPpJq610Þ,ݐ$d̾980ַJNQrF!Hڔ^0pBn>"HF/5 ;2U5x6eO y !ipb9hx'6GHD"7oHUJU0Os/f2kaBzm)ajDX#7:7~)L>\ilհ-.-ќ^ƻWLNe-& E*E\Ju.{cr?˓iAFӬY3?|ܹ#HK3=+ +`o;[Sը{*g7=׵ F&XDAj vCJmcDce\bH%4eeѳG$2ϗE?-Ryh /VUlVᬀ|+aR.|]ޭ ,RsQjgQh o{f=S'W=r"9"k넅Z'6SiZTP_ ϽqRJDa*wPv'VAAţe 0 _&8,UN8AL bUf^GpC|*{|zG +/m@ρ疇~P [)!4n*a}uw Jsg MD-& 91N*Cщ*$'af1G?pn tQ!s  M;/m2@{Tu[<~\m(88Ĵ e6_A4ڸUOm= S~8g3>zxqEj:oZ$DDeo)`+P*ӂ<&o~o9p.6”>/-z`KŴR=(BiAf3a4t>Sr:cnN k4TsJƍUc!NE1t8Rڽ047|gk/AmiAWl&~2]WQՂ¾BLc7'Yzjp9sPgS@8]1@IBo[}#)U[+ psyH łh X>RV]#2+{x/OKk\DbI Nlt #:ըRx𺘔XO+BUv?bRw@ owT[z(+S. zw(DɠIv$bI@Gtň_roM &VpJ}8wWZZwܼmWXMڠ]j`&A1YL |:#}}jE!KjՏѯƒdV/aI 6 5H1ҴT%R=2'ʞ@ (R2+ < b~ " `$QDfꩳAţe cr*\QuN5\C"Ya a!s9@ϦZQj)wj/O,eMxAsO`2ΖS)W{*rVR%p4=@A-YzK}S8 pE8E`C=18^МAmATs9J."T G6`f}2[$4%6F~~;W`\))NQ&9XOO 4|yg[ĆZWOѿxg[5\C"Ya Ha`]=#:ZМ9h\tt(8C[rS_쓟Y+b?%>)$)xAs ζAPsS"V)W{*rVR%pX&9X[O6G 3mSk~\}h X.zK}S8 pE8E`-=518^М9ȴs>u5wUs9J."T ISC͙)5"ӏx0d t@%XJRI(ù.D2`dGKoQ|^]O 4g2l+t:"K8WIB*at% A0!s0q LVS]͙)5A=9\P2? 4=18^9ҦM`|IkŊN*]Ͼ}ׯֿYqm'&&t*U,YD^vR9dZ:iԙ`j/hnoV}vq^{m֬Y:uڿ?W~->h //:thĉf̙R-eK s9t`39XYO/h˗}}}Ga]n[=~8tӧٳgKm _s MA9tPߤ(C 0A`i` 4WxJoKe0z d:tG7tta|||5>x`q="'M,J{6ʊA s9X>9x &$$$yIII 'ؠA244Ur~{հT^vRq*/13.BcC_Ny>a>UdEtX/x /Q-Vu"J%r're~S |Ǐfkpʬ^53|ʱ:u|Jx&,ѧ7;3'hͭeYW: ׼˽aںqTT9ot 5?Q8h+&O#z[ְQDՉ425kTT?ˢRQrgh:m 3 p0qZ(sH)ppjڹ#/ltO Hb8ѻ8%8{m՞!'ޒjSJ}_UfBYG;ߠ ˋNl7cT&Et]!~sh<M;gRI'`n%eBOAҕc`Gt`cv.n^Kw{!w ;6zB5!GžQN詝Қ{xi?aFa1:_m5=ҬH`" 4g"ep0u% k#(Q[dݸ'.z_sQ8c6E! c:Ι>NVNIP#ԛTxv)CD(Fֺ[bgDDkni5~Zl+>[7u|~Fa%3o-q=[7$DJ$Z䟧5WO 5aKpRt$5Be {b*?iHU l]"Ӻ?wRt(5J9;l9%nrcYpFPճ= 8^vlieœp,׳? a`CŸc[[@g X8V}]JԭSrBw{-pVáAczK&%6~GmQ1ڤ٠g}arwb3<wUE nF~99* TH,Cͻ_gwtnAVL<"xm[soӠw{ևS#>΂kk171lQŸd9%`P4`ȣr4v <&8 H/  Jf–JacP4sH٤_"nAά{H/ρGJ%h8z 68^М9Xi;( k`S?+ᗱͦpbB6H0MSlf\#KlbU[gUjQJj+㎬g|?#/ozo A8SUkyTNw_\g[)Dr"7G,>xu2Rze3*' =oZPdyz`)tUMLDE񔁛iOr|Kȇo>3<<@F;=f}G)k_7\pn|Y6& DߤL Ҟ &rV '>g<ݳfȸRL(UMT%RC]*)3{TN6͑AOnqqo ZkťHuw쇎Glʀs̶Gm'䭯 OL#7A_=/htE~E`5ⷬCyvOj U1<'f^spzKQoӺХ.53X: }uMsEus7gbv-m dyfliY+h7,oNQ0fota/Vu=}op9suwPvæ -qb `'l:<.LpqD`0=0g@ؓsMƻZi@RLWف` 8nN 6Mg{'6JV자zh/o,RZ99M?L {bxogۅc՞mJ3&Sz4;AX7=!G\$° ,LV`M) ݽ0IC (6gtI\*=!Scae)pj&wy&y5g|31 ,vF\'ӺjEI =DG.^¯TmA`}Q` ŏͼ!\E)[۱ X|h3ŏaX? vkoA=L "adЙtq8L,xTÝ6p*(98Iw Ӆ"V\?uyhBmYr'gZ-NLwk-6 bpV9vyd;z28^М9Xi;(:%=~i. ä'^<6 MoJG)@nX?IUĵ=-&<.`L"a\Tr6l˪(@7 ZMʖa Iŀwӡ7xa/u 0QAW*ҡM`AyC̘pHcBy+ |PK(ЗvQn1CyU[)Ih⎾=#J/L jI!qPcѥ6>&*PRrFLxAs(R{. ~V zxHQqE(MGM\ +H1]Qa g &zhћD0%I]Re8^& "mf \{4NК4q0AE(Œa\ag7R{mKc km8 l]b"2yX`8ʔAڰHw { y]VAӻ ZMG{d.Bkp:Cg@aLV#zjR*?#{e3eKHo6cӮq(h/Ev9xz5MH{l4GPF.B$Pbҩ՜{N:}d̞qׄ/HP1&'^ jxwhu{kf"ߘ)DaQu^==ܬ߲B+fcXMOhظo&oX-VO 3>\wE`cc$!zjRޔ#oL 6 +Tx+y(F}iɸFa ŖC spYݤiX):t 7ίPȑL[,y޳ux"KڰRtoGDrm Zl@IB#o-ih^jRG(6GH ) ;{I]إSFE8#-?Tss3{řA'=+3N'] >E s{ŀ*%eAyg}l9V)W9js9,Ӕ)3N'] >E s9XQR8lkGzYC}l9V"nx =9jc̼ܰz( d6rkC,h(W_Vh_A`0۷x9ؾՃAOq_DwQМ+2mŽ9x +]lδ9h 9|$ѭb4gU4s9h TQ攫=A5_>x(͙lvA栂t]M=uA ^g1 3 *1~z^5i^x2R_*ԚO<[=Z'*9s8?Y5~?+ 8Y.گ_?#'Hj( 8.b4g_Swx `У3f 9rL/#w q[l3KCD^;={梟7|Sl(r&LLAt+MX"99:Dƍiiiښ5k>]p %<ٳ'mW^XbPPԹΝ^@,u|(`D_9s8-/~'Jw<==Ho&pEh(7oތG%z@5jX(!^{.:G[PSO=%h"\m}W}YӗH|r߿_c+FAs`> TAĭ9$ŢJ+r%T͝;Wܸq# "y?K0lqe˖>ЃUڹJp^?f>?Y/V0` bb6ַ)Z1D> AV+D 2GvY9(lChooo[D>E)J[;gʕ,'6>HpQoBWD4MVPWȧ~ <~z%K(bC,t5?3Xቕb>ɓ'K'zqU8(z߾}RQlqP R)wߕw FwQkuTꁍ"r0<*PzGyL>yܬY~'bQ &MZQ,,EmF^k/7l yzю;"Cܼy>ӧOE.b MMMXz5֩b *mq?czQ,.֋>)8Ҡ+zя?(׈N9GoWsvPBNla'6,|;rJlMya H} #';쳰 i#H<駟ƂX"YйY6vqܑb x*7:`K+Av Ȣjk9s8-N'Asa&":RAV( 9s޽{1l2$V 'K3qUC(htO WTFGGc,~CUW>,FAs`+P~0qp9b4?'C=_YV`J&LͭpUVfϕ+Wb13\a2+K |Q+ T;.h ^CslNŽr56+ @SEDNy +98h D˩'`S`XV<*`"|͕Ċz5K*!%r'۬+ }T6mo /3gϞ-w + GLGv1 3㏑O +&&|/e4ò E322r5fXV( 88ߥpAse>8dVN- 7 + @QJڵ nݺ5Tݻ<+ +PJ0qWk+h nCBB@nKO/`XV@ồ.hnׯ_{&[|ޫ& 6+ +pqp :MyYV`X"*`D$ [x  + /LL͙geXVp\';#.ht-YV`&'ZZ3+ 8O;#.ht-YV`&>Ep9s~ + +`"|g͙%+ RA7{N1 3ϐ +Aǵ㖬+ e_]4|XV( 8Gwe;\М9X>=|Ǭ+ }9u Zҙ9X? |+ GHF:?gϞ- =+ e\"+iq1򧀯`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV`XV(\]8K endstream endobj 8 0 obj 70693 endobj 10 0 obj << /Length 11 0 R /Type /XObject /Subtype /Image /Width 600 /Height 600 /ColorSpace /DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >> stream x1 g ?@a 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0` 0`k` endstream endobj 11 0 obj 1593 endobj 12 0 obj << /Length 13 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xMHaї$T& R+SeL b}wg-E"u.VDNC:DuE^";cT03y|URcE4`λޘvztLUF\)s:k-iYj6|vP4*wd>,y vڴ=S԰79 ڸ@`ӋmvUl5`P=Gj)kP*}6~^/~.~a2 nײ0%f|U 9l7?j`l7"tiNf]?uhgM Zʲ4i[&LY_x {xO$̥߬S]%֧&7g̞>r=g8`候 8rʶ<dWT'<eL~.u"A=9뗚]>313X3-$e}u,gmg664$ыEzL*LZ_j_]Xy[?Xs N/ ]|msϚƫk_WfȸA2)oz-di2|m٣j|5ԥej8ɮeE7[Q|IM%ײxf)|6\ k`Ҳ䍐.> endobj 14 0 obj << /Type /Catalog /Pages 3 0 R /Version /1.4 >> endobj 1 0 obj << /Producer (Mac OS X 10.5.8 Quartz PDFContext) /CreationDate (D:20091119193919Z00'00') /ModDate (D:20091119193919Z00'00') >> endobj xref 0 15 0000000000 65535 f 0000074173 00000 n 0000000164 00000 n 0000074026 00000 n 0000000022 00000 n 0000000146 00000 n 0000000268 00000 n 0000000357 00000 n 0000071248 00000 n 0000073990 00000 n 0000071269 00000 n 0000073054 00000 n 0000073075 00000 n 0000073970 00000 n 0000074109 00000 n trailer << /Size 15 /Root 14 0 R /Info 1 0 R /ID [ <7d8f57750ffa456caaeb46e4a5e6402e> <7d8f57750ffa456caaeb46e4a5e6402e> ] >> startxref 74315 %%EOF ipyparallel-7.1.0/docs/source/tutorial/figs/two_digit_counts.png000066400000000000000000002177131412533144600251700ustar00rootroot00000000000000PNG  IHDRXXfsBIT|d pHYsaa?i IDATxy|LWϝDVC $XcK<֖nJOEV[T-mTkZ+(JBD1$pL|߯}sϽ3wfs(!B :!BF,!BI%B1IB!4& B!$B!И$XB!K!Bc` !BhL,!BI%B1IB!4& B!$B!И$XB!K!Bc` !BhL,!BI%B1IB!4& B!$B!И$XB!K!Bc` !BhL,!BI%B1IB!4& B!$B!И$XB!K!Bc` !BhL,!BI%B1IB!4& B!$B!И$XB!K!Bc` !BhL,!BI%B1IB!4& B!$B!И$XB!K!Bc` !BhL,!BI%B1IB!4& B!$B!И$XB!K!Bc` !BhL,!BI%B1IB!4& B!$B!И$XB!K!DuuFI֭qssCӱ|'K/N#$$b݃1bʕɉ> E͛7ӪU+z=ԨQ3gZK.gx⸸P^=~ᇧڧx2` !(._̷~]v@Q'͛ٸq#%JGJJ AAA|wL6;wҧO}]FeVw۶mPxq֮]Ν;֭&L`OvoСC ⧟~b֭ <'ާx BQHnZ8p` ᪢(s8S˕+Ν;WWCBB֯ZJUEݲeY#T;;;ŋ^xAjBBYݻ%Kul6lPEQ׮]D I BBKQGHe^?Θ1ceVsԩYy׮]1 lذTfoofuz=Ŋ3+SU?z₇}寿2gQbE(% Bvoatֿ6t8s挩lذa$&&2n8bccYz5ׯw17`ԨQtޝm۶W_qe7oΝ;wCQ^=̙T\ٳgkT\/BJII1`0`0HMM5kY-66^{qQvlժUdiԨDFFʞy6mD>}L ӧOgرzf|-ZPR%̙̙3{.IIIϜ|P]~*vTwݪڷo_uΝ3fNNN L&Nt:jrrϪM4QUUUoܸ*t:ɓfqOuvvVr|B҂%PhԨᦿUU7ח)Sy_/X~=qqqVd߿3xzzm6 @z(Usa;|pj֬w}g*k֬vvvL0RjUn߾YrEݺufΟ?OÆ 99' BBx4hLiQϣ*={Xwuݙ;w.#F sv>jժ=zg}ִKmŋTZRJaggÇvO$L@@QQQuԴ˦O:4xr` !Vtܙ={J~T~) *?{l|}}۷ϔxev!z>#"""֭#cݻ7|Ǐ7K6lق^VZ?`)IBZ>vupQ\\\̆5h߾=3 YtiJ.m?GGG<==Z&OL`` >>>ܹs={yf!ƌСCի...޽3gD:uh޼9:gѴj '''"""8x իW7u~3f +VW^|-[u1k,!!DޓK!Dq{9 .d…(Bjji]9yLkܹèQuiӆ#GXz,X@߾}INNB ={Ϭ_~IӦM˜9sHLLlٲ4oޜ^zTݝ;0bbccQ˖-W^y1 )R!B+2ШB!$B!И$XB!NB!yA[Sj<ٷ$ Ba~wUq%I\"jӦ{Atm#օ ҥ nnnt:ӴL:NǾ}ʵz?X{ڪ^uʔ):ӧOkW\A1h M[zoh"w0dtޝrZbɒ%p/Ei'/x쩩tڕ0^|ENʻ֭[iժ%J@/g[?K0n|ryz=nnnmۖ͛7?4-Wƅ򇑖ŋiٲ%'OfԩVBa* (╯G @.:k׮eĈЮ];W_e˖-QB&MpJz}]p4MΝ㯿bذa̟??͘1ӧO)W.\\zѣEyڵ;v,s̡|$&&zjBBB?>Æ qM_M||udOԪU+O?Ͽ%Kch.mz"IUqFufa9m47oݻw}kҚ"\9/dڶmKHH/Q/// ޽{sիWӰaC\\\(]4/2׮]ZQ=Jǎ,Y ߟ>'L6t/Ԏ; ܹ3۷o7g0iܸ1zŋӸqc /x\_'OҵkWpuuu>>tؑ;vo޼СCooozExxx1GNSbE:t(8;;SV-V:]-"I V>Vbrͧ~ʘ1cpwwgm6ZlY}ѱcGTUW^T\ӧOӡCZn5~{2p@s|SLazzA d޽YN: |wk( ׯgذa۷UV=s.<}PG/dƍ߿++W|rڴiC6mLׯ3|bbb(U͛77M|UXXlsL>={Xݶo߾:u={bggի9r$9s 6ЧOz=7ndڴi>LCЫW/T‰'r~v͚5ڵknݚ͛#Ls/sYϞ=QuL+ڵk_>Æ ٙ8]رc?e˖ܺu:/r5֮]͛Yv-ݻw`РAmۖP8pY<( IIIk׎8^xyx" ,xsbPE۷o6ZX1[z`0=(̶ SEQCCCMej*UT;;;uǎf-Zd޽{)m֬lʔ)(ug(j@@zM\RUEmڴg*ڸqcUQuʕKUE4hnݺuϓNSׯ_ooQEQ bVc߸q(ZKMMU^~l?֖m۪׮]3*(QUQL2f[VEQ[jǛoEjH*WZdI599k0ԀUө6m2[`\*N4ɬԩSQ磵޽{(j-Ott8MPP(:{lyjOUEmӦd*RTEQ}%ǎSu5^F ǎ+!Im1aΜ9CNrk`ʕ0f*T`*W>,Ǘ<A.] 2[ꫯRN<~i`X/S闍̙ŅsW_iWٳYYqttl(III_:ŋ?v? 0M7nUVe׮]^K|:ux9|0]veϞ=oߞ8Sdq9޽{VG899.W-[$66ɓ'a[׮]ҥK=r%:uD׮] 65kĪU`ʔ)fyWHNNoqss{>_ή]VG6[עE  @tt46lxb={Yk;ӧO`ҥWΊ+r^(SL;8qVZY]߲e>ɣ8q5kfiӦ8;;i5nآ///S ۛ3gҽ{w.\ȱc0 9G1eT۷7/N ,ӟ8JB>cԬY={m[2Z,/޽{0ٶ|I\>Nz(Y$⥗^ >ƍôirǽNz=[( Y/XwNZ맘~X{[wxm}~z}]"##8q"۷TR 8'DZ׹st? ;-NZ.2LC5kFN:ٳoo\m%eu}vY(QrLvZrss9Rdl?UU޽{c/^ŊY|9={yԩS0acmki>|TJٲe֭[vCFa|__q5VXA˖-֭cs+cs8.¢AB??',,lذ!;"""r05g7ynF&3mf͚ݻ[Aq!uG!..CzK͛7-qܹ\Ś{͚5y뭷عs'?cI?잣]z)HGK}ʶm,ٺu+м|5jįj'Y^ʕ+ /Mcݾ}ۤ3s9չ;fQq J]Z.e9/OĉiԨ?s/9d/RX1fϞիWM#GO-\2[l3gѢE={6ǷH_JqFhhd?ެei<ϟ?oK.|N~/_ίjnܹ\x:P\' ?~#G5kK/{7xZ4\… qrr*7oN@@۷ogo9z?+V~i1<ӧs'Qݻw9sEyBBŋM7;rK.↏1vX`lzPPye` rcA!l2L-[0㥤8@f͈`ڵoߞ͛7[}_e=իS\9VXW^y(4h@`` M4ۛ6mč7>|xR/iѢGf֭4lؐkע( K.}lS>>>QvmwNRR֭֭[ 6,_nK 'Dg"@Olԩqt5nPnZJmРz{{/i&W9rD Rzՠ uqTEQӧO.+VS([lQU^-Sڭ[7u׮]f 矫5R]\\TWWWQF.]֪UKuttT}||7|S󤪪VX1~yuj %Jٳ'ϏѣG՞={^^^:lGv2s4/ZHԩZ|yQuttT~c-[Lmܸꪖ(QBmӦyfuVt:12رcjpp+Í7^^^Z~}Rl٢*Λ7/GxQ}jɒ%M޽{{'OV[nzyyvvvjɒ%Ֆ-[_}U7=!C~~~S ZIX6LU՚5kϷ_/d|MN%vڱ~߿o6M6S}v2,Eiذ!Ӂ/=S8Z?ˢJ. M$$$׳g% X{w%ϟOR9(%}&\Bƍر#ժU`0p1`RÆ ^z899qe~'Yr%И}VKhL2ݛCuVRSSaL817 !̦MXhqqqxxxرc=(+L6?rާ_K!}f[;w"}$B! I*G}% BaCdA,!†2d@K!!sjO$B!l` ` !6DZIB" 9____>k֬)lΝ;߿?*Uwww:ẃ :4xbt:zCIs7 IDATK.xxx@*U -lΡCԩ8::RR%ƏO|||AV4")OBW^|L:m۶ѸqcϪU :4h"nݺŸqصk_5mۖ&ݸqcRlsoM67{gcAfSΜ9C۶mw˖-cݼ|߿@H6n˖-t֍UVϛ;uĹsv:w۬,11*UPzuvY@ٮqsscݺuV'_u 8p ,(plĉ5̟?hJ,Y#/k @d$$6nÆ zkV>h n޼ɑ#G (2ۓ5ptt$ ׯ@DmŊ/,\ei;t(6%;ef^NˣE/ymٳgQT:u8w\AUhܿpjժUСؔ۷o3rHfΜIٲe :o><==9<գXb.]!CHK_ˋCruپ};y7_.E"_OٸHTbQaZ/7l08qbAbS F͚5y7 :u >|H~ gرc?g/t6ߟݻwӭ[7*T`*:t\^}2V ({=[,X@ :n:6mĩS :f0HHH?dĈ4m{{{ ݻi׮]GiΝ;G۶m d…xzz믿2eݻNJ+ :BEA,i***ʴ^X >`С͈孷bĈ.]{/ZaOOO._LNʃ8q$Xiyٺu+ŊI&xyy /0` 8C-9qo 3gPv˦t86ݻܹsYfaZV^Çqww_.0mBz,nH[pEj׮mJ5lд^tr/9q={$66u֙/[ ___4iR@٦ӧʤIx :CXX{1-aaat '''Ì3 :LлwonjVeyeӧILL4+?t~~~V%܋Dhゃ bȐ!PreVZŎ;XrdL2`vÇ7mڴ"nڢ|ҥ@TCt֍I&I E5j!!!tԉQFJhh(jբsb"S `ׯgĉL<(jԨիy :4i&Ea۶ml۶l(PdOQI֭ e޼yDDDȑ#2eJAfSt͛yWLJ^ziӦao/_5>UKFrB!l@H{מ!#',+B;-NZ.+$3gҶm[J*~~~1h?>իWɉJ*1m4RRR,ݹs八+͛7gݹڶH%Bؐ0IxR _~%aaa;|jՊ9>}c|a%&&Ҿ}{˜7o?#K&88}"r"B~pO%&7 ܿwww7o&$$o_|02R\9_|a2i$4p9o2ݝJݺu)^ K` !6N;Kay$:"[.ׯ_7m۶D dVwРA?`*۰aիW7Ύ^z_oc$B!lƋo'~׮]ԪUTvYԩcVL2*UsΙ lK6sDw+p;sAcrpEÂ# : LW!XSɤ]U-7tV$>J>;tV53.}c^̙3ٻw/۶m37KO\Ν;Mwf˖-3K-[(TֳgOʯjŠ+hڴ)eʔy#/8` !6Dt`=jw9>>>N:qIΝKRRUTT 0zM4{8z(kT^ݴYp!}e̙xyyᅴP,$B!lqtO0xIo߾Mxx8o[8p K,1=az= .d֬Y2qDÉ~lݺVZ>p! VaT"kEaM6( C] :B.`egҥ,]?!W>|8Çl=ooo-[m$X$X9" VHrN$XOIAaNcI%BEKkxK!5TI%Bؒhz$B!l*~ '2rH|}}qvv~2Bm"`=^zG}DjXr%`0пFwbI`-FCu%7pgdB4X!}2M{ |Ns!9.5DLgHý7 ǷUKDERL">z*&τyL0)QP ׅ+eԙ{V\?beim?}LI0oW^_U:Յm @}HҴI#j=tժPl9|k9ݶPyU"8@k,hHb'@7O㘒ySZ }aF&̓ >T_dQUt9,._{{cC M1J-Za<38D]}nyaӑ+l®]Xj?o|ӶnݚW2n8yt: pp?`!\kcZPd O{ӠjX2 xΞ8aK^|e7 N:V͋K+O*TJHK&TW:ÚL\p읊iS]~>j J7T_X'& VPz?4i3߆ZB57XyoM0 Oß1r I=q iR'q`0Po8oYos^tƘV*4o/?  $ALkĘ}S:Z: `,zs+cΩa&΅adOBϠI֒^w8|^JCac`<8 [avF;ZKE Iˆ ׬|РA 9rf͚=݃n]PvUFKkH wƁ(1R&7sjVY=dv_/hj ] e:;&k\?3f!XLUM-V+s56S@cwZ|ZscSr,/,-î]FUk t L2wmƥcBP:gX.Wc`~cYi9MGI8Jپ԰}8.ʮUsMPEN]pGFU% ؒV`l 27V̓~zbv)X:~]QMw3mʶ5NoKU-VUZCUq4o¾ϠbS%eZth`I|'Oy.={5jXRթcyܹ@yGArx9PMM\.m!vǑɏ?^z2܌G++ p;RonEzTݲ^iLu*AfwYՄpykcW弉']O[@__q c+2Z5}yrW.@,bvp4 @ }w)z+PL7fl2bn•p0żNߧ8'ނ"-XN\òLzYdd?HYpJDZN?@M@@eP>4Cc+Uf>CX[BgQng߻~ViMc: 5,'jvʜ6/O5@x91EB +1]A>]|¶`t,^?]& 1v͞AW }^$yJ_LqEw ֓Hh17ؒչ$%CKP|U ּU3:ǧ8ְ5ɽH+Yj֙3Ʀڵ5vh,5*cyE=wa*::c}~stJgZoVƠEc>hO'V*0h^H5nwQؚoSo%~Wa_%T^}JvR)ٟ:Z_szP|U2t0&XZ\2=~v1eu"߼1-*{o5E8jx;N& 8r4befC9@pq'$h'0 E4RϞ=eݺuf˖-ח&M<8BoΘ%p * LA[㷸߁@Ps*?a@Oc󇖋 4$ʥ`2d/&,,_;vgV444<2&CxX7]5 }/=8˿Ye35}ԳɱI\^g1²c-A&;LՕhxta^g8t_~_Whd$T҇blhtYb |C20&c~Ohm\!] !)?dVb5JYt e;ܻyjalc@ w 0k ?KiىzZL糔L) 1i߹=]붛/I]K{)( ŵ>bT_AI_x)c7:0"\"|ׯgĉL<(jԨիyy!UUp]T(\m ׻Fc6*wkaϰlIFHwf}u6ukvo/hR(TC~ )&=}I+( )+I!Ruiޮ] C\V;ʶ~tU2qXLgg!>h|U'*JªK* 6W¸X[6Sj]$<@O %_( kh{vGWbYb^8 Ʊ'*c"hQ4 P،qw @/,=O`} |1ryXvϩre`IL2v&jC X;bTec;L p~3~&/o|'Opuueܹ̝;7w=3ј0Fm(JdJclri2hih050NS=Y35 ^z%oEGl+ضEۘqdqMqȸ~E}od} iEX&ƾW5`u;cBlgL柃kBgh[koZGkă0U XxHrv)I4#T9Uq\/\\Hd.۠((~)8Gx/1\ (Vw0^{c֞8UNXZL^@,ƺ1q쬼~L &3NS2 uΨLYŘ|8BPxM۷e"ll{Ug4޹UjA@OAZ =E?~ 1"@|mǹ~T!X;%-/hD#FR-@ŗtVX^5aQT]ǎAm?ӿ+Յ_?Wȓu҂%BHK!%2V BaK򢓻 Ӑ$B!l8z` !$}pP)t{B!ИB!-ѡ})iNw` !D&{.$B!l` `c- o&"s\tVpCw36ttVtxo-,|U~/t&ǯpkv? 4Z$H%BhHK!%҂U$H%B^$H%B^$H%Bi*$B!l;dN B!4&-XB!-C'iw` !D` ` !D",$B!l 4Z$H%B*HK!%ɽHK!5TIeb`a8yN܁x 4{6g{ZCŒƔ̎i[R\-'$$G7EhVoKp (GOc>87NH"4!SSU=Ȓܽ^l'mRWE0>/H:I'~yS6eEݘϾIr;8#ѹ4'&-XEt{AwaiH6@*2ʯ Q;H1X .EC}hx,:Oj:=+f7b k$n]$5@`O?1%ǥSRl.So`n$'h)lZ)*z[ 1U:? <_ŋ /dhߓllBg>H`eX,r# V@r RS >^ WÆeл+'hS"mÐB҂WKoP_gO--o JL'DZ *Ǜ;3h],볓4&{.ZKdTԘ RzJN}r;ߨ)~<1%l"_&ed:*c6eo.E{ei٧1C>^U_eXcM6 >.6yט^IgΈ-GPxpk[-݀dYxZMv5jrJƍGzz:111t: yѣ$&&00a+V`֬Yt֍-[[o˔)Sݺu^z_=}{ôjJ'rDe լaeR\4uXQ(&CuQŒ5VwS]HJfƝoƠjWuwyʽ?zǞ6<-S"n\YDM^RnplW'!)m8S*W5cJ5A8z=#֗]vu$Xh4""o߾\t 'X_|[l~㡇*/{),YB\\>(̚57x78py1c_Dt t[oxuAq/ģN hKܸ\ȼqI0`D/] @̗uC4@s hdI$׮kо-L!`G$*~"F8Ey%ei!4E<q҇  JN|LU)s}YT>#""(ʆ 0 F_u4 [l)_~z{ Օg}~ ^_z@ez:nt~eBCg }=дlZ<|b׶ova\nX71T R`{a,&&lB' ~#0Tmx=g+*Vʑ{]83r}?zV:!Rww\q0g޶xLU[ t:yyy]Ν k׸z*O=SN%>>,ZnMtt4ZqNXf+[87h(O|[ . Aj'2;؉yOd11që_54kHZRO~0_!W~EHͥ"P:: lZ6à)BrZ{un+{L:Ww^T*T*~i֬YCjj*~~ @BBAAAyOβeˈ.?ҥKqrrb!C駟B&[z  &5dHXXG_jH 㦇+@U{{[ŭr3rớ|Ws *x4uc+v\Pƌy&&;YȾWeL_Ao C /6?ͫU+ldHoyL.F5Ȗ.n<ŏmktާ8&7W+3Wє0MyӾ1H.}M\7nV3ܬݤiahZ:쩳0y6]K{y>^q+P6A*z84Fk^#hj|̐>hW^£ b;#q%2PBvזcj|?jT.ި2Wcԟ"h.n. z)J4]tyX[h8N 7HhJ?{mw Ңu&=!@Bd4lҷ91Ka*xM?s`t󮧩]L'&A`^"D@dTq , 6P#: "=[&%%E<9\*''U$X;f# Kbo.#O<͇b q 3a snbdHm`_';ĭ,';Hʸghk˽)x|ݍy P(ƭ*OglTAuoˡWe:k߯!ofS+#)5SO0bOhK9,'X*NT>zѺ&},^`Wm9ӿC*>ZJ=~p"*,LM_(g!͏JosB"ZtOި(ק ə` Nbj `]σώ;03\*Hcq?0|171h` OVsBBdTNld¹M2"po*%`gK= (,)\BkHɒV*nTzMLɔ7M(Nɸ뾶rW\ګhڕm_9B~}Y)S9鹲o mJYc\mpoK~|d\M @i/5G.WkooQ}P{;h 5QkFrr`o=&Ci7@re(oLMT!"j,N$X2Bc-M.u4-۰d^mhlRKRrB7$1iSؠӡ/Ѣq_`Ӏ#=u1IGibY|+GxK\ՄNC*wqNtlܘ8]vEb*Vku!ȻMl9/5~6; Oj5dߟ@LEpdPl(߆'d%oM?>]t1߉~ D&qoGZi bZxڌe-EŃbzLaBԗ54ydۯC0k86odJXcpLu Xq^ۉ Mk,F4Eļ^1 1 w%crw&RΎfi_bHou&øyhK5p=;Zڌn֘Hˈx 5o,~^cSkֹЫbtnWut(w M$Ԙ?7{D2{l0a½w68T[/菉YfLjct;|I}T3++~$Z]c2}9&x\l= ¤|MӎpdM?ye~(ͺx_Qvq'bI5usn+~'إ/DԹg'$kxb:<}3KxwE zRWeq85zs)l5ou}:d}'r泧9MP8ӗwYw-yxka/L, 9 ]J[Y;@00L*%(+pXyNZt|1}`ݧe5i҈ {fC8x$α[e& P69gð8xĮF ƽW]&;~ٰsVѵENIߒ<{Ǎ',SHvmדs0x,c‡fiC)~}:gBŕkz}6nZ)PclLCcqk4ׄYu`OTh2K<Gc<7 s 8x9ѿ<*Ӥn03nBlhweKZyH IDAT0v^ qÄofCgaֈ 5 a+>q=`B^kˆ0x EA]WKNiYE6:qHpy|G8C)zz 1cbY ij*>ʒ[}t 4oLW?lK4>is ;v m]~4?ƞbv<=GFL`0WnB4[h&{\Ɠi#^r~\?>íJ贾N7Uڨht{9NA'?i` 94nA^8x\{uC)Lh#zA$Xi& [la˖-F :d<9ZփZ Jb'䊉F}`L8:b .)7bkCoWop6Ҽ`j[InφcIȝ|@ \+n0=--Zu>'gX/As:?ۀ^SLU\-αaI: hDA~\:`7ȡɛ%:44hZCK?{-btG]5^tN/l:(=[HQD %HOvMant4/]߷`2t4$i1c>HB}` \7c;&: gHLL$,, &}'ws fo'u&^\ޭԃ: U!0̶3e܏=Jhe׊Cyzi{XHA(*`YHA` 55XiA̴Tرc׏pvvk׮,ZȤlbb"?8xxx0tP\Rq ڴi-Z>@՚KOOgĈL=عS~` 5QiZ]v̝;ݻwa:vȸq={vygһwoZ-Wfɒ%?^zatٳgosϱm6&L@\\QQQF劊ӧv?gƍ4lؐw}U u8|6Cae+ZǤĺ41rq]`]K:/CR:rg5d[e=4B㵻UNDDIII\zaÆg.] ɴlْ̙3L4iˆ#X`A>#Mɓ'i۶-_}o&;?0:N:nn5X `E fZjãVeӦM :<hڴ)_|ݖ-[(**_7:믿``Æ ֯_O6mʓ+J+¡CHMMȭHA`Pɼjo0jܾ}xx].]M:tŋ)..ɓԨQ#9uT'OV{LF"A+bPHǼdzpB@Ecĉi'l6lHff&8::(?@VVVǼ"` )_߯Ukש/&&cǢVٸq#SL!??&,AA"%BaäN^w/ "##Q*̚5qH5NeeeP(lyyyQTTDaa!&euVW,}AYj+,, ^Orr2AAA8::;q-[OUiiidffҾ}u:tQHAU*d]tѧk޽T*a[2ڵg}|݀ppp`ҥF[t) gy|ݐ!C8{,*_jYb4jԨ[ D( VĠP`P]^d QQQ'[˗]LKnx'|XƌC6mˎ9/9sW_}Ņ _d},M$X `E*zy*=`zY`999ѹsgVX˵nݚݻw{saccC>};wI_Sʗ_~ܹscʔ)cǎDGG3qD ҥ uNcVN$X `Ud;s9r$#GQBCCپ}{N8|5iND% VDRW]%  X= 2`}Nʙ"2ojUм=Cǹ3dQٕefA67JpTތ::*Pb5X<`Y!:vMi4> I_㾠8]M(8{%4v4Dc1{ШQ;P+qJ^ w@PplC%\h ?M0ngAx(ARru,v#^Ơjpn~>tΏthVy&Ũit̕E|9}M:,=zY'ܛH{DG#k1f\M__@&x\|#Zh]!^J½ C-L`7 w>4Z׹i4l[R`Zv}<IEd>îe7dM" p6ZׅK%[ͨoү,^yNj3SGa~Zz!=:)F+-&KcVGC Y\84=#LPNTQw&SJèJS,5ҮI)=ܪ\6fq}E)ag`(9*6)A<ﺽ$kۻ .-,.ƿU6 ]*Wͣ<'h@ks-ӽ`ɹ>X'j`][kpR? ͥT8wlns@A^yp|-N{IF*a̝|}~)?CZ1(tR[k!3x :0\>hB(}ܵ;O؏ܷd/^iή5K ť(+$$\ĥDejo(iƠBq}((} AUz .OxfSkF)$m^%5]-(&hf.{ t&G2x}nEmې@~oXJm팓 .(2KLe0DO\*܋H؅I]ByOڳJ2st5 y5VdxIP G=S9)5 TOj߄s[x2LA]/5)<5&U{$6L^߉vSc4- HQq8Qj 5tTω PgSmQG>ˊ5yOS瓱|._@Pa,0Ɲ0e*0pԒS34J37•)χ˅ 'p1\:WuԿ;f$cW瑷q/S8ވáKOB:_sPzC`WڏȽW+$Xb$hl%j 7h&eէy | 柏iAM(Pk91o\(_Nj_WnED7bg1iUG =1 NZލy̟&_ϸ}hс.A`[;=̕3EJ +4c2c %,+Cw= V7n ^Rmi\P(l#@ZZFJHfIIWW~J ;f됚;C`' iputѶy*K\דp0Xj* {H pJ%q,ߎwţm2StvDa#WG {U?JslkW(d`$L̐`U=U %dsT0 (8+YpC'Jf]߸&((§єgUw$;, V[H"Q*!~Vώuߖ4cTfkzrp{>yj1ˀu/ %,q kю??cK_U% LEZ%X濺V`epX f\*ZW<)y ,CX&Qt2hu$0)w{zn/ZO p}*@2 Ơ7}- n _ 9:w kAoZQ+֒xv ĽcSPd :@pWQ&Aanۖ&Oy/Cm![<|l3dikfgwX?,ML4Z?{Jc? ^ֺh򑄅'J-\ȖdB:H[Mf#N<4[4_2iQsPL5v^3^ܱmW6}qzTމ*~ Gw;}q'?rhDwˁy.ӲK{'kAѭ}ڨdnO@`!([%kHGV`wx <)'eQ,2 p9Wlh6<\8ߨ38:.fp`]:`Ht n^T=5b)MInpbW/ϒGٜ4\ݕtwS:Su\ߢp.woP@Ӗvj95_eKt(eO>po"B.aoƂ`K ڬDޜZ7q%zn6 }:b$L6M`<_9;++ lxEΡ X3AzvRXį+[ู@綰b. ,GNJߪN!#%Ԕ7.TnR?+atj9R'wF !?Sr;GAέ_! ?qI@7ǟc߂3fK^7]KluRuqrR @3Ѕ7v2pl]~˓KGKhV{֌`7%9lq@ D_"mi_펭07~Ζ(䦢yg7HZޞv2>MFv.o;[ȡm?f͂ls8)iفWfJwfqJ v ڄ:uMxtb[:Gbb"aaaPiJـJWk}:Cuv-Lor Z]PLW’8z(]+?@Py3v IDAT,q U5X `EDHA0Mey"A+"j` їޠYc %,AA"bA$X `EsQei"A+"`"A+"&D% VD E k޽tWW[ 员ȣwo{uuwyuT;Nu&JvLdc#Ōa.d),qE"1Ku{̙3Un;{,UnA+AEz]u7:Bd |.RըjaJrrQ"[5jT! BtdoB&%X~)2ڲSLDH Pcb%X}h&NH@@Q{{{:vHDDD]( .ѣ=zcƌqu  ԌUNP;u  ԊD(D"z`-_Y^@T P3z3LMW#Fp1b=ˋKA.b%X/_߿  oAdd$}-X M`r?k yC~$G'2jG#WV(us9;o6|u{1UyWYBRW8gm ?ҕŻY|]T[`0YUƱDoQNI3ɁoW^z\NjW 3{?{ŵVz)**j슢1vcEM&FM4fEKk{{Dcׂ=DŎ]D"H[ce,~~9gf>}9yԈ 6ʹON~FND!jSP1 |p[͊d6ՔOߎbNg`N\<&֤ދh\9&GgSM>y }v$r5{-kL(=t9y\E&Ok#9>| s'a.tnE㿠,_F2VIAO6Ο?OժUUZ/PNҞc̙3ǔ–,q+$fcYOB g֖; og`;D{5M4mjbu퀂Um?GoǺ1TtT zV+J4űMwgRݱ8Yյ1$L˨(Q2̸*x'7}|xSm)"(GJ:9l>]߻t 0Hτ_{ վ4jaf\ٚkp(aQ J즢r(ޕls44p7> m^TMN `y ^ٜp|/{[Cx! i6{wԱz%9fe݌ktwli=UM8c"l߷JsqWQ[{ZmSk«$m?f撗)OˢƸnk œ`qWuB ?WS K(D5d޽g}ƞ={Xz5999tؑhDEEy[_2~xx 1j(f͚ѣi4:wLtt4 .d˖-5zpʔ)dff+W#&&hժO[7~s%''Ddd$Ӈ_~ݐO1[w/ .}rnlֳ83hR}dP,g&c#dng}0h!:- `/&c$n=M3_>+qoVwڏBe3DyŰ Z"k[Я]⃑i:'&ˀ Fp~iۛbl6|~UT돍wzg<17,ncR?X1'iwf#_6PkBo*w B3 %hzά,Mж)|WNp: r骁/ [QN&*:,7@pDԭ[۷o[ 6]һwo۹s'!C2Syfi&իg2 o6SL!11?Gx1LϋfP9s8p;wR%+>$7S˲16&n>vd=|}ȕ2\L8{3;.{ p ̕Eo͜Egr|z넃+yƹ_f3Jxt Sǥrh=jm?YxbJaJ>OOFswY:KLmL&lNvȕ23M !WF!#S򗻢sw{B=9ZspQ|㽗B)åp_hb!nJ%6D\!#%W#r2,Gqq}P|2Ã\ΏYFqݰq#gJR}_SG=7sJILȥX0f{|< S. g ÀN < zM*?>饐I(Y8q.]/]'Npҥ=<`RJřmrظ8*/ܼy___j57o|l'wY XBAvx2H{~bQ?c->L}s`MZQJ󒗜&)+s%w]CӒO{apoQˢ[p wmf.JOIi^؊ _gln*T{5,uzn)ύF|G&'',GdArH|||J<6%%;;;,<< M3.8m6sJύ?7g\qUr )t=ckDc!*edj;A!hN DϝvѓsY 5g+.fgUrAdwb11;ƿo0?|׆">B^b `8zņ_y@k#k)4?;nau5Hc`\+P(dS6vG2ql쉈nU4 qR%.ݝؒÔ|δMpgX|l&Z휂+J7[!i1Qy:b\yABsӰs>T*Q]@ȋ1"x4 '_zh'>~ڴi[ŋӬY3S#h֬3k g`-_@ߥ+XbhS)h%z-  x-3qLeJ4VYMO ,%PJia22TS2G)d9y{N"t܏)K೭Eiۧ^N]:~\>T7}4C=2ƊCr39Un9T4k'2;;dm^(ׯAyoduG㐿jѣ׫~o8dzӨw+d4lW8r絈~/@\Y > V56~X]v ;'m߲M> ,oFծο_k/܁\rX:M>W %:3y۩}3ۅتvEBml: Gێ·`pzliGmVvd2s-*JDD_~%fbԨQ7k.>lUF!=='''J%^^^h4rss/ 855-[HMMQPN3|$d2aߚ&^d(2w; L1Z6d, Xmƿk xlJ1b RN@&nh^kpkZ/lkQԵ mܕBRRN\cI$zᾋ 88[0 8>AfeB|M}u(Փ.pp|=\%"쏒dE@N;8Wo~187ʝGvpiP& #`XN$$~Z"м0t2t"ߜOr6i$8Z--Y%Kyf^}USٳg )0߽{5*ٸqcΞ\ܹsfmi cN, Nʀ+Zk`sG&[R^-k VͲ`.!oT~_YK0Q0066EG,8ר\ 8N&3?u̘1p>s7npg;ҷo_>C6liiiTRߚϙ3SGzC_Fe jѣ֢~T8VDDSCM%$$$$$li5|f̘AXX=z=Jթ^zG*U&yyxxδi_DDD0l0q0tPx73g|\|={X? i`= %!!!!Q#B+%o֭d2vΝ;u2ؔ)Spqq!**H2j7n.!!!!!QhH^ônm=Phh(.eE Ѩ؛DR ,;;;NDi"Xr=XbOHD9BZ*bPM@Μ9cܹs" GShTMJ4ZThh4V4 ׯ_/]AAJP1֣y&...e-CBBBBBRъ@3VZʕ+MFլFӴm۶IHHHHH<ɃU1pVVV7?x`vvv׏Җ'!!!!!HD g`5QFPF 6nHPPPx2D g`E b!%Th3g8ti?##!C´i0VIA"oRҧBرcٽ{iĉ]^ٳ7o^@aQ6ҦB1i$t:ׯ/`ҤIDDDzj&LP*-:BN׬9D@Ѣi_}/?PӑyWUS\պ1RS4٧.{&}Z _B .?*0Y~s u!ƿۀ@6P |W1bÝ-M!#Z̃F ?E[8UjxMm\9xעMnu%ՠ/WyY\#d=Tnݖg$@u1 D ׵T9^^IP3 $v@c`4H$M%woƀz<81.;/- a9@?bOTOOOgϒA~ʛ IDATСg.KyE]2yuw ]> C}}AN7p8?dj蚪kffe k~0Y.ج6r{5Y'/I^0}Q;p8Y@be 19/go1M =5 2n݂:Wʠ:96a6JE*/u: *7,hg-lhg^ lʅObe=؉`Pҁ%b"z6T-yy7n/;!5z&g`>DR ,777n޼I֭9tަq 2p X`$&Dѳ=zBH!= ]@SD&TĔGU~6Y 47lo_Qh@6\(T믿Zܵ]Ќ@?Lu 7j`qa}S\.ĺ௰ < k^AD~\D _`lEn`dy} `eKb`d:-RHޣ/7h3I 9[N r{@?H:0Un%Ш@QWR "oUT!ºurUE^_{>yzo%[Ys <-yx`HMUqiM|avv.۠QSi8?O(SlLɄ.Pv$g7tƈm]ߵ*/t'6pGsam|u+1.^S;X$on]a6f.>?2^oN$<0ګSẐɰZq 2+MB\5/M>r"o|T$l=A@W2b%o[`01$XO5]6K.{M`_$h\he0[u o(;wxIDslZǦ!q݆rkwed`50n!0/n`Ulq+ k2 sۦ.s C@5$y66eCZo1N ,tbP4J©m5Bi;ٍ7eqm:crnM.(V CDp2:{̳jmLQ *a+ D }!ʚSzY4ŨO0;@O`π` ab`LJM7+׆#ý?+@V>ur*͵X e00h4p}0|-,KR9 FVi@^; [_ɂ̩Y Ѩ*m* cǎ߿^zIdC[e/T(6С -E^vt}8K_o:5M^= -) J6x7%`k.2y.PtڸXuŨ˭q!7Uפq߮5Y 9(]*oJ^2Ӟ Cf߷#oZyh܈Qc'0\rke搶0 W@w3<@qe6 $-M2M@"q6" 77g($],5DN"FBR4tͼPK~l_F6D^&\>/T}nPմpcL㪙JHP9ԉJ{½xRSx|\Eypux7 lw6HP9Cl УAQq>H< m`0yd&OlQ/J(+r?Fu̓~opv)̟d4r^}wC` ɛ>y((`S(\\CbS\^ _s.Y"r `WVl|  q8#(I@M2°`A}V?z @䞈spG6,k;o牐QD@!^HN6˅6nu(ׅWZ+ t!E%]U\}sd$fm?t_ѤOu<;踰6_4%rm-Z\3v F&c!+ z$i*pUDfB[qaY6(&%'޻D_0°|ĺkE^l`p .䞕w惣/T?˚B"%-}*U4-5kQyhٌ(E鉼M+ fO/b:f~EnAR9ݺb7s2%HMrťmWL432Osme2?* #pDU"$+/8A) `xɀqV10^DM/ AF"LK""(F04!Oua.-Y\׍w9s8yQ/Zю`!-#yZL:N\J_pI' 7ywK|U0ppteo* 90/r °_[5pbNyf* 5.C*f#] sˀE"V=`!>D.oSA{/o W@"]~=m }_O]Q=Ѩ*md-'O$88{Pً .~()f .V_2vҲ`au{dwJcg`xk;>7I'`bcci޼6ߊvqo.n'q0xMtKXB{$$$$$$i$K3V^M=bժU4%$$$$$J=2*HK>Ґ?(G"ѨTN#XqK4.]R)kZ/Zm+e(Oq4>݅Yv"SU K_lr9+1ר~oIKe-cZXߕNjx]-#=£Ηӧ9u)))̘13f3 ,X˗RI&|駼k]hQQQ\~̔)SL0a۶m#;;M2sL:u$?_FT8ko$!!!!!QN+99%KD߾}YtUPNʜ9s"''(x ~kj_2}t&OL׮]9v$$$wFsdddpB*Wŋ cϞ=kN~(M*e{%!!!!!O`֣"QiiituOUܹ3 ,0uVZe2RRR9s&Çg̙k׎|?~<`ٲeЪU+:t@ӦM0a111ϗҠD9`'pRYJJe*۹s'b!C`0ؼylӦMԫWd\( ~m;Fbb.HD9`171/GF2tP ;6mm۶v)))`qRRRLxzzZ+(+d`IHHHHH#(+MC~Y.=ye1w\MƄ ֭yyy^>}azרHHDc`W<v5+:?9ݻԩS裏L;v$11#G ,///4 ۛ'55-[HMLRP̚)KBBBBBC9h¢.88;w)x bΞ=kݻШQ#SYƍ-;w? (G${{WiQ'aooʕ+ڭ\LF>}Le}ҥK;vTjYv-ÌO4D(!!!!!Q(D;v ++콍=zjժ׏ V CѰj*͆ =<<gڴixzz/sq"""6l3:t(QQQ̙3ooo[._̞={DF2$$$$$$h`ԨQܸq矑d\vV^M X~=_5r w1l0M2Ϗɓ'3uTvj{2aƎKvv6͚5cǎf3HD9i>9ŵk{Zә>}]sر;*Wl1XbÇӽ{w<==QԮ]%!!!!&x:$3n: DYf Njy#\ tosWzowQr0c '*@w`qp'?7/^6#p21W!SNk W;a%pr-ajW=DaȞ0d<Gc?eMmlUup5lac܉Lp FK3ρM!-:̓Ol 0d΂|c_)p6Jy-SvH ?YYȪVEѿILm YYhYMn{;d Z ZDה}ɳVs}f6_]ÕWK<X$9>k?1%4#tFgr??CtJ|ZVմNTm_St=s~V:gbw=CR3ߙlYI~ zĨٗ#Df>Di"XOIBBÇgĈ,^TަMQn Ú@bt!Bp 3߅{i0qt 'Zla:;:kCJft j7=ğY9AˡKX;B|$ 7R_`Rx:xb`rA .k@x'C6EC9dZG-3?q` z{wx,4fwlŜ}p[rc_RywA5 23C=?Dިl ǏCFtMvpg \Q mb2*ojĬ8hRNk4m [&vQUUϳEB&4`UHStR8qͮ ÿ=a6 }U 7;5}ǰ|ph$'k-4,iT̰x Ȍ?ȋXE6 7炃:bcÑxύ.2p.ObEׂΐ؛/\Nc4ٔʹ(O-~BO8'X.RHd>%ˋ .JLJ#G>/KwB&+MB2[2y[7H)dRdCһ<VrG;s㪀i", pbY ty}_˄T9=I_/Jҭ^(?d%VEѻqe#,݌!;>qyWn} 7 wvU'Χv%7ɐ+\̊UN*N%nK36Dۜ:"xu77wmqLYG,#lHS@VV `С:tӧvZw.5/WpF; ?]xmb%qz8w\ ^Q>Hǰ|Z ^Np!>FbŞkCE0*H̑O-@Y ^,kju ,TvM1MG>m.Qc+}ApWI>np~mxǼπдmOn%r#O1 @v6)5j[M.1WxzD5 䢪Lfx܃[pOmqr B?-b*}fwf FNIЇ/raI<27j v41Wy:N%ʅ|ݡ/"bd Y8<3.ХR Uӳb&QHCO^'77ٳg3n8BCCQ*9}ѩa߁y0yYB2d€ԅ0i9CJ:.Yx7K'!/x@V "^{&P ]Z3hTEdQ0dB$О" E,!9?i8H_;5qlD4-d&ώ?ߐ$&px.RߣߡhU7 z\mr>:Wq Jj%''F;.1R/'hxŸGcAgmm'퀐RAli$d`=%^^^+1 aa8թSJ4y5̓C`3>(0r;s>RC^.7}R% IDAT % BAXA3O)0/&-J9]N̏ =AuOXt#.OAlp0*PBH' 2op?.AHo nAQM\M\h?Z5U Rb݈jma?jtڏWγɨK 2%d>r;:d-$=梜> [ǟ;xPhuȌ[&EXr5T=qpmLd;S'soPΣhZJq/L`߸}jkoo\\}Ru4r9m tYN]CDAk3N& CKV'ix)h\?r/1I ŖsJ.I b 2,/c+B`Œ⡒ɑje)a9Dոyb-PD2~R,?{y jDq(:rc?O"$S9Z 9`XLSQX85_6!inPmoɽ3>3e/5r]Sw67 px [+Z0\yPx 7+faa{1~CV3] n꯼`V^#LxL:y{'nsxi+j@o[E%0a>Z?rCN!7CȃE0Yyvaߡyz@= p;6+/|[Uõ -6z zxuvhGY~V~J4Tcg/cyd2xYTMώFDi"`=?,\DT1JFgKs!#{*i,.CJ!hA>˓͙0'$=׀!]ERH.V ߱~^݀+_c|k ti=@oTz5[%e@p7+-2T{^=!+0gK{(I!gWֈc.t70q<?c/sWϠJ2)m벘~'QV\N'SJx0_~)XxS) .ukQӏUn[W@t^N?ݔ{ՠ+  L*VSܹz 3+3xnrW={=&+.?Oò.7Rp2#{B+owwIR֝x%(r]vUU^a,: [P12b`jXF:e,c`5Z@T21Eu ,cRU962X 1 @  Cd5g'a` @`BP(o2:Pk9Ѩa` @`Bh{4E26Pk(dN ,# ,@ LF =X2oOp% )R9nt%IaWۈ!Bc# ,@ L *Gx0@ 0%4ȞA$5>2"UyEiNw%|b}裸izG-A~XhޣP1Z@)i$ "3i{" ,@ L 66FEX@ E i{" ,@ L z"@  "a`a` @`Ji)0*a` @`Jj)0*a` @`J'a` @`JYOP#IZ" ,@ L 5D2:ܟe1 ⿫hQ;()^BJ9|ϠcS8kvñ p2 i5sUQy~ ;p9roAMO^ {'Y%;Ty}L w(8w-tm +tl[S/HX2[^Mqsj/UQn{>ѫ %?o]EanUx~1.~i:2k.}wa8wl*mԓ~$&F,ABP!r@9ɧ5j3;ViXFŇ[nblC-6jz`D0LY6\6 xKEj7Aa.= ~lf !O)"8l,T9-DM>/΂~`Fw~ à I'`,8>:ֲI *H{o‹к.8t;΃0hRK6I̚! koβpPm? |?"繛i RxHJ% K3/}<|, p;^-|uYS,Â`"$wi)Kd"߁~n΍sCh8~p;ԗMѐ  +B|:#-e y>TWLfK/,}tF(&ΡcQh5e,;9MG +8nDK~m>[ɹcCu/, >@cAQiւ"̾ЮhT|'3>:eW#/C@hO w9tSdzu͎ߟ_ϩ5Gi9οh@H*P(oM/5ApKZ ۷`ގWOTV< ud^5Uk9\J ~fKuOپu'Q{-W}>k6P*qLIYƢ=VǸ2&I"Q(4 nruS n=KFQ,{v 0UGP(zҸm\JeCR=sҢ3g[#? ;͇qŶ^aW(GEq= M6h6K*Ec^;I,PkPIy|zA Q&jdf; 4WP@F|A5U"JXr-"!ǁ7HƔ_' Yv!TޫG PAHf@C0Ƞ6y0[voO]g5TA^>[ S§5T9y޼smnOr^ -=a0/{N4y*UArYkaVю̯woJ`DM=^=Ӥm6!6F 2ݴع>znf.ҰbEȈФy^c0IyqgɍM(@3Gc;ЯW(6d&QwD]\ @q2W&m|UquP 4@PoAoLҍ^و6Y>}:ׯ[n鴟;w.&M^`׮]0o<~mvo>/^̶m߿]gJQPqt˝dF<853KHZKm?N 7.AVi ^ IVS3թe7L Kf 1G֪m8lΦ"9BJ Q A:4>r;ecwP]R\¾#aU,@3mHrr765;afmΉ4Jv-z{{x222}/-/_ɩ=00?+R9so0gwNAA3gdҤI4oUV@TTR=zкukMFtt,_Q \nÙ-SZ}.&+&타*QABظJK]3 H~H4Zxݿ=\ixPX:a/Z4ѭ"O;:̩J(ʇ3#+8~ÌQU\M/-t(qx;y'RRj k4nG²a̝eT_uphH{T׶EqG : 叝67"/c4l0uǨPqõe-,[9yzTSL]Rc\Ӻt=ztF;g:mGFa֭%e ͚5+11b111TM "P1[OWi>P@Ʈp}M༾mM1 r߽[[vaU+]`Zpuu%!!ASO&$$W8 ,S&nywD0/k:ժ$*Ǧ2Oa {C7U( K`|ڻlɾHB_cZvI 0nd7\΄"=O!@גȀ{`W=4Ys[86H[ |ohd -5U@g=KKKôiӘ0a999mۖu3* 0@ 0%ŋ߄z&L ]o8I@X@ ȏ0@ 0%D{# ,@ L ѨK SaVMQ1=j$̗%r0Z֣GTY5 IDATG-A{y,Tj@ xFUM~GT*[l @_Β%Kw'?(/7|ܦ]Q81 29x6}PQy|a'cQeNv愵 =z knx,'Uzx3̩uAzL9z9==bncFrmd'Sp+OG>GAX9ʮ))*]ruT98zfh#uF ]w)Ħ% z/ܟ~.kNy+hN/SdWTL!^P (>GUn3o$/ QYa2%?G%=:32 +Ra-Km:03W†G.:}1.^c(V~sMaJn$)/GF/(8w̕S6ʑH]•P倣 }uEph9d%3D=,)0*&fff88^@P*(2ތ=fkXVp#NíDJ=n\{Az2q&^Ř)4mmɜ5n8q82Y\L,`Z7ҼJDɦ.49C~w 0'4l Y ߇D% _N*)>z.^UpsaR~l(*y i-x'n /;W !-Fki8zτApl Q`fEz"03b̘1X,X gŊ?^z .jt @Ȼg*,%cR;GcHZ/xx-Z"~CJ-xxIM@k2~-ngqpT2>&orOor/XE*ǖ7 ]ͿKB/\rl}}/ylx"UK:& =s=Kъ:ݹ;{0pi  wmYk ˴O#˦"~T$@ pXd#f|ǩ2AJ-q0Ⱥ^%׹]9,N̎,=ԂK{}oG(T[UO-!:l]wU?-UҏGAU[^Op6,\p@PuB:Y ?B FnmzP<6Dn=GVInO+P*!Arcf`e.}JuӪ\?Yn^ m\2z@4ZFEX$!!={ҰaC~wXhׯgĈg Xȹ ίF%y(ʃ~lwIOs21dnf I'j!yjaӊ[ _C2t͞B~, sU$<h:eHٝ@M_OY5u ;7~ͫw(-$qeVx_^"5EEd\Ŧ7#sTSY55Ӑbkx48YTh* V5r${]`%cܨf$n\U&fg6VxwlHK*aWq ÌneD1CUυ;<^2*bߏR=2c:=W W;O0kQ5lOu1p' +}JfQH-!p*:kk9xE= ʼnF\D#ӱ""" )v' 777ȑ#ӧO+bk,-e^{a VqzTҧ~,҂,0/o VAw|XH >X*_Wuvމf7 Nҗ*]nMbg}j%&gV Μzߗw isKTҕf]{55d ,C TL2! M*8 79 z# !BF7xo;^R>8ĝKHO.`tj7bڪ9i,uBǹW`x~z&&kc;A+oxwaWPoViyH7X:Ca^j/CF2r{a [m?yJIsiKрfni)K^ FUMN>M˖-Kbڷo_R_n~?A~~XH O&t^_ڜKP15Y2Wj(gYXoYNJp=Er#=~Gu3nF뭓y=PSKHMdymx9ܲnغXs9&aɽ+z79ԅj2n8=F>AAjJ@Ux)$Wt0Òd & nrm o) re2ojsoîrfkZ-ݩ Tlot-}M^+)~0apf~wn{\6ϾIB%I ö\X7Rjџ;L ZHq[a&5A r!EM3L2~Р ͡q//lz]b,?ei)գ+@$}"V5&..|J_tPZSr.Ұaph0lOhLUR`kL,,``ӆH{?9#A p h=KÑݧyIvYLnJ6}N[tOJOcT?{7k~#ҴOݒņTTֳh^$e?x_ <48wzk`LB܀H"}dUXX)( ͶE=5G}`igSb\ϑdg"ԕ Gv}GBrJh[8Xopsk`T-"۳ d[e6fV0.̴T?@6\?:&}Ͱq|ef  y2 zHG`Uɓ'J~_xwe[fR7׿ *,1iJ[_ ۾J8]b\Ҿ^I}1Y9꽈28mM7NijsqULn%aamG '2ʛ8%!Q3g-)cZRj\ӾLtt. ZڔW4i/ C^9GX*T{m=U1t\?`ФT)Ӡem@gR+:_}=-eƍPeqULm?i 7_vT"-" j2p@J%AAA,^#Fo>exƷ rWt-ή4+n;m^}PG޼7^S׽0Ԯo|g Yqw7.HH*)>&ܺ49{;p-.|ݫ٥(s_y*ϐ5H1X"%LewbS^ߐ;ߣMmŅTsߘG}Kt9dFៈnhE gp!T锶;uǪ;'al4vיfꑛg"h3kbfƙl^GAn!}:Ge`I0Ro$Ъ7'4DZogpAHׁHw'H/y^\0i^\sx0@2~G"zM8Ȱ0onp&.6d s_}8xBr^U`m˫ {AWKib.IF}=x+s3G9o!dkc`Y#(Dɰ:Vn WbDLOB(\K3 } 恗!?"B-GJA}cb%j? ffh<*leDi_TWаZV|Sjp4Ӷ3 'Wi_RBAPkV':sMN~')-ӑ:OAXs')}f(KQ\}Gէo'̾s57{W UElKRɺ|uZ4Y^ãYb˫) )d<_ t@JPޫr:U1yRx},R*bl,`4p)5xZ&ʊbvd)xZ隌텣kńq),и-#>MU@S;`p6VJ OèRfbҮUϷU߾MbՇ6wwCe)Cg x%~\TR& Gb'V]"Uc+Oi?[XDBE)N;>|bAkWy)W'yMQg݂x T21Eu ,cQXT21E ,#Q˘T22j|) ,c#@ 0%ʽMQA@ 2#%T%t4Ѕމ=Nƒet%)!>D@ - 8ggg,--iԨ:mvMΝ͍ѣGs m74oޜK>@g<K SBMi\}ׯ_O=prrg߾}beeU&22ɶmXhw&((J,X صkC aĉ̟?!;A  @`J9+997xxu?Yfl޼R4hЀ@V^HHH`̛7)Sн{w2223gǏI/hz@ =X| 999L>69rW_}ĸܹ3M4_~))ۺu+ѣGlccǎ*u0@ 0%Z*ap)ڴi[ܾ}'O秷~VJۺ$bP S؃%6+!99w2l0ر#̘1'O%R퓌0@ 0%ZM^^wSN[ow^o ,@ L rAZR]isΝ;G~tcJ233Ug{'Nkw]T*...U*3"K S8.6/6JwצM4]BA˖-[?>>X7npuvN'a` @??@DDNvtؑu֡V7FGGs{P(Xv֬YmgIG  @`JU9{fРA̜9FS>sLKa}GӇ_|z4f̘AVtR2hтc:t`׮]\sRfMi" ,@ 駟gŤŤI +iSO}vf͚3<-|'XXXl/ˋ%KJ Xx1o#CX&JjBwuFC.^}C 5uY sR@rHNe֢d , (hޞm{| 1{)zoebah6ǢTVLSv]t}^6GBr48*ۚAoy1qIGο̙]UNn~]OÔ1J@s;_Pu 4,[a?{Nej}bA'+Rm`4Ah+\VV} }dԔB)0g t-m >#*F@>My1']F^Tk_\漍M6:ydvPx%V8StCp>&COq6*4 s|iE7P9rd]ˣ5eXZɪx/{f}M޽ݻ}wgnnNXX2AR_eS 27 9h)їu^ktmԢA\}-A(R ^Xtdpe1h(dQaLwO6GϛA].l Zv~_cab= ś\b3 |9c˒.tci̔o8>\{bMA#!x.\oCqdv@MKq1h 3UT)0kXu Cet8tQش6.K]|&?J{B^XS"t4 6j5MSj@ReMyOrdq ?7ɍ N! ad0^{9hrH3'+qp&sG0aSoo @A9wiYMǰ=NCv.<DzMCaD M_bBЊсFϐ>sXHޫwRc`\B(Y& {qQ95<ߺ`n)] pejlO k\ֆд+J3qruZEc{4I%6AT"]Z%;e35s4rU1%}'Y V  l#$ISEOkX WS墤2s(,9q8ln9&q#dʚ86qULqٕx)fJS^r QC&s wҍҨդDIe9i?o6%8ZL],K}U,j5ξIAA#4ZXpM_p,JZAPWu5ԭ s& C ! ,lH˂ K./N0dXWㄗQ%Ɣ(ʸIaZ&7&,BVT'-d_m9ۗbնɽߔ5Ƚ$'b8ng䓝s]7Y?%3Q_7qZMS ]В}+/L8 ^BԈ@-qv[C;'Kd V{PU9-r{E (u@n5/X[^2\[J7T7Y?=>r=oc$ |[jԴoê= #(jESddLdLccZyRIO)2Iy%cST@-;P;}}z׻c] ][OF(:>WN&CKKi]72[ sY dC4 2 >h"Kس"8`}u' 5  nV8|yi 'WJX0b.DZp&sjC02 nW{DjcnI9j]yi ĴϺ''Y!X Kfȡغ + gLOވĎؼ:V;r8U<ǖ|V qxRFe֣iӈHl⏿L gg=~o G2.C'x푯h߭M1,Scwt&x0[seZ'D])߼%sKe]Rĵu=ʉ{>^`<9w0A)}ڻ*~r:-~=rgsOZAgo[=;v PN{>@woѮS#[!ƻt9UQ M|ϩW|>9,҆@UZr/1Su'x}G(m~2u/eq3{4H=25{X2^O V}j,b['c]lgohL/5qމrl,BC@h쓓܋An$o/c}ڰ/q뿁C|L]n8q7z/總9Xذ^=4oSk?-!Ǭ 869[91 g|O|S/ƫ !忩;'VA[-4 yF.c|n|>)`!GP;s1%eD6cj"t'0їv;'_ʂ~}5&|p+@4p uρNT?u'0LX1׽v# x-{< oK 8f@Lmc" qET-fN}{ܓ#. BxplDZ" 9,)K#Uc' x|8фEN阣u RɤA e?NبSrV@Sޫ0GtNv/8zr߂ n9k!3JN@l+HM1!A60^s}8ȱ89KHIqβ,.Y<Q4aVp[0-( 2]Sc;&_OǏ.&$<_vnn=_r X^gjObb"CfFJLf ujeA [$CۭH5uzs-uj]3CxJ^#ǵמa3P½xٶK$KvzPDDD0,DDDD S1LKDDD0Yi>u~?Y}݃#Y ">3u(` sund-u~sCFEDDJ9PVm7)`(`JnSI JKKuSg:~|&Q/*+{UiH{F1+?2H[o3g潢fLozL3ԖXm]k&`%%%xJ",oS۷0lX- '~Ur6z΍ާs4iǎtҥZWj 99_XBCC}9Ol߾ P7"l 22C""b7XW$@KDDįh@KDDįh!P+q2=KF[\nV"##q:4k֌믿?׹;vp0{lOO^zAXX]t1j+p8eĈhт0nvUnYYG&**0vʚ5kr=A&M4h}]z"<~8F\{ո=y-ض61}{>.Q񱤤$VZŨQˣdz'MĐ!CX`o&'Nk׮lڴS/'']rAfΜInn.O?4Ş:+VgϞŻK-۷/ofkѤIy^x Xγ>C=DNNÆ cРA:tJJJݻ7+Wd,\/AZT{9?СC8p >뮻Xj爊:kEGB&,Eħgwն,˶, ;ud?SCqD׽6m%%%JSNvLLl֬YeYQ?}t۲,{ΝmMl˲͛7϶,NMM=0c ۲,{ҥy۲,{ٞ &ؖeya۲,'v{W˗/-˲WX){ᇫ' yyy'gxۆm˼hhKZlʕ+Yv-ߟݻw3aسg`ذa{@zAnn.[lo巿-Nk;v5kpw),!CPPP-[ӯ_j+(((`5FwN[D-_HzUSX3UnT3TШC z&D7 Nٷo hݺu:tmzZyxxx+//Ve˖55k֬ξT)**E5k+EDLSCU5 E8?*dYiӦ8Oh;UUYU=t***jꫯj߿}󺶈)FME|l޽W okh׮qqq̚5 aaa$%%;pqOyee%s!&&mѣGΝ[ޣٓ".]Zjӽ{w.\xN8"e>4%cзo_ `ݺudddr(N3fOVVO~۷gذaL2I=ظq#SL!"">7{wg}C2ydXd .<;v4iNnݺi&NJƍϺ{mLzꫯsHVC,;v,aaadffr-ЧOyz͚5k<ƌ[oźu|2 yiժ&yGNtt4\s5dggi`XZ 0/)SЮ];̙drqP+U[nv/fҥdeey6p޽;;w3$0TX̰vPZ&I"1Y%$cc<ϱq{Wu[s~ο9I IhmʶG Pζl?vƲ}~ck^^|ƥ)L}==cWKG#ݙmۍd@5m[#?kiMFtxld 2kj޴%34}' f?:zxK;d@gŬع$'3"mO3e@8@oTz9jXة#g=>7d\qQ` ɫl#c}1}/?_I1O3WbPC#v?_3UG!lu\y:Cx#G -2nK3W`N!ȀjiU7VF'K/{ӖV:)T(7[PRl`b Kײ0ѯ[? M1U%GS""w%wu X_6Uғ5JʚȀ.V5c`UCYu܈QC+>Ȃ;L4_ӵZ"?O/e`ʘƨ+fU6\%d@=q#͜LYow@KM!%V)GcF2\QIgz͵j#P3SnpL ๧s3' @ @ 22 @ @ D`)~tpz摯>?K@ {ѯޗWZlկymZkӚ<Γok5Wi}mx .OU8^ѳntgYk~L@ H?Og^3YX,[w_lmӟV0o,Jv+q:>Uwxŗ] U9'}0a,fbF,fbFYKOBJ8S~uEP=#.|ٛ6xF`;M[/EE+X,-^{@Kfʦ?=>/gZcox1Ǔ/w]#_o'f獙A@g|Z˃U׏lm@t_IcUxLzQO̔Lbԓd@|3Wi`,"-]qEz{qbc-; 0,;vB$X҇ WqKAxҢj4,~ `*5nTG,]>k]u5 d@='97 _.WUQKKv5|gu֪..c3c @ 'bX16E  |i[bh]>U (ekm}!~6a}2@ *9F1Sl3a0~.?F@ YcR5H]t63d@-KK9F 5K9^ݟ=1a0)22 @ @ 2Mս'#qiXF Cp{VCpG쓏 ˦l1 Mg~f`Lfۘa 8іL+gܝm7Ȁ,ٖ~t_<5',emifɲiT@ N=;an_zyz+3h e7Ȁ񙩯BYXצ?l8ýL3ud N1}nɓ7k ;}[if6glۑ)@=#i(Bm˃x${}J,pba5]XʆQk,q`43;$:8bqy*kGZ|mG*?bi0 !{*T9->Zf`S`*3ŋ y[HcQ?aLYbvef`8ĬH40id؎47WW{ `aqB=X$L*{c̐K_o`aqt%+ۏ,fKKՈiKd[x o?6ܯT5o"O9).:.red[$r$9tlЪҏl,-^GV9- 8*>K*7o zK Snߖfb~eTRGPY\Q3c ϞT`l߲*a0xrUiU1ge5;F~ekY5@uB$/O S*W_9-02E!jeb;|*  Xzj$ EU ˄~!cTceђF_Alջ/qd EU ˆ5l@K$ySBX,;CMJ_u)ȠE!4SNjabl TC}+N*CAPTVKU%`XDϸa`TJﺲ-ۜ{Iy4REoܷlacI mٶ%ZlSyx*ثO~` 0Շ=mU8n"( Zxĕ7 1U2 Xʴ(ЗƓ&l-tߔgj7 %4+x_5J3_Kc?T`&T1U!dMvaߪ=@ z^ǒ[$ѽ@֬p2WX2ݚf2t88 OeD2Cu8E!ņgX^d+wO@6ԐAifȠE;&TWEEȪf)Tmx@ ~Ow j@tQv/VUd@O1cU%MdUlz@ ~LfX !beCٶ3ct"],S_5M$WZ@}̎e #U!{Z@RT*?VzF3[ЉN[nدWji{eJ aQfȠܯLZa*[ԋ 2;ͤvΊ mVde\Ջ@ V1:W]7],fvNr[Sz2n* MW62N~Ƞo41a*~n`@}#jv:yodHf`AXqq˶ ]RvٲJYU`f`E۞ff1=êTnJ 3d zDb/k - MifhPE|~ȪYb3d A*Bf @ )PeSBVzB &,A U4dZ2<̨@}  zLaM$d A1`YՍSE@=c,Ydy Z*fYȀZal*iuAo&RHf AO!cty2 + TVVfK APO?F 2 n ΢&2ȠGXqbVɩTmƫr~`mLfT detL` v 02XQc qPl¾UZTLO (r% #%k2XqJv$K 06,W}@tr%adpHf7~)q$`EXuk*Y V@3L c Aϊ+͎+&sW`Er彆q(_2H_^2@˩ff#& }$dCdJ zQ00$6}ճ 2XVdalԝ=@ & =$PB}9 ^3c AOِm{ C$f._p@ˡ̐\ d rM`40^S=9f R#8ꭋ4z ~D*&c"y hRp~ 512X@ K2X!ͤ V@ƠusqF:l瑤5c\*>WzYDf`imGmm _7m +s|}w*תK 5؟v +,mc3dsN5vr lm~HZ|K ,YE9=)`76P5;mJ_O/}hzȮYsH'Ξǃ%Yb?3dtE#-+mӬm=^ڸw޴/|tȑ<M=~k=XgGڽ-m*n ]3A ~q7񗧫־Ĉ"8}YOfky ciݺu |}_H#vk-D4Ӊv ʅ];륣֞zaEs͘55Bىgc~GdXޟLNN*YiO / 1c6eC jdͦ]FS Ya, eKw߽|˛-ZޜHڍto͚E[w9'{< mYp_;e4sgY1?4e!uFw77q fʨW5E0,f^{u/}{|lΞo,exsZ3c  90r zڣO?6=,\,ޢToy/6lv(쏥K.Ys{Kx܇>84Y|^\ C id0F_i\tr5ȂQ0bv,넱oܚfeLg﹧y/Y̿ױ=c,$k. d0qK$\*e R 3\^{]0kfz<5{{&'c]J'˕X#*?϶{/0r#O}r7gr?sK:V<8VzP,ܳ-'d@'DzUۡ>ӷN'2 Xc'/b?0,?eV~vfk}V,BهȀfw˟'ȀeWZ^x}@,"B3?0 ֞JK Xv2Lی3 2_},ɠݛۛ?pUWק F 0|*fǢjdXm̶WyW}}!}@f[n]z;ߕN?56ѣó+ۚv[wq[D !}i{\tBqؔm7^}%קBW|[ޛ:@<xv"Ś>p{kG>"٥ڱ|6 X13ӿgaluȑVh`ѻ>Қ%[zC A0lN#}dby޹[7HCcߝfؘﻥO~/ 3a,|3_"m_V8`f cBٛAqoʶ~vXH357`׿! W^pmX i ܆LW}I|o~ޫF,xs֍ď> cWX=,lX,m٧H33Fs3^4SӬo϶guW/E% 3l2 qo_0"ljŌ]ע,9 7C\19,[TbѬm_1 { 3d7d/۹@ hu& `|eb*f[PlkEMBXM:{w߽}#D|\U|E{ߗG6hEe,1yX/?X*?]yۿw,;8O So-edh~{P)(4bY_fY3 {928O;٦ 9t2ת1<|]37-ܛ%K萘Q*N|;u :fp`vq7MoNmvd#5;b 3nہڴF뤿k>@\(ܿo_e?쟣 sDZZg :uYxD{b,W>)fǖFѓUgz16{th:-\5_$^,(#([7t]ZwY,t>b.Q󄍑Yf>ief mtЯ;5Պ1U%PE~{~|};3ڳ8Ēmx^ G,wq-ޕ1[<2?>By^ԟզ/7dyld1t׿~y|̽&3X{[RڠvS |iCv\ܯ2jȢ,f"h=\ K?ߖ<5s7俨L A/O>V-GWuPYد1eѸp jՊqn6!-f~)fn4<'*/ՙ=Xna9~wqM<t0;Ƞ"D^`~{d{;l_k}E&N8ߔayX|˛|żlL7pg͙̃Xv$Ȁ΄(Z"pE`j-jBlu/f^mʼnh|~W4 XnXr|Sy{qN #kRQԦWJqWv,>^DxgǞ=yP ([fz޾ESY,S9zdP`ʻ`AcsWʶ d0 bIS]#,b3PvIUy,5٧ٶo/XWtWlWhSsxbcAcưtM4-U)Ďp&Pq6N( QqQ2a[[?_Y[flJ@UFz/irNyNK5Bw}E"`FW+xE' :k} cы,>F`3nо[ ͶPu/Ms<6Xu'+󕵧uY N k SC@WE["7XcaN!%lk 0E~[~x]y؝?G6i+oΏqoG[y3q6{L pu+c{(Gڒk$X % 񻷷Xoy{Zl?}k:qfoՍ+,nK3va0Ǎ=N\c_ϿcoZB [Wy(K^Ӑ^a)l6It4;wݺ ١lg~Rm iʓJw*x=h jiwرT-0s#ߚİXVJCWb6e2/l_ K栨@ 22 @ L?~Q 4,n d|(@ `@ `m399g>ui޽F 2`:n>`-ٺuի2'~CLD([ȕW^>{d􉉯y(7>&|&oy3e77;PUF̪Q5%TYf_grriBX70>:%O{&@ cMbytH<ʮ?p_n-Q,YXly3\yUi+8#e!+?L~\w!`w~'n輴S=K4>h`{lY0ѻ>1moow]b9ٕW@aeSWzɠ3CB&|mY]i2C6vc]m'WeK}}a"7k7P iO6R-ϴCjdqѠDد}7ōvo=mw9rP~8?A:^ޓ裭%̹LV,/u{{Aw!kO=e.g z&oXl7"ζXwsqzE64ھ>f^{{ny WmF'> :!jܲ^:2h evٿNW}`E=p{WGafjbc7m6]FGG xE}{߱o6?o޲yǒ%eM>cϜ3ekOuMѧIhl񷄱JnM33_k !ޛ(ȿKz3Lʊ͟_w۪)+?w#?+^:2jKWi͘/!,f>>9"Bm /0Mưmի_bX+7ߐר"=J2(tdĐ%\KQSe_/]3j-ur5Ӊagk_M'M/7+>ya$C?:Wsv0>l}^$v(K1),_œ 94,Ɇ*IǗnn?F$6j_J3d7߆}7 #u3X[PmN䛫]f[WW/Uk滂hWa44 ;KPA=:V,W^s2s,yn4 S XmVyw04̐ X&0  ɩ  P%K˦ PR#b2:jb@6YS27R1`9#%2 deus+}2aT%U2D 5g !#53i@ ~;rB`b2K A&dCJ`;%h@dyTXmO{ŬW[,kPX[+߲Kcr%t2輘ЭN3uc٫@׹Y9,/贪uj(=mj`>Tn(х}`Eh}A'm˷nvUj΢vlK@/ㆁڙj*TUR}Cwʶ 0f J_Rc{}oHZ]I :cKUVS# @_h$K:,X* }W~I1,U]VyWx]ɭ#d)SeNN6M]p)UU}@_Pl,T*>3 $]#v1,ctD;R =҂1hұTEWX0܂*m.Fkw* V\6"\[Serc xaZg,[:ϪU oDU1V\UٔoMjkG2s v8xՙJ BD#U]}2-~`%Iͩ^a#Uh ދz0⤩l8YUaYz%fL6 f0.UO GǕ[00ƒىAKUc(/)ѶTLfY8 ?)6 ԥʲ/wq0P6XvF.U%I٪BzfTY"~dN^تo)7)Ir ԩ+f5Rz`VXU]zT hzKVb)pv U-U@I10 })U~cEXC=clR76^69)7cz]Vq:ˇrmIEZ6Ja4+_Jۉu`ےPx__L3dvzbܧcՍ@Խ+h[' U@t0lsEX+S9^{T70t5[S^^lUݚK@ kO_uk Ug㚩~0]7:6jWH6ReQ` kexΥ. h$˖\F@ e~ c~W1lXDžF5%ecu\5X-?Pe0FlMۄ1]N] cuRk VP1˖ꄱc#+ bٲa(l- cu~j0+N Ò4S̨ VTf̌߱TϨ c04n~qec`/ WU7uLanc1qŌ k[c7|M5Rge AaXz[1ONj"hḒ͢f TraթU3@p+T^lsFIvb*f6x͚_wfx^W/p!\q6ǹې̬vc7=)Dq*6aT~v7fۤȠƲ<5V]~A/:gM:ܵFOv6 bC׾}"M%bQkvMR/d0vk|SʌƐz3iO2fԋb,m8Sګ"۩u?/uǞ}4?N5PMqe7ڤ(}!crO|Ҁy!v7>􋣝.v 7=зɫOo7}dvE w g@KwϿA,Z(6tZss,lG}f"Җ*L@ dh۷ךE9qUww]£Q2X@$ѿM?,çljM ,߽7{5h}lfaD@ IDtۮIq&-zصq# vKIgmzPvƧM)yX|1EQ;#ϸ9 dd@EX,W\ @ @ 22 @ @ 2TCo>;Lǜs)N?kq8VX j}sw>qOgQgg=#KZ9kӋ]S)"98Ti~HGV4>/]@nt6:{5;ۓrο}>D,Ey .H#?{ْV|䭥Q1. 5>//=>\ d0"@q٥]l;;.g,BNxp.Yl߾ 0uu乿 s8o/+o̐d0"u"@,ҏ<;WlK cb Yg%,[-ilcDٹNRvڎVÐ5t:rBbF̌L߹ol3:9 ib&+ crb 2w?Sλlߣ{o 2`E{5%'8ˎ"UʰXc̐S;@M7K>NqY+!j"FH? y1ˆ1Cvcf}p~{gQeUgDWU?Ēѯ}UKWLW1CVkEqF=%_k56e{[gȀ>tޖҾw}5Ve 3,c2Bf[ad0ĢCzbE},ŌRtyھ[uh{Z0μZcW s@7!NPˆ%*0Ԍ}"θK|E+ ȀPˆ0Vq#^ SMp2V!ZaU*]D^Tˆ6= [aD2gX0aV@(d_uZ2 VcY0mV }|A?ྱ,.fW/ΔU7 /0>3NO=X}>"X ̬Wo1]gn+ˢq%f'l5\)fg_j j`YZj e\\O|qj#{&eBfw"Ȁn8@e.+?(fY[r5[lX^m?nD5F2"tEV.0d@@  ddɸ4dr&F VН_z8=a#@+d*xwM}h 7BkPs߻> ʘζ}[>=ۿ ZN5dwd/=|kmZu##?{YZ ҋ]ct`LeۍٶCڔmϛ:}KI':=뭚F` to support native Python object serialization and add some additional commands. When multiple engines are started, parallel and distributed computing becomes possible. ### IPython controller The IPython controller processes provide an interface for working with a set of engines. At a general level, the controller is a collection of processes to which IPython engines and clients can connect. The controller is composed of a {class}`Hub` and a collection of {class}`Schedulers`. These Schedulers are typically run in separate processes on the same machine as the Hub. The controller also provides a single point of contact for users who wish to access the engines connected to the controller. There are different ways of working with a controller. In IPython, all of these models are implemented via the {meth}`.View.apply` method, after constructing {class}`.View` objects to represent subsets of engines. The two primary models for interacting with engines are: - A **Direct** interface, where engines are addressed explicitly - A **LoadBalanced** interface, where the Scheduler is entrusted with assigning work to appropriate engines Advanced users can readily extend the View models to enable other styles of parallelism. ```{note} A single controller and set of engines can be used with multiple models simultaneously. This opens the door for lots of interesting things. ``` #### The Hub The center of an IPython cluster is the Hub. This is the process that keeps track of engine connections, schedulers, clients, as well as all task requests and results. The primary role of the Hub is to facilitate queries of the cluster state, and minimize the necessary information required to establish the many connections involved in connecting new clients and engines. #### Schedulers All actions that can be performed on the engine go through a Scheduler. While the engines themselves block when user code is run, the schedulers hide that from the user to provide a fully asynchronous interface to a set of engines. ### IPython client and views There is one primary object, the {class}`~.parallel.Client`, for connecting to a cluster. For each execution model, there is a corresponding {class}`~.parallel.View`. These views allow users to interact with a set of engines through the interface. Here are the two default views: - The {class}`DirectView` class for explicit addressing. - The {class}`LoadBalancedView` class for destination-agnostic scheduling. ## Getting Started To use IPython for parallel computing, you need to start one instance of the controller and one or more instances of the engine. Initially, it is best to start a controller and engines on a single host using the {command}`ipcluster` command. To start a controller and 4 engines on your local machine: To launch a cluster: ```ipython In [1]: import ipyparallel as ipp In [2]: cluster = ipp.Cluster(n=4) In [3]: await cluster.start_cluster() # or cluster.start_cluster_sync() without await ``` ```{note} Most Cluster methods are async, and all async cluster methods have a blocking version with a `_sync` suffix, e.g. `await cluster.start_cluster()` and `cluster.start_cluster_sync()` ``` You can also launch clusters at the command-line with: ``` $ ipcluster start -n 4 ``` which is equivalent to `ipp.Cluster(n=4, cluster_id="")` and connect to the already-running cluster with {meth}`.Cluster.from_file` ```python cluster = ipp.Cluster.from_file() ``` For a convenient one-liner to start a cluster and connect a client, use {meth}`~.Cluster.start_and_connect_sync`: ```ipython In [1]: import ipyparallel as ipp In [2]: rc = ipp.Cluster(n=4).start_and_connect_sync() ``` More details about starting the IPython controller and engines can be found {ref}`here `. Once you have a handle on a cluster, you can connect a client. To make sure everything is working correctly, try the following commands: ```ipython In [2]: rc = cluster.connect_client_sync() In [3]: rc.wait_for_engines(n=4) In [4]: rc.ids Out[4]: [0, 1, 2, 3] In [5]: rc[:].apply_sync(lambda: "Hello, World") Out[5]: [ 'Hello, World', 'Hello, World', 'Hello, World', 'Hello, World' ] ``` When a client is created with no arguments, the client tries to find the corresponding JSON file in the local `~/.ipython/profile_default/security` directory. Or if you specified a profile, you can use that with the Client. This should cover most cases: ```ipython In [2]: cluster = ipp.Cluster.from_file(profile="myprofile", cluster_id="...") In [3]: rc = cluster.connect_client_sync() ``` If you have put the JSON file in a different location or it has a different name, create the Cluster object like this: ```ipython In [2]: cluster = ipp.Cluster.from_file('/path/to/my/cluster-.json') ``` Remember, a client needs to be able to see the Hub's ports to connect. So if the controller and client are on different machines, you may need to use an ssh server to tunnel access to that machine, in which case you would connect with: ```ipython In [2]: c = ipp.Client('/path/to/my/ipcontroller-client.json', sshserver='me@myhub.example.com') ``` Where 'myhub.example.com' is the url or hostname of the machine on which the Hub process is running (or another machine that has direct access to the Hub's ports). The SSH server may already be specified in ipcontroller-client.json, if the controller was instructed at its launch time. ### Cluster as context manager The {class}`~.ipyparallel.Cluster` and {class}`~.ipyparallel.Client` classes can be used as context managers for easier cleanup of resources. - Entering a `Cluster` context 1. starts the cluster 2. waits for engines to be ready 3. connects a client 4. returns the client - Exiting a `Client` context closes the client's socket connections to the cluster. - Exiting a `Cluster` context shuts down all of the cluster's resources. If you know you won't need your cluster anymore after you use it, use of these context managers is encouraged. For example: ```python import ipyparallel as ipp # start cluster, connect client with ipp.Cluster(n=4) as rc: e_all = rc[:] ar = e_all.apply_sync(task) ar.wait_interactive() results = ar.get() # have results, cluster is shutdown ``` You are now ready to learn more about the {ref}`Direct ` and {ref}`LoadBalanced ` interfaces to the controller. [zeromq]: https://zeromq.org/ ipyparallel-7.1.0/docs/source/tutorial/magics.md000066400000000000000000000226641412533144600217320ustar00rootroot00000000000000(parallel-magics)= # Parallel Magic Commands We provide a few IPython magic commands that make it a bit more pleasant to execute Python commands on the engines interactively. These are mainly shortcuts to {meth}`.DirectView.execute` and {meth}`.AsyncResult.display_outputs` methods respectively. These magics will automatically become available when you create a Client: ```ipython In [1]: import ipyparallel as ipp In [2]: rc = ipp.Client() ``` The initially active View will have attributes `targets='all', block=True`, which is a blocking view of all engines, evaluated at request time (adding/removing engines will change where this view's tasks will run). ## The Magics ### %px The %px magic executes a single Python command on the engines specified by the {attr}`targets` attribute of the {class}`DirectView` instance: ```ipython # import numpy here and everywhere In [25]: with rc[:].sync_imports(): ....: import numpy importing numpy on engine(s) In [27]: %px a = numpy.random.rand(2,2) Parallel execution on engines: [0, 1, 2, 3] In [28]: %px numpy.linalg.eigvals(a) Parallel execution on engines: [0, 1, 2, 3] Out [0:68]: array([ 0.77120707, -0.19448286]) Out [1:68]: array([ 1.10815921, 0.05110369]) Out [2:68]: array([ 0.74625527, -0.37475081]) Out [3:68]: array([ 0.72931905, 0.07159743]) In [29]: %px print 'hi' Parallel execution on engine(s): all [stdout:0] hi [stdout:1] hi [stdout:2] hi [stdout:3] hi ``` Since engines are IPython as well, you can even run magics remotely: ```ipython In [28]: %px %pylab inline Parallel execution on engine(s): all [stdout:0] Populating the interactive namespace from numpy and matplotlib [stdout:1] Populating the interactive namespace from numpy and matplotlib [stdout:2] Populating the interactive namespace from numpy and matplotlib [stdout:3] Populating the interactive namespace from numpy and matplotlib ``` And once in pylab mode with the inline backend, you can make plots and they will be displayed in your frontend if it supports the inline figures (e.g. notebook or qtconsole): ```ipython In [40]: %px plot(rand(100)) Parallel execution on engine(s): all Out[0:79]: [] Out[1:79]: [] Out[2:79]: [] Out[3:79]: [] ``` ### %%px Cell Magic \%%px can be used as a Cell Magic, which accepts some arguments for controlling the execution. #### Targets and Blocking \%%px accepts `--targets` for controlling which engines on which to run, and `--[no]block` for specifying the blocking behavior of this cell, independent of the defaults for the View. ```ipython In [6]: %%px --targets ::2 ...: print "I am even" ...: Parallel execution on engine(s): [0, 2] [stdout:0] I am even [stdout:2] I am even In [7]: %%px --targets 1 ...: print "I am number 1" ...: Parallel execution on engine(s): 1 I am number 1 In [8]: %%px ...: print "still 'all' by default" ...: Parallel execution on engine(s): all [stdout:0] still 'all' by default [stdout:1] still 'all' by default [stdout:2] still 'all' by default [stdout:3] still 'all' by default In [9]: %%px --noblock ...: import time ...: time.sleep(1) ...: time.time() ...: Async parallel execution on engine(s): all Out[9]: In [10]: %pxresult Out[0:12]: 1339454561.069116 Out[1:10]: 1339454561.076752 Out[2:12]: 1339454561.072837 Out[3:10]: 1339454561.066665 ``` ```{seealso} {ref}`pxconfig` accepts these same arguments for changing the _default_ values of targets/blocking for the active View. ``` #### Output Display \%%px also accepts a `--group-outputs` argument, which adjusts how the outputs of multiple engines are presented. ```{seealso} {meth}`.AsyncResult.display_outputs` for the grouping options. ``` ```ipython In [50]: %%px --block --group-outputs=engine ....: import numpy as np ....: A = np.random.random((2,2)) ....: ev = numpy.linalg.eigvals(A) ....: print ev ....: ev.max() ....: Parallel execution on engine(s): all [stdout:0] [ 0.60640442 0.95919621] Out [0:73]: 0.9591962130899806 [stdout:1] [ 0.38501813 1.29430871] Out [1:73]: 1.2943087091452372 [stdout:2] [-0.85925141 0.9387692 ] Out [2:73]: 0.93876920456230284 [stdout:3] [ 0.37998269 1.24218246] Out [3:73]: 1.2421824618493817 ``` ### %pxresult If you are using %px in non-blocking mode, you won't get output. You can use %pxresult to display the outputs of the latest command, as is done when %px is blocking: ```ipython In [39]: dv.block = False In [40]: %px print 'hi' Async parallel execution on engine(s): all In [41]: %pxresult [stdout:0] hi [stdout:1] hi [stdout:2] hi [stdout:3] hi ``` \%pxresult calls {meth}`.AsyncResult.display_outputs` on the most recent request. It accepts the same output-grouping arguments as %%px, so you can use it to view a result in different ways. ### %autopx The %autopx magic switches to a mode where everything you type is executed on the engines until you do %autopx again. ```ipython In [30]: dv.block=True In [31]: %autopx %autopx enabled In [32]: max_evals = [] In [33]: for i in range(100): ....: a = numpy.random.rand(10,10) ....: a = a+a.transpose() ....: evals = numpy.linalg.eigvals(a) ....: max_evals.append(evals[0].real) ....: In [34]: print "Average max eigenvalue is: %f" % (sum(max_evals)/len(max_evals)) [stdout:0] Average max eigenvalue is: 10.193101 [stdout:1] Average max eigenvalue is: 10.064508 [stdout:2] Average max eigenvalue is: 10.055724 [stdout:3] Average max eigenvalue is: 10.086876 In [35]: %autopx Auto Parallel Disabled ``` (pxconfig)= ### %pxconfig The default targets and blocking behavior for the magics are governed by the {attr}`block` and {attr}`targets` attribute of the active View. If you have a handle for the view, you can set these attributes directly, but if you don't, you can change them with the %pxconfig magic: ```ipython In [3]: %pxconfig --block In [5]: %px print 'hi' Parallel execution on engine(s): all [stdout:0] hi [stdout:1] hi [stdout:2] hi [stdout:3] hi In [6]: %pxconfig --targets ::2 In [7]: %px print 'hi' Parallel execution on engine(s): [0, 2] [stdout:0] hi [stdout:2] hi In [8]: %pxconfig --noblock In [9]: %px print 'are you there?' Async parallel execution on engine(s): [0, 2] Out[9]: In [10]: %pxresult [stdout:0] are you there? [stdout:2] are you there? ``` ## Multiple Active Views The parallel magics are associated with a particular {class}`~.DirectView` object. You can change the active view by calling the {meth}`~.DirectView.activate` method on any view. ```ipython In [11]: even = rc[::2] In [12]: even.activate() In [13]: %px print 'hi' Async parallel execution on engine(s): [0, 2] Out[13]: In [14]: even.block = True In [15]: %px print 'hi' Parallel execution on engine(s): [0, 2] [stdout:0] hi [stdout:2] hi ``` When activating a View, you can also specify a _suffix_, so that a whole different set of magics are associated with that view, without replacing the existing ones. ```ipython # restore the original DirecView to the base %px magics In [16]: rc.activate() Out[16]: In [17]: even.activate('_even') In [18]: %px print 'hi all' Parallel execution on engine(s): all [stdout:0] hi all [stdout:1] hi all [stdout:2] hi all [stdout:3] hi all In [19]: %px_even print "We aren't odd!" Parallel execution on engine(s): [0, 2] [stdout:0] We aren't odd! [stdout:2] We aren't odd! ``` This suffix is applied to the end of all magics, e.g. %autopx_even, %pxresult_even, etc. For convenience, the {class}`~.Client` has a {meth}`~.Client.activate` method as well, which creates a DirectView with block=True, activates it, and returns the new View. The initial magics registered when you create a client are the result of a call to {meth}`rc.activate` with default args. ## Engines as Kernels Engines are really the same object as the Kernels used elsewhere in IPython, with the minor exception that engines connect to a controller, while regular kernels bind their sockets, listening for connections from a QtConsole or other frontends. Sometimes for debugging or inspection purposes, you would like a QtConsole connected to an engine for more direct interaction. You can do this by first instructing the Engine to _also_ bind its kernel, to listen for connections: ```ipython In [50]: %px import ipyparallel as ipp; ipp.bind_kernel() ``` Then, if your engines are local, you can start a qtconsole right on the engine(s): ```ipython In [51]: %px %qtconsole ``` Careful with this one, because if your view is of 16 engines it will start 16 QtConsoles! Or you can view the connection info and work out the right way to connect to the engines, depending on where they live and where you are: ```ipython In [51]: %px %connect_info Parallel execution on engine(s): all [stdout:0] { "stdin_port": 60387, "ip": "127.0.0.1", "hb_port": 50835, "key": "eee2dd69-7dd3-4340-bf3e-7e2e22a62542", "shell_port": 55328, "iopub_port": 58264 } Paste the above JSON into a file, and connect with: $> ipython --existing or, if you are local, you can connect with: $> ipython --existing kernel-60125.json or even just: $> ipython --existing if this is the most recent IPython session you have started. [stdout:1] { "stdin_port": 61869, ... ``` ```{note} `%qtconsole` will call {func}`bind_kernel` on an engine if it hasn't been done already, so you can often skip that first step. ``` ipyparallel-7.1.0/docs/source/tutorial/process.md000066400000000000000000000733631412533144600221470ustar00rootroot00000000000000(parallel-process)= # Starting the IPython controller and engines To use IPython for parallel computing, you need to start an IPython cluster. This includes one instance of the controller and one or more instances of the engine. The controller and each engine can run on different machines or on the same machine. Any mechanism for starting processes that can communicate over the network Broadly speaking, there are two ways of going about starting a controller and engines: - As managed processes, using {class}`.Cluster` objects. This includes via the {command}`ipcluster` command. - In a more manual way using the {command}`ipcontroller` and {command}`ipengine` commands directly. Starting with IPython Parallel 7, some features are only available when using the `Cluster` API, so this is encouraged. Such features include collective interrupts, signaling support, restart APIs, and improved crash handling. This document describes both of these methods. We recommend that new users start with the `Cluster` API within scripts or notebooks. ## General considerations Before delving into the details about how you can start a controller and engines using the various methods, we outline some of the general issues that come up when starting the controller and engines. These things come up no matter which method you use to start your IPython cluster. If you are running engines on multiple machines, you will likely need to instruct the controller to listen for connections on an external interface. This can be done by specifying the `ip` argument on the command-line, or the `IPController.ip` configurable in {file}`ipcontroller_config.py`, or the `controller_ip` argument when creating a Cluster: If your machines are on a trusted network, you can safely instruct the controller to listen on all interfaces with: ```python cluster = ipp.Cluster(controller_ip='*') ``` or ``` $> ipcontroller --ip="*" ``` Or you can set the same behavior as the default by adding the following line to your {file}`ipcontroller_config.py`: ```python c.IPController.ip = '*' # c.IPController.location = 'controllerhost.tld' ``` ```{note} `--ip=*` instructs ZeroMQ to listen on all interfaces, but it does not contain the IP needed for engines / clients to know where the controller is. This can be specified with the `--location` argument, such as `--location=10.0.0.1`, or `--location=server.local`, the specific IP address or hostname of the controller, as seen from engines and/or clients. IPython uses `socket.gethostname()` for this value by default, but it may not always be the right value. Check the `location` field in your connection files if you are having connection trouble. ``` ```{versionchanged} 6.1 Support hostnames in location, in addition to ip addresses. ``` ```{note} Due to the lack of security in ZeroMQ, the controller will only listen for connections on localhost by default. If you see Timeout errors on engines or clients, then the first thing you should check is the ip address the controller is listening on, and make sure that it is visible from the timing out machine. ``` ```{seealso} Our [notes](security) on security in the new parallel computing code. ``` Let's say that you want to start the controller on `host0` and engines on hosts `host1`-`hostn`. The following steps are then required: 1. Start the controller on `host0` by running {command}`ipcontroller` on `host0`. The controller must be instructed to listen on an interface visible to the engine machines, via the `ip` command-line argument or `IPController.ip` in {file}`ipcontroller_config.py`. 2. Make the JSON file ({file}`ipcontroller-engine.json`) created by the controller on `host0` available on hosts `host1`-`hostn`. 3. Start the engines on hosts `host1`-`hostn` by running {command}`ipengine`. This command may have to be told where the JSON file ({file}`ipcontroller-engine.json`) is located. At this point, the controller and engines will be connected. By default, the JSON files created by the controller are put into the {file}`$IPYTHONDIR/profile_default/security` directory. If the engines share a filesystem with the controller, step 2 can be skipped as the engines will automatically look at that location. The final step required to use the running controller from a client is to move the JSON file {file}`ipcontroller-client.json` from `host0` to any host where clients will be run. If these file are put into the {file}`IPYTHONDIR/profile_default/security` directory of the client's host, they will be found automatically. Otherwise, the full path to them has to be passed to the client's constructor. ## Managing `Clusters` The {class}`~.ipyparallel.Cluster` class provides a managed way of starting a controller and engines in the following situations: 1. When the controller and engines are all run on localhost. This is useful for testing or running on a multicore computer. 2. When engines are started using the {command}`mpiexec` command that comes with most MPI [^cite_mpi] implementations 3. When engines are started using the PBS [^cite_pbs] batch system (or other `qsub` systems, such as SGE). 4. When the controller is started on localhost and the engines are started on remote nodes using {command}`ssh`. Under the hood, `Cluster` objects ultimately launch {command}`ipcontroller` and {command}`ipengine` processes to perform the steps described above. The simplest way to use clusters requires no configuration, and will launch a controller and a number of engines on the local machine. For instance, to start one controller and 4 engines on localhost: ```python cluster = ipp.Cluster(n=4) cluster.start_cluster_sync() ``` ## Configuring an IPython cluster Cluster configurations are stored as `profiles`. You can create a new profile with: ``` $ ipython profile create --parallel --profile=myprofile ``` This will create the directory {file}`IPYTHONDIR/profile_myprofile`, and populate it with the default configuration files for the three IPython cluster commands. Once you edit those files, you can continue to call ipcluster/ipcontroller/ipengine with no arguments beyond `profile=myprofile`, and any configuration will be maintained. There is no limit to the number of profiles you can have, so you can maintain a profile for each of your common use cases. The default profile will be used whenever the profile argument is not specified, so edit {file}`IPYTHONDIR/profile_default/*_config.py` to represent your most common use case. The configuration files are loaded with commented-out settings and explanations, which should cover most of the available possibilities. Additionally, each profile can have many cluster instances at once, identified by a `cluster_id`. You can see running clusters with: ``` $> ipcluster list ``` ### Using various batch systems with {command}`ipcluster` {command}`ipcluster` has a notion of {class}`Launcher` that can start controllers and engines via some mechanism. Currently supported models include {command}`ssh`, {command}`mpiexec`, and PBS-style (Torque, SGE, LSF). In general, these are configured by the {attr}`Cluster.engine_set_launcher_class`, and {attr}`Cluster.controller_launcher_class` configurables, which can be the fully specified object name (e.g. `'ipyparallel.cluster.launcher.LocalControllerLauncher'`), but if you are using IPython's builtin launchers, you can specify a launcher by its short name, e.g: ```python c.IPClusterEngines.engine_launcher_class = 'ssh' # which is equivalent to c.IPClusterEngines.engine_launcher_class = 'ipyparallel.cluster.launcher.SSHEngineSetLauncher' ``` The shortest form being of particular use on the command line, where all you need to do to get an IPython cluster running with engines started with MPI is: ```bash $> ipcluster start --engines=mpi ``` Assuming that the default MPI configuration is sufficient. ```{note} The Launchers and configuration are designed in such a way that advanced users can subclass and configure them to fit their own system that we have not yet supported ``` ### Writing custom Launchers TODO: example writing custom Launchers ### Using {command}`ipcluster` in mpiexec/mpirun mode The mpiexec/mpirun mode is useful if you: 1. Have MPI installed. 2. Your systems are configured to use the {command}`mpiexec` or {command}`mpirun` commands to start MPI processes. If these are satisfied, you can create a new profile: ``` $ ipython profile create --parallel --profile=mpi ``` and edit the file {file}`IPYTHONDIR/profile_mpi/ipcluster_config.py`. There, instruct ipcluster to use the MPI launchers by adding the lines: ```python c.Cluster.engine_launcher_class = 'mpi' ``` If the default MPI configuration is correct, then you can now start your cluster, with: ``` $ ipcluster start -n 4 --profile=mpi ``` This does the following: 1. Starts the IPython controller on current host. 2. Uses {command}`mpiexec` to start 4 engines. On newer MPI implementations (such as OpenMPI), this will work even if you don't make any calls to MPI or call {func}`MPI_Init`. However, older MPI implementations require each process to call {func}`MPI_Init` upon starting. More details on using MPI with IPython can be found {ref}`here `. ### Using {command}`ipcluster` in PBS mode The PBS mode uses the Portable Batch System (PBS) to start the engines. As usual, we will start by creating a fresh profile: ``` $ ipython profile create --parallel --profile=pbs ``` And in {file}`ipcluster_config.py`, we will select the PBS launchers for the controller and engines: ```python c.Cluster.controller_launcher_class = 'pbs' c.Cluster.engine_launcher_class = 'pbs' ``` IPython does provide simple default batch templates for PBS and SGE, but you may need to specify your own. Here is a sample PBS script template: ```bash #PBS -N ipython #PBS -j oe #PBS -l walltime=00:10:00 #PBS -l nodes={n//4}:ppn=4 #PBS -q {queue} cd $PBS_O_WORKDIR export PATH=$HOME/usr/local/bin /usr/local/bin/mpiexec -n {n} ipengine --profile-dir={profile_dir} ``` There are a few important points about this template: 1. This template will be rendered at runtime using IPython's {class}`EvalFormatter`. This is a subclass of {class}`string.Formatter` that allows simple expressions on keys. 2. Instead of putting in the actual number of engines, use the notation `{n}` to indicate the number of engines to be started. You can also use expressions like `{n//4}` in the template to indicate the number of nodes. There will always be `{n}` and `{profile_dir}` variables passed to the formatter. These allow the batch system to know how many engines, and where the configuration files reside. The same is true for the batch queue, with the template variable `{queue}`. 3. Any options to {command}`ipengine` can be given in the batch script template, or in {file}`ipengine_config.py`. 4. Depending on the configuration of you system, you may have to set environment variables in the script template. The controller template should be similar, but simpler: ```bash #PBS -N ipython #PBS -j oe #PBS -l walltime=00:10:00 #PBS -l nodes=1:ppn=1 #PBS -q {queue} cd $PBS_O_WORKDIR export PATH=$HOME/usr/local/bin ipcontroller --profile-dir={profile_dir} ``` Once you have created these scripts, save them with names like {file}`pbs.engine.template`. Now you can load them into the {file}`ipcluster_config` with: ```python c.PBSEngineSetLauncher.batch_template_file = "pbs.engine.template" c.PBSControllerLauncher.batch_template_file = "pbs.controller.template" ``` Alternately, you can define the templates as strings inside {file}`ipcluster_config`. Whether you are using your own templates or our defaults, the extra configurables available are the number of engines to launch (`{n}`, and the batch system queue to which the jobs are to be submitted (`{queue}`)). These are configurables, and can be specified in {file}`ipcluster_config`: ```python c.PBSLauncher.queue = 'veryshort.q' c.IPClusterEngines.n = 64 ``` Note that assuming you are running PBS on a multi-node cluster, the Controller's default behavior of listening only on localhost is likely too restrictive. In this case, also assuming the nodes are safely behind a firewall, you can instruct the Controller to listen for connections on all its interfaces, by adding in {file}`ipcontroller_config`: ```python c.IPController.ip = '*' ``` You can now run the cluster with: ``` $ ipcluster start --profile=pbs -n 128 ``` Additional configuration options can be found in the PBS section of {file}`ipcluster_config`. ```{note} Due to the flexibility of configuration, the PBS launchers work with simple changes to the template for other {command}`qsub`-using systems, such as Sun Grid Engine, and with further configuration in similar batch systems like Condor. ``` ### Using {command}`ipcluster` in SSH mode The SSH mode uses {command}`ssh` to execute {command}`ipengine` on remote nodes and {command}`ipcontroller` can be run remotely as well, or on localhost. ```{note} When using this mode it highly recommended that you have set up SSH keys and are using ssh-agent [^cite_ssh] for password-less logins. ``` As usual, we start by creating a clean profile: ``` $ ipython profile create --parallel --profile=ssh ``` To use this mode, select the SSH launchers in {file}`ipcluster_config.py`: ```python c.IPClusterEngines.engine_launcher_class = 'ssh' # or 'sshproxy' # and if the Controller is also to be remote: c.IPClusterStart.controller_launcher_class = 'ssh' ``` The controller's remote location and configuration can be specified: ```python # Set the user and hostname for the controller # c.SSHControllerLauncher.hostname = 'controller.example.com' # c.SSHControllerLauncher.user = os.environ.get('USER','username') # Set the arguments to be passed to ipcontroller # note that remotely launched ipcontroller will not get the contents of # the local ipcontroller_config.py unless it resides on the *remote host* # in the location specified by the `profile-dir` argument. # c.SSHControllerLauncher.controller_args = ['--reuse', '--ip=*', '--profile-dir=/path/to/cd'] ``` Engines are specified in a dictionary, by hostname and the number of engines to be run on that host. ```python c.SSHEngineSetLauncher.engines = { 'host1.example.com' : 2, 'host2.example.com' : 5, 'host3.example.com' : (1, ['--profile-dir=/home/different/location']), 'host4.example.com' : {'n': 3, 'engine_args': ['--profile-dir=/away/location'], 'engine_cmd': ['/home/venv/bin/python', '-m', 'ipyparallel.engine']}, 'host5.example.com' : 8 } ``` - The `engines` dict, where the keys are the host we want to run engines on and the value is the number of engines to run on that host. - on host3, the value is a tuple, where the number of engines is first, and the arguments to be passed to {command}`ipengine` are the second element. - on host4, a dictionary configures the engine. The dictionary can be used to specify the number of engines to be run on that host `n`, the engine arguments `engine_args`, as well as the engine command itself `engine_cmd`. This is particularly useful for virtual environments on heterogeneous clusters where the location of the python executable might vary from host to host. For engines without explicitly specified arguments, the default arguments are set in a single location: ```python c.SSHEngineSetLauncher.engine_args = ['--profile-dir=/path/to/profile_ssh'] ``` Current limitations of the SSH mode of {command}`ipcluster` are: - Untested and unsupported on Windows. Would require a working {command}`ssh` on Windows. Also, we are using shell scripts to setup and execute commands on remote hosts. #### Moving files with SSH SSH launchers will try to move connection files, controlled by the `to_send` and `to_fetch` configurables. If your machines are on a shared filesystem, this step is unnecessary, and can be skipped by setting these to empty lists: ```python c.SSHLauncher.to_send = [] c.SSHLauncher.to_fetch = [] ``` If our default guesses about paths don't work for you, or other files should be moved, you can manually specify these lists as tuples of `(local_path, remote_path)` for `to_send`, and `(remote_path, local_path)` for `to_fetch`. If you do specify these lists explicitly, IPython _will not_ automatically send connection files, so you must include this yourself if they should still be sent/retrieved. ### Starting the controller and engines on different hosts When the controller and engines are running on different hosts, things are slightly more complicated, but the underlying ideas are the same: 1. Start the controller on a host using {command}`ipcontroller`. The controller must be instructed to listen on an interface visible to the engine machines, via the `ip` command-line argument or `IPController.ip` in {file}`ipcontroller_config.py`: ``` $ ipcontroller --ip=192.168.1.16 ``` ```python # in ipcontroller_config.py IPController.ip = '192.168.1.16' ``` 2. Copy {file}`ipcontroller-engine.json` from {file}`IPYTHONDIR/profile_/security` on the controller's host to the host where the engines will run. 3. Use {command}`ipengine` on the engine's hosts to start the engines. The only thing you have to be careful of is to tell {command}`ipengine` where the {file}`ipcontroller-engine.json` file is located. There are two ways you can do this: - Put {file}`ipcontroller-engine.json` in the {file}`IPYTHONDIR/profile_/security` directory on the engine's host, where it will be found automatically. - Call {command}`ipengine` with the `--file=full_path_to_the_file` flag. The `file` flag works like this: ``` $ ipengine --file=/path/to/my/ipcontroller-engine.json ``` ```{note} If the controller's and engine's hosts all have a shared file system ({file}`IPYTHONDIR/profile_/security` is the same on all of them), then no paths need to be specified or files copied. ``` #### SSH Tunnels If your engines are not on the same LAN as the controller, or you are on a highly restricted network where your nodes cannot see each others ports, then you can use SSH tunnels to connect engines to the controller. ```{note} This does not work in all cases. Manual tunnels may be an option, but are highly inconvenient. Support for manual tunnels will be improved. ``` You can instruct all engines to use ssh, by specifying the ssh server in {file}`ipcontroller-engine.json`: % I know this is really JSON, but the example is a subset of Python: ```python { "url":"tcp://192.168.1.123:56951", "exec_key":"26f4c040-587d-4a4e-b58b-030b96399584", "ssh":"user@example.com", "location":"192.168.1.123" } ``` This will be specified if you give the `--enginessh=use@example.com` argument when starting {command}`ipcontroller`. Or you can specify an ssh server on the command-line when starting an engine: ``` $> ipengine --profile=foo --ssh=my.login.node ``` For example, if your system is totally restricted, then all connections will be loopback, and ssh tunnels will be used to connect engines to the controller: ``` [node1] $> ipcontroller --enginessh=node1 [node2] $> ipengine [node3] $> ipcluster engines --n=4 ``` Or if you want to start many engines on each node, the command `ipcluster engines --n=4` without any configuration is equivalent to running ipengine 4 times. ### An example using ipcontroller/engine with ssh No configuration files are necessary to use ipcontroller/engine in an SSH environment without a shared filesystem. You need to make sure that the controller is listening on an interface visible to the engines, and move the connection file from the controller to the engines. 1. start the controller, listening on an ip-address visible to the engine machines: ``` [controller.host] $ ipcontroller --ip=192.168.1.16 [IPController] Using existing profile dir: u'/Users/me/.ipython/profile_default' [IPController] Hub listening on tcp://192.168.1.16:63320 for registration. [IPController] Hub using DB backend: 'ipyparallel.controller.dictdb.DictDB' [IPController] hub::created hub [IPController] writing connection info to /Users/me/.ipython/profile_default/security/ipcontroller-client.json [IPController] writing connection info to /Users/me/.ipython/profile_default/security/ipcontroller-engine.json [IPController] task::using Python leastload Task scheduler [IPController] Heartmonitor started [IPController] Creating pid file: /Users/me/.ipython/profile_default/pid/ipcontroller.pid Scheduler started [leastload] ``` 2. on each engine, fetch the connection file with scp: ``` [engine.host.n] $ scp controller.host:.ipython/profile_default/security/ipcontroller-engine.json ./ ``` ```{note} The log output of ipcontroller above shows you where the json files were written. They will be in {file}`~/.ipython` under {file}`profile_default/security/ipcontroller-engine.json` ``` 3. start the engines, using the connection file: ``` [engine.host.n] $ ipengine --file=./ipcontroller-engine.json ``` A couple of notes: - You can avoid having to fetch the connection file every time by adding `--reuse` flag to ipcontroller, which instructs the controller to read the previous connection file for connection info, rather than generate a new one with randomized ports. - In step 2, if you fetch the connection file directly into the security dir of a profile, then you need not specify its path directly, only the profile (assumes the path exists, otherwise you must create it first): ``` [engine.host.n] $ scp controller.host:.ipython/profile_default/security/ipcontroller-engine.json ~/.ipython/profile_ssh/security/ [engine.host.n] $ ipengine --profile=ssh ``` Of course, if you fetch the file into the default profile, no arguments must be passed to ipengine at all. - Note that ipengine _did not_ specify the ip argument. In general, it is unlikely for any connection information to be specified at the command-line to ipengine, as all of this information should be contained in the connection file written by ipcontroller. ### Make JSON files persistent At fist glance it may seem that that managing the JSON files is a bit annoying. Going back to the house and key analogy, copying the JSON around each time you start the controller is like having to make a new key every time you want to unlock the door and enter your house. As with your house, you want to be able to create the key (or JSON file) once, and then use it at any point in the future. To do this, the only thing you have to do is specify the `--reuse` flag, so that the connection information in the JSON files remains accurate: ``` $ ipcontroller --reuse ``` Then copy the JSON files over the first time and you are set. You can start and stop the controller and engines any many times as you want in the future, as long as you make sure to tell the controller to reuse the file. ```{note} You may ask the question: what ports does the controller listen on if you don't tell is to use specific ones? The default is to use high random port numbers. We do this for two reasons: i) to increase security through obscurity and ii) to multiple controllers on a given host to start and automatically use different ports. ``` ### Log files All of the components of IPython have log files associated with them. These log files can be extremely useful in debugging problems with IPython and can be found in the directory {file}`IPYTHONDIR/profile_/log`. Sending the log files to us will often help us to debug any problems. ### Configuring `ipcontroller` The IPython Controller takes its configuration from the file {file}`ipcontroller_config.py` in the active profile directory. #### Ports and addresses In many cases, you will want to configure the Controller's network identity. By default, the Controller listens only on loopback, which is the most secure but often impractical. To instruct the controller to listen on a specific interface, you can set the {attr}`IPController.ip` trait. To listen on all interfaces, specify: ```python c.IPController.ip = '*' ``` When connecting to a Controller that is listening on loopback or behind a firewall, it may be necessary to specify an SSH server to use for tunnels, and the external IP of the Controller. If you specified that the IPController listen on loopback, or all interfaces, then IPython will try to guess the external IP. If you are on a system with VM network devices, or many interfaces, this guess may be incorrect. In these cases, you will want to specify the 'location' of the Controller. This is the IP of the machine the Controller is on, as seen by the clients, engines, or the SSH server used to tunnel connections. For example, to set up a cluster with a Controller on a work node, using ssh tunnels through the login node, an example {file}`ipcontroller_config.py` might contain: ```python # allow connections on all interfaces from engines # engines on the same node will use loopback, while engines # from other nodes will use an external IP c.IPController.ip = '*' # you typically only need to specify the location when there are extra # interfaces that may not be visible to peer nodes (e.g. VM interfaces) c.IPController.location = '10.0.1.5' # or to get an automatic value, try this: import socket hostname = socket.gethostname() # alternate choices for hostname include `socket.getfqdn()` # or `socket.gethostname() + '.local'` ex_ip = socket.gethostbyname_ex(hostname)[-1][-1] c.IPController.location = ex_ip # now instruct clients to use the login node for SSH tunnels: c.IPController.ssh_server = 'login.mycluster.net' ``` After doing this, your {file}`ipcontroller-client.json` file will look something like this: ```python { "url":"tcp:\/\/*:43447", "exec_key":"9c7779e4-d08a-4c3b-ba8e-db1f80b562c1", "ssh":"login.mycluster.net", "location":"10.0.1.5" } ``` Then this file will be all you need for a client to connect to the controller, tunneling SSH connections through login.mycluster.net. #### Database Backend The Hub stores all messages and results passed between Clients and Engines. For large and/or long-running clusters, it would be unreasonable to keep all of this information in memory. For this reason, we have two database backends: [^cite_mongodb] via [PyMongo], and SQLite with the stdlib {py:mod}`sqlite`. MongoDB is our design target, and the dict-like model it uses has driven our design. As far as we are concerned, BSON can be considered essentially the same as JSON, adding support for binary data and datetime objects, and any new database backend must support the same data types. ```{seealso} MongoDB [BSON doc]( https://bsonspec.org) ``` To use one of these backends, you must set the {attr}`IPController.db_class` trait: ```python # for a simple dict-based in-memory implementation, use dictdb # This is the default and the fastest, since it doesn't involve the filesystem c.IPController.db_class = 'ipyparallel.controller.dictdb.DictDB' # To use MongoDB: c.IPController.db_class = 'ipyparallel.controller.mongodb.MongoDB' # and SQLite: c.IPController.db_class = 'ipyparallel.controller.sqlitedb.SQLiteDB' # You can use NoDB to disable the database altogether, in case you don't need # to reuse tasks or results, and want to keep memory consumption under control. c.IPController.db_class = 'ipyparallel.controller.dictdb.NoDB' ``` When using the proper databases, you can allow for tasks to persist from one session to the next by specifying the MongoDB database or SQLite table in which tasks are to be stored. The default is to use a table named for the Hub's Session, which is a UUID, and thus different every time. ```python # To keep persistent task history in MongoDB: c.MongoDB.database = 'tasks' # and in SQLite: c.SQLiteDB.table = 'tasks' ``` Since MongoDB servers can be running remotely or configured to listen on a particular port, you can specify any arguments you may need to the PyMongo {py:class}`~.pymongo.Connection`: ```python # positional args to pymongo.Connection c.MongoDB.connection_args = [] # keyword args to pymongo.Connection c.MongoDB.connection_kwargs = {} ``` But sometimes you are moving lots of data around quickly, and you don't need that information to be stored for later access, even by other Clients to this same session. For this case, we have a dummy database, which doesn't store anything. This lets the Hub stay small in memory, at the obvious expense of being able to access the information that would have been stored in the database (used for task resubmission, requesting results of tasks you didn't submit, etc.). To use this backend, pass `--nodb` to {command}`ipcontroller` on the command-line, or specify the {class}`NoDB` class in your {file}`ipcontroller_config.py` as described above. ```{seealso} For more information on the database backends, see the {ref}`db backend reference `. ``` ### Configuring `ipengine` The IPython Engine takes its configuration from the file {file}`ipengine_config.py` The Engine itself also has some amount of configuration. Most of this has to do with initializing MPI or connecting to the controller. To instruct the Engine to initialize with an MPI environment set up by mpi4py, add: ```python c.MPI.use = 'mpi4py' ``` In this case, the Engine will use our default mpi4py init script to set up the MPI environment prior to execution. We have default init scripts for mpi4py and pytrilinos. If you want to specify your own code to be run at the beginning, specify `c.MPI.init_script`. You can also specify a file or python command to be run at startup of the Engine: ```python c.IPEngine.startup_script = u'/path/to/my/startup.py' c.IPEngine.startup_command = 'import numpy, scipy, mpi4py' ``` These commands/files will be run again, after each It's also useful on systems with shared filesystems to run the engines in some scratch directory. This can be set with: ```python c.IPEngine.work_dir = u'/path/to/scratch/' ``` [^cite_mongodb]: MongoDB database [^cite_pbs]: Portable Batch System [^cite_ssh]: SSH-Agent [pymongo]: https://pymongo.readthedocs.io ipyparallel-7.1.0/docs/source/tutorial/task.md000066400000000000000000000402001412533144600214130ustar00rootroot00000000000000(parallel-task)= # The IPython task interface The task interface to the cluster presents the engines as a fault tolerant, dynamic load-balanced system of workers. Unlike the direct interface, in the task interface the user has no direct access to individual engines. By allowing the IPython scheduler to assign work, this interface is simultaneously simpler and more powerful. Best of all, the user can use both of these interfaces running at the same time to take advantage of their respective strengths. When the user can break up the user's work into segments that do not depend on previous execution, the task interface is ideal. But it also has more power and flexibility, allowing the user to guide the distribution of jobs, without having to assign tasks to engines explicitly. ## Creating a `LoadBalancedView` As always, the first step is to start a cluster and connect a {class}`.Client` instance. For more detailed information about starting the controller and engines, see our {ref}`introduction ` to using IPython for parallel computing. ```ipython In [1]: import ipyparallel as ipp In [2]: cluster = ipp.Cluster() In [3]: cluster.start_cluster_sync() In [4]: rc = cluster.connect_client_sync() In [5]: rc.wait_for_engines(4) ``` For load-balanced execution, we will make use of a {class}`.LoadBalancedView` object, which can be constructed via the client's {meth}`~.Client.load_balanced_view` method: ```ipython In [4]: lview = rc.load_balanced_view() # default load-balanced view ``` ```{seealso} For more information, see the in-depth explanation of {ref}`Views `. ``` ## Quick and easy parallelism In many cases, you want to apply a Python function to a sequence of objects, but _in parallel_. Like the direct interface, these can be implemented via the task interface. The exact same tools can perform these actions in load-balanced ways as well as multiplexed ways: a parallel version of {py:func}`map` and {func}`@view.parallel` function decorator. If one specifies the argument `balanced=True`, then they are dynamically load balanced. Thus, if the execution time per item varies significantly, you should use the versions in the task interface. ### Parallel map To load-balance {func}`map`, use a LoadBalancedView: ```ipython In [62]: lview.block = True In [63]: serial_result = map(lambda x:x**10, range(32)) In [64]: parallel_result = lview.map(lambda x:x**10, range(32)) In [65]: serial_result==parallel_result Out[65]: True ``` ### Parallel function decorator Parallel functions are just like normal functions, but they can be called on sequences and _in parallel_. The direct interface provides a decorator that turns any Python function into a parallel function: ```ipython In [10]: @lview.parallel() ....: def f(x): ....: return 10.0 * x ** 4 ....: In [11]: f.map(range(32)) # this is done in parallel Out[11]: [0.0, 10.0, 160.0, ...] ``` (parallel-dependencies)= ## Dependencies Often, pure atomic load-balancing is too primitive for your work. In these cases, you may want to associate some kind of `Dependency` that describes when, where, or whether a task can be run. In IPython, we provide two types of dependencies: [Functional Dependencies] and [Graph Dependencies] ```{note} It is important to note that the pure ZeroMQ scheduler does not support dependencies, and you will see errors or warnings if you try to use dependencies with the pure scheduler. ``` ### Functional Dependencies Functional dependencies are used to determine whether a given engine is capable of running a particular task. This is implemented via a special {class}`Exception` class, {class}`UnmetDependency`, found in `ipyparallel.error`. Its use is very simple: if a task fails with an UnmetDependency exception, then the scheduler, instead of relaying the error up to the client like any other error, catches the error, and submits the task to a different engine. This will repeat indefinitely, and a task will never be submitted to a given engine a second time. You can manually raise the {class}`UnmetDependency` yourself, but IPython has provided some decorators for facilitating this behavior. There are two decorators and a class used for functional dependencies: ```ipython In [9]: import ipyparallel as ipp ``` #### @ipp.require The simplest sort of dependency is requiring that a Python module is available. The `@ipp.require` decorator lets you define a function that will only run on engines where names you specify are importable: ```ipython In [10]: @ipp.require('numpy', 'zmq') ....: def myfunc(): ....: return dostuff() ``` Now, any time you apply {func}`myfunc`, the task will only run on a machine that has numpy and pyzmq available, and when {func}`myfunc` is called, numpy and zmq will be imported. You can also require specific objects, not just module names: ```python def foo(a): return a * a @ipp.require(foo) def bar(b): return foo(b) @ipp.require(bar) def baz(c, d): return bar(c) - bar(d) view.apply_sync(baz, 4, 5) ``` #### @ipp.depend The `@ipp.depend` decorator lets you decorate any function with any _other_ function to evaluate the dependency. The dependency function will be called at the start of the task, and if it returns `False`, then the dependency will be considered unmet, and the task will be assigned to another engine. If the dependency returns _anything other than `False`_, the rest of the task will continue. ```ipython In [10]: def platform_specific(plat): ....: import sys ....: return sys.platform == plat In [11]: @ipp.depend(platform_specific, 'darwin') ....: def mactask(): ....: do_mac_stuff() In [12]: @ipp.depend(platform_specific, 'nt') ....: def wintask(): ....: do_windows_stuff() ``` In this case, any time you apply `mactask`, it will only run on an macOS machine. `@ipp.depend` is like `apply`, in that it has a `@ipp.depend(f, *args, **kwargs)` signature. #### dependents You don't have to use the decorators on your tasks, if for instance you may want to run tasks with a single function but varying dependencies, you can directly construct the {class}`dependent` object that the decorators use: ```ipython In [13]: def mytask(*args): ....: dostuff() In [14]: mactask = dependent(mytask, platform_specific, 'darwin') # this is the same as decorating the declaration of mytask with @ipp.depend # but you can do it again: In [15]: wintask = dependent(mytask, platform_specific, 'nt') # in general: In [16]: t = dependent(f, g, *dargs, **dkwargs) # is equivalent to: In [17]: @ipp.depend(g, *dargs, **dkwargs) ....: def t(a, b, c): ....: # contents of f ``` ### Graph Dependencies Sometimes you want to restrict the time and/or location to run a given task as a function of the time and/or location of other tasks. This is implemented via a subclass of {class}`set`, called a {class}`Dependency`. A Dependency is a set of `msg_ids` corresponding to tasks, and a few attributes to guide how to decide when the Dependency has been met. The switches we provide for interpreting whether a given dependency set has been met: any|all : Whether the dependency is considered met if _any_ of the dependencies are done, or only after _all_ of them have finished. This is set by a Dependency's {attr}`all` boolean attribute, which defaults to `True`. success \[default: True\] : Whether to consider tasks that succeeded as fulfilling dependencies. failure \[default\] : Whether to consider tasks that failed as fulfilling dependencies. using `failure=True,success=False` is useful for setting up cleanup tasks, to be run only when tasks have failed. Sometimes you want to run a task after another, but only if that task succeeded. In this case, `success` should be `True` and `failure` should be `False`. However sometimes you may not care whether the task succeeds, and always want the second task to run, in which case you should use `success=failure=True`. The default behavior is to only use successes. There are other switches for interpretation that are made at the _task_ level. These are specified via keyword arguments to the view's {meth}`~.LoadBalancedView.apply` method. after,follow : You may want to run a task _after_ a given set of dependencies have been run and/or run it _where_ another set of dependencies are met. To support this, every task has an `after` dependency to restrict time, and a `follow` dependency to restrict destination. timeout : You may also want to set a time-limit for how long the scheduler should wait before a task's dependencies are met. This is done via a `timeout`, which defaults to 0, which indicates that the task should never timeout. If the timeout is reached, and the scheduler still hasn't been able to assign the task to an engine, the task will fail with a {class}`DependencyTimeout`. ```{note} Dependencies only work within the task scheduler. You cannot instruct a load-balanced task to run after a job submitted via the MUX interface. ``` The simplest form of Dependencies is with `all=True, success=True, failure=False`. In these cases, you can skip using Dependency objects, and pass msg_ids or AsyncResult objects as the `follow` and `after` keywords to {meth}`client.apply`: ```ipython In [14]: client.block = False In [15]: ar = lview.apply(f, args, kwargs) In [16]: ar2 = lview.apply(f2) In [17]: with lview.temp_flags(after=[ar, ar2]): ....: ar3 = lview.apply(f3) In [18]: with lview.temp_flags(follow=[ar], timeout=2.5) ....: ar4 = lview.apply(f3) ``` ```{seealso} Some parallel workloads can be described as a [Directed Acyclic Graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph), or DAG. See {ref}`DAG Dependencies ` for an example demonstrating how to use map a NetworkX DAG onto task dependencies. ``` #### Impossible Dependencies The schedulers do perform some analysis on graph dependencies to determine whether they are not possible to be met. If the scheduler does discover that a dependency cannot be met, then the task will fail with an {class}`ImpossibleDependency` error. This way, if the scheduler realized that a task can never be run, it won't sit indefinitely in the scheduler clogging the pipeline. The basic cases that are checked: - depending on nonexistent messages - `follow` dependencies were run on more than one machine and `all=True` - any dependencies failed and `all=True, success=True, failures=False` - all dependencies failed and `all=False, success=True, failure=False` ```{warning} This analysis has not been proven to be rigorous, so it is likely possible for tasks to become impossible to run in obscure situations, so a timeout may be a good choice. ``` ## Retries and Resubmit ### Retries Another flag for tasks is `retries`. This is an integer, specifying how many times a task should be resubmitted after failure. This is useful for tasks that should still run if their engine was shutdown, or may have some statistical chance of failing. The default is to not retry tasks. ### Resubmit Sometimes you may want to re-run a task. This could be because it failed for some reason, and you have fixed the error, or because you want to restore the cluster to an interrupted state. For this, the {class}`Client` has a {meth}`rc.resubmit` method. This takes one or more msg_ids, and returns an {class}`AsyncHubResult` for the result(s). You cannot resubmit a task that is pending - only those that have finished, either successful or unsuccessful. (parallel-schedulers)= ## Schedulers There are a variety of valid ways to determine where jobs should be assigned in a load-balancing situation. In IPython, we support several standard schemes, and even make it easy to define your own. The scheme can be selected via the `scheme` argument to {command}`ipcontroller`, or in the {attr}`TaskScheduler.schemename` attribute of a controller config object. The built-in routing schemes: To select one of these schemes: ``` $ ipcontroller --scheme= for instance: $ ipcontroller --scheme=lru ``` lru: Least Recently Used > Always assign work to the least-recently-used engine. A close relative of > round-robin, it will be fair with respect to the number of tasks, agnostic > with respect to runtime of each task. plainrandom: Plain Random > Randomly picks an engine on which to run. twobin: Two-Bin Random > **Requires numpy** > > Pick two engines at random, and use the LRU of the two. This is known to be better > than plain random in many cases, but requires a small amount of computation. leastload: Least Load > **This is the default scheme** > > Always assign tasks to the engine with the fewest outstanding tasks (LRU breaks tie). weighted: Weighted Two-Bin Random > **Requires numpy** > > Pick two engines at random using the number of outstanding tasks as inverse weights, > and use the one with the lower load. ### Greedy Assignment Tasks can be assigned greedily as they are submitted. If their dependencies are met, they will be assigned to an engine right away, and multiple tasks can be assigned to an engine at a given time. This limit is set with the `TaskScheduler.hwm` (high water mark) configurable in your {file}`ipcontroller_config.py` config file, with: ```python # the most common choices are: c.TaskSheduler.hwm = 0 # (minimal latency, default in IPython < 0.13) # or c.TaskScheduler.hwm = 1 # (most-informed balancing, default in ≥ 0.13) ``` In IPython \< 0.13, the default is 0, or no-limit. That is, there is no limit to the number of tasks that can be outstanding on a given engine. This greatly benefits the latency of execution, because network traffic can be hidden behind computation. However, this means that workload is assigned without knowledge of how long each task might take, and can result in poor load-balancing, particularly for submitting a collection of heterogeneous tasks all at once. You can limit this effect by setting hwm to a positive integer, 1 being maximum load-balancing (a task will never be waiting if there is an idle engine), and any larger number being a compromise between load-balancing and latency-hiding. In practice, some users have been confused by having this optimization on by default, so the default value has been changed to 1 in IPython 0.13. This can be slower, but has more obvious behavior and won't result in assigning too many tasks to some engines in heterogeneous cases. ### Pure ZMQ Scheduler For maximum throughput, the 'pure' scheme is not Python at all, but a C-level {class}`MonitoredQueue` from PyZMQ, which uses a ZeroMQ `DEALER` socket to perform all load-balancing. This scheduler does not support any of the advanced features of the Python {class}`.Scheduler`. Disabled features when using the ZMQ Scheduler: - Engine unregistration : Task farming will be disabled if an engine unregisters. Further, if an engine is unregistered during computation, the scheduler may not recover. - Dependencies : Since there is no Python logic inside the Scheduler, routing decisions cannot be made based on message content. - Early destination notification : The Python schedulers know which engine gets which task, and notify the Hub. This allows graceful handling of Engines coming and going. There is no way to know where ZeroMQ messages have gone, so there is no way to know what tasks are on which engine until they _finish_. This makes recovery from engine shutdown very difficult. ```{note} TODO: performance comparisons ``` ## More details The {class}`LoadBalancedView` has many more powerful features that allow quite a bit of flexibility in how tasks are defined and run. The next places to look are in the following classes: - {class}`~ipyparallel.client.view.LoadBalancedView` - {class}`~ipyparallel.client.asyncresult.AsyncResult` - {meth}`~ipyparallel.client.view.LoadBalancedView.apply` - {mod}`~ipyparallel.controller.dependency` The following is an overview of how to use these classes together: 1. Create a {class}`Client` and {class}`LoadBalancedView` 2. Define some functions to be run as tasks 3. Submit your tasks to using the {meth}`apply` method of your {class}`LoadBalancedView` instance. 4. Use {meth}`.Client.get_result` to get the results of the tasks, or use the {meth}`AsyncResult.get` method of the results to wait for and then receive the results. ```{seealso} A demo of {ref}`DAG Dependencies ` with NetworkX and IPython. ``` ipyparallel-7.1.0/etc/000077500000000000000000000000001412533144600146135ustar00rootroot00000000000000ipyparallel-7.1.0/etc/jupyter/000077500000000000000000000000001412533144600163155ustar00rootroot00000000000000ipyparallel-7.1.0/etc/jupyter/jupyter_notebook_config.d/000077500000000000000000000000001412533144600234665ustar00rootroot00000000000000ipyparallel-7.1.0/etc/jupyter/jupyter_notebook_config.d/ipyparallel.json000066400000000000000000000001301412533144600266710ustar00rootroot00000000000000{ "NotebookApp": { "nbserver_extensions": { "ipyparallel": true } } } ipyparallel-7.1.0/etc/jupyter/jupyter_server_config.d/000077500000000000000000000000001412533144600231545ustar00rootroot00000000000000ipyparallel-7.1.0/etc/jupyter/jupyter_server_config.d/ipyparallel.json000066400000000000000000000001261412533144600263640ustar00rootroot00000000000000{ "ServerApp": { "jpserver_extensions": { "ipyparallel": true } } } ipyparallel-7.1.0/etc/jupyter/nbconfig/000077500000000000000000000000001412533144600201025ustar00rootroot00000000000000ipyparallel-7.1.0/etc/jupyter/nbconfig/tree.d/000077500000000000000000000000001412533144600212635ustar00rootroot00000000000000ipyparallel-7.1.0/etc/jupyter/nbconfig/tree.d/ipyparallel.json000066400000000000000000000000741412533144600244750ustar00rootroot00000000000000{ "load_extensions": { "ipyparallel/main": true } } ipyparallel-7.1.0/examples000077700000000000000000000000001412533144600215652docs/source/examplesustar00rootroot00000000000000ipyparallel-7.1.0/install.json000066400000000000000000000002671412533144600164060ustar00rootroot00000000000000{ "packageManager": "python", "packageName": "ipyparallel", "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package ipyparallel" } ipyparallel-7.1.0/ipyparallel/000077500000000000000000000000001412533144600163565ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/__init__.py000066400000000000000000000055751412533144600205030ustar00rootroot00000000000000# coding: utf-8 """The IPython ZMQ-based parallel computing interface.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from traitlets.config.configurable import MultipleInstanceError from ._version import __version__ # noqa from ._version import version_info # noqa from .client.asyncresult import * # noqa from .client.client import Client # noqa from .client.remotefunction import * # noqa from .client.view import * # noqa from .cluster import Cluster # noqa from .cluster import ClusterManager # noqa from .controller.dependency import * # noqa from .error import * # noqa from .serialize import * # noqa from .util import interactive # noqa # ----------------------------------------------------------------------------- # Functions # ----------------------------------------------------------------------------- def bind_kernel(**kwargs): """Bind an Engine's Kernel to be used as a full IPython kernel. This allows a running Engine to be used simultaneously as a full IPython kernel with the QtConsole or other frontends. This function returns immediately. """ from ipykernel.kernelapp import IPKernelApp from ipyparallel.engine.app import IPEngine # first check for IPKernelApp, in which case this should be a no-op # because there is already a bound kernel if IPKernelApp.initialized() and isinstance(IPKernelApp._instance, IPKernelApp): return if IPEngine.initialized(): try: app = IPEngine.instance() except MultipleInstanceError: pass else: return app.bind_kernel(**kwargs) raise RuntimeError("bind_kernel must be called from an IPEngine instance") def register_joblib_backend(name='ipyparallel', make_default=False): """Register the default ipyparallel backend for joblib.""" from .joblib import register return register(name=name, make_default=make_default) # nbextension installation (requires notebook ≥ 4.2) def _jupyter_server_extension_paths(): return [{'module': 'ipyparallel'}] def _jupyter_nbextension_paths(): return [ { 'section': 'tree', 'src': 'nbextension/static', 'dest': 'ipyparallel', 'require': 'ipyparallel/main', } ] def _jupyter_labextension_paths(): return [ { "src": "labextension", "dest": "ipyparallel-labextension", } ] def _load_jupyter_server_extension(app): """Load the server extension""" # localte the appropriate APIHandler base class before importing our handler classes from .nbextension.base import get_api_handler get_api_handler(app) from .nbextension.handlers import load_jupyter_server_extension return load_jupyter_server_extension(app) # backward-compat load_jupyter_server_extension = _load_jupyter_server_extension ipyparallel-7.1.0/ipyparallel/_async.py000066400000000000000000000061641412533144600202130ustar00rootroot00000000000000"""Async utilities""" import asyncio import concurrent.futures import inspect import threading from functools import partial from tornado.ioloop import IOLoop def _asyncio_run(coro): """Like asyncio.run, but works when there's no event loop""" # for now: using tornado for broader compatibility with FDs, # e.g. when using the only partially functional default # Proactor on windows loop = IOLoop() return loop.run_sync(lambda: asyncio.ensure_future(coro)) class AsyncFirst: """Wrapper class that defines synchronous `_sync` method wrappers around async-native methods. Every coroutine method automatically gets an `_sync` alias that runs it synchronously. """ _async_thread = None def _thread_main(self): asyncio_loop = asyncio.new_event_loop() asyncio.set_event_loop(asyncio_loop) loop = self._thread_loop = IOLoop.current() loop.add_callback(self._loop_started.set) loop.start() def _in_thread(self, async_f, *args, **kwargs): """Run an async function in a background thread""" if self._async_thread is None: self._loop_started = threading.Event() self._async_thread = threading.Thread(target=self._thread_main, daemon=True) self._async_thread.start() self._loop_started.wait(timeout=5) future = concurrent.futures.Future() async def thread_callback(): try: future.set_result(await async_f(*args, **kwargs)) except Exception as e: future.set_exception(e) self._thread_loop.add_callback(thread_callback) return future.result() def _synchronize(self, async_f, *args, **kwargs): """Run a method synchronously Uses asyncio.run if asyncio is not running, otherwise puts it in a background thread """ try: loop = asyncio.get_event_loop() except RuntimeError: # sometimes get returns a RuntimeError # if there's no current loop under certain policies loop = None if loop and loop.is_running(): return self._in_thread(async_f, *args, **kwargs) else: return _asyncio_run(async_f(*args, **kwargs)) def __getattr__(self, name): if name.endswith("_sync"): # lazily define `_sync` method wrappers for coroutine methods async_name = name[:-5] async_method = super().__getattribute__(async_name) if not inspect.iscoroutinefunction(async_method): raise AttributeError(async_name) return partial(self._synchronize, async_method) return super().__getattribute__(name) def __dir__(self): attrs = super().__dir__() seen = set() for cls in self.__class__.mro(): for name, value in cls.__dict__.items(): if name in seen: continue seen.add(name) if inspect.iscoroutinefunction(value): async_name = name + "_sync" attrs.append(async_name) return attrs ipyparallel-7.1.0/ipyparallel/_version.py000066400000000000000000000001101412533144600205440ustar00rootroot00000000000000version_info = (7, 1, 0) __version__ = ".".join(map(str, version_info)) ipyparallel-7.1.0/ipyparallel/apps/000077500000000000000000000000001412533144600173215ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/apps/__init__.py000066400000000000000000000000001412533144600214200ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/apps/baseapp.py000066400000000000000000000166661412533144600213250ustar00rootroot00000000000000# encoding: utf-8 """ The Base Application class for ipyparallel apps """ import logging import os import re import sys import traitlets from IPython.core.application import base_aliases as base_ip_aliases from IPython.core.application import base_flags as base_ip_flags from IPython.core.application import BaseIPythonApplication from IPython.utils.path import expand_path from jupyter_client.session import Session from tornado.ioloop import IOLoop from traitlets import Bool from traitlets import default from traitlets import Instance from traitlets import observe from traitlets import Unicode from traitlets.config.application import catch_config_error from traitlets.config.application import LevelFormatter from .._version import __version__ # FIXME: CUnicode is needed for cli parsing # with traitlets 4 # bump when we require traitlets 5, which requires Python 3.7 if int(traitlets.__version__.split(".", 1)[0]) < 5: from traitlets import CUnicode else: # don't need CUnicode with traitlets 4 CUnicode = Unicode # ----------------------------------------------------------------------------- # Module errors # ----------------------------------------------------------------------------- class PIDFileError(Exception): pass # ----------------------------------------------------------------------------- # Main application # ----------------------------------------------------------------------------- base_aliases = {} base_aliases.update(base_ip_aliases) base_aliases.update( { 'work-dir': 'BaseParallelApplication.work_dir', 'log-to-file': 'BaseParallelApplication.log_to_file', 'clean-logs': 'BaseParallelApplication.clean_logs', 'log-url': 'BaseParallelApplication.log_url', 'cluster-id': 'BaseParallelApplication.cluster_id', } ) base_flags = { 'log-to-file': ( {'BaseParallelApplication': {'log_to_file': True}}, "send log output to a file", ) } base_flags.update(base_ip_flags) class BaseParallelApplication(BaseIPythonApplication): """The base Application for ipyparallel apps Primary extensions to BaseIPythonApplication: * work_dir * remote logging via pyzmq * IOLoop instance """ version = __version__ _deprecated_classes = None def init_crash_handler(self): # disable crash handler from IPython pass def _log_level_default(self): # temporarily override default_log_level to INFO return logging.INFO def _log_format_default(self): """override default log format to include time""" return u"%(asctime)s.%(msecs).03d [%(name)s]%(highlevel)s %(message)s" work_dir = Unicode( os.getcwd(), config=True, help='Set the working dir for the process.' ) @observe('work_dir') def _work_dir_changed(self, change): self.work_dir = str(expand_path(change['new'])) log_to_file = Bool(config=True, help="whether to log to a file") clean_logs = Bool( False, config=True, help="whether to cleanup old logfiles before starting" ) log_url = Unicode( '', config=True, help="The ZMQ URL of the iplogger to aggregate logging." ) cluster_id = CUnicode( '', config=True, help="""String id to add to runtime files, to prevent name collisions when using multiple clusters with a single profile simultaneously. When set, files will be named like: 'ipcontroller--engine.json' Since this is text inserted into filenames, typical recommendations apply: Simple character strings are ideal, and spaces are not recommended (but should generally work). """, ) loop = Instance(IOLoop) def _loop_default(self): return IOLoop.current() session = Instance(Session) @default("session") def _default_session(self): return Session(parent=self) aliases = base_aliases flags = base_flags @catch_config_error def initialize(self, argv=None): """initialize the app""" super(BaseParallelApplication, self).initialize(argv) if "IPP_SESSION_KEY" in os.environ: self.config.Session.key = os.environ["IPP_SESSION_KEY"].encode("ascii") self.init_deprecated_config() self.to_work_dir() self.reinit_logging() def init_deprecated_config(self): if not self._deprecated_classes: return deprecated_config_found = False new_classname = self.__class__.__name__ for deprecated_classname in self._deprecated_classes: if deprecated_classname in self.config: cfg = self.config[deprecated_classname] new_config = self.config[new_classname] for key, deprecated_value in list(cfg.items()): if key in new_config: new_value = new_config[key] if new_value != deprecated_value: self.log.warning( f"Ignoring c.{deprecated_classname}.{key} = {deprecated_value}, overridden by c.{new_classname}.{key} = {new_value}" ) else: self.log.warning( f"c.{deprecated_classname}.{key} is deprecated in ipyparallel 7, use c.{new_classname}.{key} = {deprecated_value}" ) new_config[key] = deprecated_value cfg.pop(key) deprecated_config_found = True if deprecated_config_found: # reload config self.update_config(self.config) def to_work_dir(self): wd = self.work_dir if wd != os.getcwd(): os.chdir(wd) self.log.info("Changing to working dir: %s" % wd) # This is the working dir by now. sys.path.insert(0, '') def reinit_logging(self): # Remove old log files log_dir = self.profile_dir.log_dir if self.clean_logs: for f in os.listdir(log_dir): if re.match(r'%s-\d+\.(log|err|out)' % self.name, f): try: os.remove(os.path.join(log_dir, f)) except (OSError, IOError): # probably just conflict from sibling process # already removing it pass if self.log_to_file: # Start logging to the new log file log_filename = f"{self.name}-{self.cluster_id}-{os.getpid()}.log" logfile = os.path.join(log_dir, log_filename) if sys.__stderr__: print(f"Sending logs to {logfile}", file=sys.__stderr__) open_log_file = open(logfile, 'w') else: open_log_file = None if open_log_file is not None: while self.log.handlers: self.log.removeHandler(self.log.handlers[0]) self._log_handler = logging.StreamHandler(open_log_file) self.log.addHandler(self._log_handler) else: self._log_handler = self.log.handlers[0] # Add timestamps to log format: self._log_formatter = LevelFormatter(self.log_format, datefmt=self.log_datefmt) self._log_handler.setFormatter(self._log_formatter) # do not propagate log messages to root logger # ipcluster app will sometimes print duplicate messages during shutdown # if this is 1 (default): self.log.propagate = False ipyparallel-7.1.0/ipyparallel/apps/ipclusterapp.py000066400000000000000000000004171412533144600224100ustar00rootroot00000000000000import warnings warnings.warn(f"{__name__} is deprecated in ipyparallel 7. Use ipyparallel.cluster") from ipyparallel.cluster.app import IPCluster, IPClusterStart, main # noqa IPClusterApp = IPCluster launch_new_instance = main if __name__ == "__main__": main() ipyparallel-7.1.0/ipyparallel/apps/ipcontrollerapp.py000066400000000000000000000004061412533144600231100ustar00rootroot00000000000000import warnings warnings.warn(f"{__name__} is deprecated in ipyparallel 7. Use ipyparallel.controller") from ipyparallel.controller.app import IPController, main IPControllerApp = IPController launch_new_instance = main if __name__ == "__main__": main() ipyparallel-7.1.0/ipyparallel/apps/ipengineapp.py000066400000000000000000000003621412533144600221730ustar00rootroot00000000000000import warnings warnings.warn(f"{__name__} is deprecated in ipyparallel 7. Use ipyparallel.engine") from ipyparallel.engine.app import IPEngine, main IPEngineApp = IPEngine launch_new_instance = main if __name__ == "__main__": main() ipyparallel-7.1.0/ipyparallel/apps/iploggerapp.py000077500000000000000000000044511412533144600222130ustar00rootroot00000000000000#!/usr/bin/env python # encoding: utf-8 """ A simple IPython logger application """ from IPython.core.profiledir import ProfileDir from traitlets import Dict from ipyparallel.apps.baseapp import base_aliases from ipyparallel.apps.baseapp import BaseParallelApplication from ipyparallel.apps.baseapp import catch_config_error from ipyparallel.apps.logwatcher import LogWatcher # ----------------------------------------------------------------------------- # Module level variables # ----------------------------------------------------------------------------- #: The default config file name for this application _description = """Start an IPython logger for parallel computing. IPython controllers and engines (and your own processes) can broadcast log messages by registering a `zmq.log.handlers.PUBHandler` with the `logging` module. The logger can be configured using command line options or using a cluster directory. Cluster directories contain config, log and security files and are usually located in your ipython directory and named as "profile_name". See the `profile` and `profile-dir` options for details. """ # ----------------------------------------------------------------------------- # Main application # ----------------------------------------------------------------------------- aliases = {} aliases.update(base_aliases) aliases.update(dict(url='LogWatcher.url', topics='LogWatcher.topics')) class IPLoggerApp(BaseParallelApplication): name = u'iplogger' description = _description classes = [LogWatcher, ProfileDir] aliases = Dict(aliases) @catch_config_error def initialize(self, argv=None): super(IPLoggerApp, self).initialize(argv) self.init_watcher() def init_watcher(self): try: self.watcher = LogWatcher(parent=self, log=self.log) except: self.log.error("Couldn't start the LogWatcher", exc_info=True) self.exit(1) self.log.info("Listening for log messages on %r" % self.watcher.url) def start(self): self.watcher.start() try: self.watcher.loop.start() except KeyboardInterrupt: self.log.critical("Logging Interrupted, shutting down...\n") launch_new_instance = IPLoggerApp.launch_instance if __name__ == '__main__': launch_new_instance() ipyparallel-7.1.0/ipyparallel/apps/launcher.py000066400000000000000000000003741412533144600215000ustar00rootroot00000000000000"""Deprecated import for ipyparallel.cluster.launcher""" import warnings from ipyparallel.cluster.launcher import * # noqa warnings.warn( f"{__name__} is deprecated in ipyparallel 7. Use ipyparallel.cluster.launcher.", DeprecationWarning, ) ipyparallel-7.1.0/ipyparallel/apps/logwatcher.py000066400000000000000000000061201412533144600220310ustar00rootroot00000000000000""" A logger object that consolidates messages incoming from ipcluster processes. """ import logging import zmq from jupyter_client.localinterfaces import localhost from traitlets import Instance from traitlets import List from traitlets import Unicode from traitlets.config.configurable import LoggingConfigurable from zmq.eventloop import ioloop from zmq.eventloop import zmqstream class LogWatcher(LoggingConfigurable): """A simple class that receives messages on a SUB socket, as published by subclasses of `zmq.log.handlers.PUBHandler`, and logs them itself. This can subscribe to multiple topics, but defaults to all topics. """ # configurables topics = List( [''], config=True, help="The ZMQ topics to subscribe to. Default is to subscribe to all messages", ) url = Unicode(config=True, help="ZMQ url on which to listen for log messages") def _url_default(self): return 'tcp://%s:20202' % localhost() # internals stream = Instance('zmq.eventloop.zmqstream.ZMQStream', allow_none=True) context = Instance(zmq.Context) def _context_default(self): return zmq.Context.instance() loop = Instance('tornado.ioloop.IOLoop') def _loop_default(self): return ioloop.IOLoop.current() def __init__(self, **kwargs): super(LogWatcher, self).__init__(**kwargs) s = self.context.socket(zmq.SUB) s.bind(self.url) self.stream = zmqstream.ZMQStream(s, self.loop) self.subscribe() self.on_trait_change(self.subscribe, 'topics') def start(self): self.stream.on_recv(self.log_message) def stop(self): self.stream.stop_on_recv() def subscribe(self): """Update our SUB socket's subscriptions.""" self.stream.setsockopt(zmq.UNSUBSCRIBE, '') if '' in self.topics: self.log.debug("Subscribing to: everything") self.stream.setsockopt(zmq.SUBSCRIBE, '') else: for topic in self.topics: self.log.debug("Subscribing to: %r" % (topic)) self.stream.setsockopt(zmq.SUBSCRIBE, topic) def _extract_level(self, topic_str): """Turn 'engine.0.INFO.extra' into (logging.INFO, 'engine.0.extra')""" topics = topic_str.split('.') for idx, t in enumerate(topics): level = getattr(logging, t, None) if level is not None: break if level is None: level = logging.INFO else: topics.pop(idx) return level, '.'.join(topics) def log_message(self, raw): """receive and parse a message, then log it.""" if len(raw) != 2 or '.' not in raw[0]: self.log.error("Invalid log message: %s" % raw) return else: topic, msg = raw # don't newline, since log messages always newline: topic, level_name = topic.rsplit('.', 1) level, topic = self._extract_level(topic) if msg[-1] == '\n': msg = msg[:-1] self.log.log(level, "[%s] %s" % (topic, msg)) ipyparallel-7.1.0/ipyparallel/client/000077500000000000000000000000001412533144600176345ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/client/__init__.py000066400000000000000000000000001412533144600217330ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/client/_joblib.py000066400000000000000000000044541412533144600216150ustar00rootroot00000000000000"""joblib parallel backend for IPython Parallel""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import absolute_import from joblib.parallel import AutoBatchingMixin from joblib.parallel import ParallelBackendBase import ipyparallel as ipp class IPythonParallelBackend(AutoBatchingMixin, ParallelBackendBase): def __init__(self, view=None, **kwargs): super().__init__(**kwargs) self._cluster_owner = False self._client_owner = False if view is None: self._client_owner = True try: # load the default cluster cluster = ipp.Cluster.from_file() except FileNotFoundError: # other load errors? cluster = self._cluster = ipp.Cluster() self._cluster_owner = True cluster.start_cluster_sync() else: # cluster running, ensure some engines are, too if not cluster.engines: cluster.start_engines_sync() rc = cluster.connect_client_sync() rc.wait_for_engines(cluster.n or 1) view = rc.load_balanced_view() # use cloudpickle or dill for closures, if available. # joblib tends to create closures default pickle can't handle. try: import cloudpickle # noqa except ImportError: try: import dill # noqa except ImportError: pass else: view.client[:].use_dill() else: view.client[:].use_cloudpickle() self._view = view def effective_n_jobs(self, n_jobs): """A View can run len(view) jobs at a time""" return len(self._view) def terminate(self): """Close the client if we created it""" if self._client_owner: self._view.client.close() if self._cluster_owner: self._cluster.stop_cluster_sync() def apply_async(self, func, callback=None): """Schedule a func to be run""" future = self._view.apply_async(func) if callback: future.add_done_callback(lambda f: callback(f.result())) return future ipyparallel-7.1.0/ipyparallel/client/asyncresult.py000066400000000000000000001016511412533144600225660ustar00rootroot00000000000000"""AsyncResult objects for the client""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import print_function import sys import threading import time from concurrent.futures import Future from contextlib import contextmanager from datetime import datetime from functools import partial from queue import Queue from threading import Event import zmq from decorator import decorator from IPython import get_ipython from IPython.display import display from IPython.display import display_pretty from IPython.display import publish_display_data from .futures import MessageFuture from .futures import multi_future from ipyparallel import error from ipyparallel.util import _parse_date from ipyparallel.util import compare_datetimes from ipyparallel.util import progress from ipyparallel.util import utcnow def _raw_text(s): display_pretty(s, raw=True) _default = object() # global empty tracker that's always done: finished_tracker = zmq.MessageTracker() @decorator def check_ready(f, self, *args, **kwargs): """Check ready state prior to calling the method.""" self.wait(0) if not self._ready: raise TimeoutError("result not ready") return f(self, *args, **kwargs) _metadata_keys = [] # threading.TIMEOUT_MAX new in 3.2 _FOREVER = getattr(threading, 'TIMEOUT_MAX', int(1e6)) class AsyncResult(Future): """Class for representing results of non-blocking calls. Extends the interfaces of :py:class:`multiprocessing.pool.AsyncResult` and :py:class:`concurrent.futures.Future`. """ msg_ids = None _targets = None _tracker = None _single_result = False owner = False _last_display_prefix = "" _stream_trailing_newline = True def __init__( self, client, children, fname='unknown', targets=None, owner=False, return_exceptions=False, ): super(AsyncResult, self).__init__() if not isinstance(children, list): children = [children] self._single_result = True else: self._single_result = False self._return_exceptions = return_exceptions if isinstance(children[0], str): self.msg_ids = children self._children = [] else: self._children = children self.msg_ids = [f.msg_id for f in children] self._client = client self._fname = fname self._targets = targets self.owner = owner self._ready = False self._ready_event = Event() self._output_ready = False self._output_event = Event() self._sent_event = Event() self._success = None if self._children: self._metadata = [f.output.metadata for f in self._children] else: self._metadata = [self._client.metadata[id] for id in self.msg_ids] self._init_futures() def _init_futures(self): """Build futures for results and output; hook up callbacks""" if not self._children: for msg_id in self.msg_ids: future = self._client._futures.get(msg_id, None) if not future: result = self._client.results.get(msg_id, _default) # result resides in local cache, construct already-resolved Future if result is not _default: future = MessageFuture(msg_id) future.output = Future() future.output.metadata = self.client.metadata[msg_id] future.set_result(result) future.output.set_result(None) if not future: raise KeyError("No Future or result for msg_id: %s" % msg_id) self._children.append(future) self._result_future = multi_future(self._children) self._sent_future = multi_future([f.tracker for f in self._children]) self._sent_future.add_done_callback(self._handle_sent) self._output_future = multi_future( [self._result_future] + [f.output for f in self._children] ) # on completion of my constituents, trigger my own resolution self._result_future.add_done_callback(self._resolve_result) self._output_future.add_done_callback(self._resolve_output) self.add_done_callback(self._finalize_result) def _iopub_streaming_output_callback(self, eid, msg_future, msg): """Callback registered during AsyncResult.stream_output()""" msg_type = msg['header']['msg_type'] ip = get_ipython() if ip is not None: in_kernel = getattr(ip, 'kernel', None) is not None else: in_kernel = False if msg_type == 'stream': msg_content = msg['content'] stream_name = msg_content['name'] if in_kernel: parent_msg_id = msg['parent_header']['msg_id'] display_id = f"{parent_msg_id}-{stream_name}" md = msg_future.output.metadata full_stream = md[stream_name] if display_id in self._already_streamed: update = True else: self._already_streamed[display_id] = True update = False publish_display_data( { "text/plain": f"[{stream_name}:{eid}] " + full_stream, }, transient={"display_id": display_id}, update=update, ) return else: stream = getattr(sys, stream_name, sys.stdout) self._display_stream( msg_content['text'], f'[{stream_name}:{eid}] ', file=stream, ) if ip is None: return if msg_type == 'display_data': msg_content = msg['content'] _raw_text('[output:%i]' % eid) self._republish_displaypub(msg_content, eid) @contextmanager def stream_output(self): """Stream output for this result as it arrives. Returns a context manager, during which output is streamed. """ # Keep a handle on the futures so we can remove the callback later future_callbacks = {} self._already_streamed = {} self._stream_trailing_newline = True self._last_display_prefix = "" for eid, msg_future in zip(self._targets, self._children): callback_func = partial( self._iopub_streaming_output_callback, eid, msg_future ) future_callbacks[msg_future] = callback_func msg_future.iopub_callbacks.append(callback_func) try: yield finally: # clear stream cache self._already_streamed = {} # Remove the callback for msg_future, callback in future_callbacks.items(): msg_future.iopub_callbacks.remove(callback) def __repr__(self): if self._ready: return "<%s: %s:finished>" % (self.__class__.__name__, self._fname) else: return "<%s: %s>" % (self.__class__.__name__, self._fname) def __dir__(self): keys = dir(self.__class__) if not _metadata_keys: from .client import Metadata _metadata_keys.extend(Metadata().keys()) keys.extend(_metadata_keys) return keys def _reconstruct_result(self, res): """Reconstruct our result from actual result list (always a list) Override me in subclasses for turning a list of results into the expected form. """ if self._single_result: return res[0] else: return res def get(self, timeout=None, return_exceptions=None): """Return the result when it arrives. Arguments: timeout : int [default None] If `timeout` is not ``None`` and the result does not arrive within `timeout` seconds then ``TimeoutError`` is raised. If the remote call raised an exception then that exception will be reraised by get() inside a `RemoteError`. return_exceptions : bool [default False] If True, return Exceptions instead of raising them. """ if not self.ready(): self.wait(timeout) if return_exceptions is None: # default to attribute, if AsyncResult was created with return_exceptions=True return_exceptions = self._return_exceptions if self._ready: if self._success: return self.result() else: e = self.exception() if return_exceptions: return self._reconstruct_result(self._raw_results) else: raise e else: raise TimeoutError("Result not ready.") def _check_ready(self): if not self.ready(): raise TimeoutError("Result not ready.") def ready(self): """Return whether the call has completed.""" if not self._ready: self.wait(0) return self._ready def wait_for_output(self, timeout=-1): """Wait for our output to be complete. AsyncResult.wait only waits for the result, which may arrive before output is complete. """ if self._output_ready: return True if timeout and timeout < 0: timeout = None return self._output_event.wait(timeout) def _resolve_output(self, f=None): """Callback that fires when outputs are ready""" if self.owner: [self._client.metadata.pop(mid, None) for mid in self.msg_ids] self._output_ready = True self._output_event.set() def wait(self, timeout=-1): """Wait until the result is available or until `timeout` seconds pass. This method always returns None. """ if self._ready: return True if timeout and timeout < 0: timeout = None self._ready_event.wait(timeout) self.wait_for_output(0) return self._ready def _resolve_result(self, f=None): if self.done(): return if f: results = f.result() else: results = list(map(self._client.results.get, self.msg_ids)) # store raw results self._raw_results = results try: if self._single_result: r = results[0] if isinstance(r, Exception): raise r else: results = self._collect_exceptions(results) except Exception as e: self._success = False self.set_exception(e) else: self._success = True self.set_result(self._reconstruct_result(results)) def _collect_exceptions(self, results): """Wrap Exceptions in a CompositeError if self._return_exceptions is True, this is a no-op """ if self._return_exceptions: return results else: return error.collect_exceptions(results, self._fname) def _finalize_result(self, f): if self.owner: [self._client.results.pop(mid, None) for mid in self.msg_ids] self._ready = True self._ready_event.set() def successful(self): """Return whether the call completed without raising an exception. Will raise ``AssertionError`` if the result is not ready. """ assert self.ready() return self._success # ---------------------------------------------------------------- # Extra methods not in mp.pool.AsyncResult # ---------------------------------------------------------------- def get_dict(self, timeout=-1): """Get the results as a dict, keyed by engine_id. timeout behavior is described in `get()`. """ results = self.get(timeout) if self._single_result: results = [results] engine_ids = [md['engine_id'] for md in self._metadata] rdict = {} for engine_id, result in zip(engine_ids, results): if engine_id in rdict: raise ValueError( "Cannot build dict, %i jobs ran on engine #%i" % (engine_ids.count(engine_id), engine_id) ) else: rdict[engine_id] = result return rdict @property def r(self): """result property wrapper for `get(timeout=-1)`.""" return self.get() _DATE_FIELDS = [ "submitted", "started", "completed", "received", ] def _parse_metadata_dates(self): """Ensure metadata date fields are parsed on access Rather than parsing timestamps from str->dt on receipt, parse on access for compatibility. """ for md in self._metadata: for key in self._DATE_FIELDS: if isinstance(md.get(key, None), str): md[key] = _parse_date(md[key]) @property def metadata(self): """property for accessing execution metadata.""" self._parse_metadata_dates() if self._single_result: return self._metadata[0] else: return self._metadata @property def result_dict(self): """result property as a dict.""" return self.get_dict() def __dict__(self): return self.get_dict(0) def abort(self): """Abort my tasks, if possible. Only tasks that have not started yet can be aborted. """ assert not self.ready(), "Can't abort, I am already done!" return self._client.abort(self.msg_ids, targets=self._targets, block=True) def _handle_sent(self, f): """Resolve sent Future, build MessageTracker""" trackers = f.result() trackers = [t for t in trackers if t is not None] self._tracker = zmq.MessageTracker(*trackers) self._sent_event.set() @property def sent(self): """check whether my messages have been sent.""" return self._sent_event.is_set() and self._tracker.done def wait_for_send(self, timeout=-1): """wait for pyzmq send to complete. This is necessary when sending arrays that you intend to edit in-place. `timeout` is in seconds, and will raise TimeoutError if it is reached before the send completes. """ if not self._sent_event.is_set(): if timeout and timeout < 0: # Event doesn't like timeout < 0 timeout = None elif timeout == 0: raise TimeoutError("Still waiting to be sent") # wait for Future to indicate send having been called, # which means MessageTracker is ready. tic = time.time() if not self._sent_event.wait(timeout): raise TimeoutError("Still waiting to be sent") if timeout: timeout = max(0, timeout - (time.time() - tic)) try: if timeout is None: # MessageTracker doesn't like timeout=None timeout = -1 return self._tracker.wait(timeout) except zmq.NotDone: raise TimeoutError("Still waiting to be sent") # ------------------------------------- # dict-access # ------------------------------------- def __getitem__(self, key): """getitem returns result value(s) if keyed by int/slice, or metadata if key is str.""" if isinstance(key, int): self._check_ready() return self._collect_exceptions([self.result()[key]])[0] elif isinstance(key, slice): self._check_ready() return self._collect_exceptions(self.result()[key]) elif isinstance(key, str): # metadata proxy *does not* require that results are done self.wait(0) self.wait_for_output(0) self._parse_metadata_dates() values = [md[key] for md in self._metadata] if self._single_result: return values[0] else: return values else: raise TypeError( "Invalid key type %r, must be 'int','slice', or 'str'" % type(key) ) def __getattr__(self, key): """getattr maps to getitem for convenient attr access to metadata.""" try: return self.__getitem__(key) except (TimeoutError, KeyError): raise AttributeError( "%r object has no attribute %r" % (self.__class__.__name__, key) ) @staticmethod def _wait_for_child(child, evt, timeout=_FOREVER): """Wait for a child to be done""" if child.done(): return evt.clear() child.add_done_callback(lambda f: evt.set()) evt.wait(timeout) # asynchronous iterator: def __iter__(self): if self._single_result: raise TypeError("AsyncResults with a single result are not iterable.") try: rlist = self.get(0) except TimeoutError: # wait for each result individually evt = Event() for child in self._children: self._wait_for_child(child, evt=evt) result = child.result() self._collect_exceptions([result]) yield result else: # already done for r in rlist: yield r def __len__(self): return len(self.msg_ids) # ------------------------------------- # Sugar methods and attributes # ------------------------------------- def timedelta(self, start, end, start_key=min, end_key=max): """compute the difference between two sets of timestamps The default behavior is to use the earliest of the first and the latest of the second list, but this can be changed by passing a different Parameters ---------- start : one or more datetime objects (e.g. ar.submitted) end : one or more datetime objects (e.g. ar.received) start_key : callable Function to call on `start` to extract the relevant entry [default: min] end_key : callable Function to call on `end` to extract the relevant entry [default: max] Returns ------- dt : float The time elapsed (in seconds) between the two selected timestamps. """ if not isinstance(start, datetime): # handle single_result AsyncResults, where ar.stamp is single object, # not a list start = start_key(start) if not isinstance(end, datetime): # handle single_result AsyncResults, where ar.stamp is single object, # not a list end = end_key(end) return compare_datetimes(end, start).total_seconds() @property def progress(self): """the number of tasks which have been completed at this point. Fractional progress would be given by 1.0 * ar.progress / len(ar) """ self.wait(0) return len(self) - len(set(self.msg_ids).intersection(self._client.outstanding)) @property def elapsed(self): """elapsed time since initial submission""" if self.ready(): return self.wall_time now = submitted = utcnow() self._parse_metadata_dates() for md in self._metadata: stamp = md["submitted"] if stamp and stamp < submitted: submitted = stamp return compare_datetimes(now, submitted).total_seconds() @property @check_ready def serial_time(self): """serial computation time of a parallel calculation Computed as the sum of (completed-started) of each task """ t = 0 self._parse_metadata_dates() for md in self._metadata: t += compare_datetimes(md['completed'], md['started']).total_seconds() return t @property @check_ready def wall_time(self): """actual computation time of a parallel calculation Computed as the time between the latest `received` stamp and the earliest `submitted`. For similar comparison of other timestamp pairs, check out AsyncResult.timedelta. """ return self.timedelta(self.submitted, self.received) def wait_interactive(self, interval=0.1, timeout=-1, widget=None): """interactive wait, printing progress at regular intervals. Parameters ---------- interval : float Interval on which to update progress display. timeout : float Time (in seconds) to wait before raising a TimeoutError. -1 (default) means no timeout. widget : bool default: True if in an IPython kernel (notebook), False otherwise. Override default context-detection behavior for whether a widget-based progress bar should be used. """ if timeout and timeout < 0: timeout = None N = len(self) tic = time.perf_counter() progress_bar = progress(widget=widget, total=N, unit='tasks', desc=self._fname) while not self.ready() and ( timeout is None or time.perf_counter() - tic <= timeout ): self.wait(interval) progress_bar.update(self.progress - progress_bar.n) progress_bar.update(self.progress - progress_bar.n) progress_bar.close() def _republish_displaypub(self, content, eid): """republish individual displaypub content dicts""" ip = get_ipython() if ip is None: # displaypub is meaningless outside IPython return md = content['metadata'] or {} md['engine'] = eid ip.display_pub.publish(data=content['data'], metadata=md) def _display_stream(self, text, prefix='', file=None): """Redisplay a stream""" if not text: # nothing to display return if file is None: file = sys.stdout end = "" if prefix: if prefix == self._last_display_prefix: # same prefix, no need to re-display prefix = "" else: self._last_display_prefix = prefix if prefix and not self._stream_trailing_newline: # prefix changed, no trailing newline; insert newline pre = "\n" else: pre = "" if prefix: sep = "\n" else: sep = "" self._stream_trailing_newline = text.endswith("\n") print(f"{pre}{prefix}{sep}{text}", file=file, end="") def _display_single_result(self, result_only=False): if not result_only: self._display_stream(self.stdout) self._display_stream(self.stderr, file=sys.stderr) if get_ipython() is None: # displaypub is meaningless outside IPython return if not result_only: for output in self.outputs: self._republish_displaypub(output, self.engine_id) if self.execute_result is not None: display(self.get()) @check_ready def display_outputs(self, groupby="type", result_only=False): """republish the outputs of the computation Parameters ---------- groupby : str [default: type] if 'type': Group outputs by type (show all stdout, then all stderr, etc.): [stdout:1] foo [stdout:2] foo [stderr:1] bar [stderr:2] bar if 'engine': Display outputs for each engine before moving on to the next: [stdout:1] foo [stderr:1] bar [stdout:2] foo [stderr:2] bar if 'order': Like 'type', but further collate individual displaypub outputs. This is meant for cases of each command producing several plots, and you would like to see all of the first plots together, then all of the second plots, and so on. result_only: boolean [default: False] Only display the execution result and skip stdout, stderr and display-outputs. Usually used when using streaming output since these outputs would have already been displayed. """ self.wait_for_output() if self._single_result: self._display_single_result(result_only=result_only) return stdouts = self.stdout stderrs = self.stderr execute_results = self.execute_result output_lists = self.outputs results = self.get() targets = self.engine_id if groupby == "engine": for eid, stdout, stderr, outputs, r, execute_result in zip( targets, stdouts, stderrs, output_lists, results, execute_results ): if not result_only: self._display_stream(stdout, '[stdout:%i] ' % eid) self._display_stream(stderr, '[stderr:%i] ' % eid, file=sys.stderr) if get_ipython() is None: # displaypub is meaningless outside IPython continue if (outputs and not result_only) or execute_result is not None: _raw_text('[output:%i]' % eid) if not result_only: for output in outputs: self._republish_displaypub(output, eid) if execute_result is not None: display(r) elif groupby in ('type', 'order'): if not result_only: # republish stdout: for eid, stdout in zip(targets, stdouts): self._display_stream(stdout, '[stdout:%i] ' % eid) # republish stderr: for eid, stderr in zip(targets, stderrs): self._display_stream(stderr, '[stderr:%i] ' % eid, file=sys.stderr) if get_ipython() is None: # displaypub is meaningless outside IPython return if not result_only: if groupby == 'order': output_dict = dict( (eid, outputs) for eid, outputs in zip(targets, output_lists) ) N = max(len(outputs) for outputs in output_lists) for i in range(N): for eid in targets: outputs = output_dict[eid] if len(outputs) >= N: _raw_text('[output:%i]' % eid) self._republish_displaypub(outputs[i], eid) else: # republish displaypub output for eid, outputs in zip(targets, output_lists): if outputs: _raw_text('[output:%i]' % eid) for output in outputs: self._republish_displaypub(output, eid) # finally, add execute_result: for eid, r, execute_result in zip(targets, results, execute_results): if execute_result is not None: display(r) else: raise ValueError( "groupby must be one of 'type', 'engine', 'collate', not %r" % groupby ) class AsyncMapResult(AsyncResult): """Class for representing results of non-blocking maps. AsyncMapResult.get() will properly reconstruct gathers into single object. AsyncMapResult is iterable at any time, and will wait on results as they come. If ordered=False, then the first results to arrive will come first, otherwise results will be yielded in the order they were submitted. """ def __init__( self, client, children, mapObject, fname='', ordered=True, return_exceptions=False, ): self._mapObject = mapObject self.ordered = ordered AsyncResult.__init__( self, client, children, fname=fname, return_exceptions=return_exceptions, ) self._single_result = False def _reconstruct_result(self, res): """Perform the gather on the actual results.""" if self._return_exceptions: if any(isinstance(r, Exception) for r in res): # running with _return_exceptions, # cannot reconstruct original # use simple chain iterable flattened = [] for r in res: if isinstance(r, Exception): flattened.append(r) else: flattened.extend(r) return flattened return self._mapObject.joinPartitions(res) # asynchronous iterator: def __iter__(self): it = self._ordered_iter if self.ordered else self._unordered_iter for r in it(): yield r def _yield_child_results(self, child): """Yield results from a child for use in iterator methods """ rlist = child.result() if not isinstance(rlist, list): rlist = [rlist] self._collect_exceptions(rlist) for r in rlist: yield r # asynchronous ordered iterator: def _ordered_iter(self): """iterator for results *as they arrive*, preserving submission order.""" try: rlist = self.get(0) except TimeoutError: # wait for each result individually evt = Event() for child in self._children: self._wait_for_child(child, evt=evt) for r in self._yield_child_results(child): yield r else: # already done for r in rlist: yield r # asynchronous unordered iterator: def _unordered_iter(self): """iterator for results *as they arrive*, on FCFS basis, ignoring submission order.""" try: rlist = self.get(0) except TimeoutError: queue = Queue() for child in self._children: child.add_done_callback(queue.put) for i in range(len(self)): # use very-large timeout because no-timeout is not interruptible child = queue.get(timeout=_FOREVER) for r in self._yield_child_results(child): yield r else: # already done for r in rlist: yield r class AsyncHubResult(AsyncResult): """Class to wrap pending results that must be requested from the Hub. Note that waiting/polling on these objects requires polling the Hub over the network, so use `AsyncHubResult.wait()` sparingly. """ def _init_futures(self): """disable Future-based resolution of Hub results""" pass def wait(self, timeout=-1): """wait for result to complete.""" start = time.time() if timeout and timeout < 0: timeout = None if self._ready: return local_ids = [m for m in self.msg_ids if m in self._client.outstanding] local_ready = self._client.wait(local_ids, timeout) if local_ready: remote_ids = [m for m in self.msg_ids if m not in self._client.results] if not remote_ids: self._ready = True else: rdict = self._client.result_status(remote_ids, status_only=False) pending = rdict['pending'] while pending and (timeout is None or time.time() < start + timeout): rdict = self._client.result_status(remote_ids, status_only=False) pending = rdict['pending'] if pending: time.sleep(0.1) if not pending: self._ready = True if self._ready: self._output_ready = True try: results = list(map(self._client.results.get, self.msg_ids)) if self._single_result: r = results[0] if isinstance(r, Exception) and not self._return_exceptions: raise r else: results = self._collect_exceptions(results) self._success = True self.set_result(self._reconstruct_result(results)) except Exception as e: self._success = False self.set_exception(e) finally: if self.owner: [self._client.metadata.pop(mid) for mid in self.msg_ids] [self._client.results.pop(mid) for mid in self.msg_ids] __all__ = ['AsyncResult', 'AsyncMapResult', 'AsyncHubResult'] ipyparallel-7.1.0/ipyparallel/client/client.py000066400000000000000000002617561412533144600215050ustar00rootroot00000000000000"""A semi-synchronous Client for IPython parallel""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import print_function import asyncio import json import os import re import socket import time import types import warnings from collections.abc import Iterable from concurrent.futures import Future from functools import partial from getpass import getpass from pprint import pprint from threading import current_thread from threading import Event from threading import Thread import jupyter_client.session import zmq from decorator import decorator from ipykernel.comm import Comm from IPython import get_ipython from IPython.core.application import BaseIPythonApplication from IPython.core.profiledir import ProfileDir from IPython.core.profiledir import ProfileDirError from IPython.paths import get_ipython_dir from IPython.utils.capture import RichOutput from IPython.utils.coloransi import TermColors from IPython.utils.path import compress_user from jupyter_client.localinterfaces import is_local_ip from jupyter_client.localinterfaces import localhost from jupyter_client.session import Session from tornado import ioloop from traitlets import Any from traitlets import Bool from traitlets import Bytes from traitlets import default from traitlets import Dict from traitlets import HasTraits from traitlets import Instance from traitlets import List from traitlets import Set from traitlets import Unicode from traitlets.config.configurable import MultipleInstanceError from zmq.eventloop.zmqstream import ZMQStream from .asyncresult import AsyncHubResult from .asyncresult import AsyncResult from .futures import MessageFuture from .futures import multi_future from .view import BroadcastView from .view import DirectView from .view import LoadBalancedView from ipyparallel import error from ipyparallel import serialize from ipyparallel import util from ipyparallel.serialize import PrePickled from ipyparallel.serialize import Reference pjoin = os.path.join jupyter_client.session.extract_dates = lambda obj: obj # -------------------------------------------------------------------------- # Decorators for Client methods # -------------------------------------------------------------------------- @decorator def unpack_message(f, self, msg_parts): """Unpack a message before calling the decorated method.""" idents, msg = self.session.feed_identities(msg_parts, copy=False) try: msg = self.session.deserialize(msg, content=True, copy=False) except: self.log.error("Invalid Message", exc_info=True) else: if self.debug: pprint(msg) return f(self, msg) # -------------------------------------------------------------------------- # Classes # -------------------------------------------------------------------------- _no_connection_file_msg = """ Failed to connect because no Controller could be found. Please double-check your profile and ensure that a cluster is running. """ class ExecuteReply(RichOutput): """wrapper for finished Execute results""" def __init__(self, msg_id, content, metadata): self.msg_id = msg_id self._content = content self.execution_count = content['execution_count'] self.metadata = metadata # RichOutput overrides @property def source(self): execute_result = self.metadata['execute_result'] if execute_result: return execute_result.get('source', '') @property def data(self): execute_result = self.metadata['execute_result'] if execute_result: return execute_result.get('data', {}) return {} @property def _metadata(self): execute_result = self.metadata['execute_result'] if execute_result: return execute_result.get('metadata', {}) return {} def display(self): from IPython.display import publish_display_data publish_display_data(self.data, self.metadata) def _repr_mime_(self, mime): if mime not in self.data: return data = self.data[mime] if mime in self._metadata: return data, self._metadata[mime] else: return data def _repr_mimebundle_(self, *args, **kwargs): data, md = self.data, self.metadata if 'text/plain' in data: data = data.copy() data['text/plain'] = self._plaintext() return data, md def __getitem__(self, key): return self.metadata[key] def __getattr__(self, key): if key not in self.metadata: raise AttributeError(key) return self.metadata[key] def __repr__(self): execute_result = self.metadata['execute_result'] or {'data': {}} text_out = execute_result['data'].get('text/plain', '') if len(text_out) > 32: text_out = text_out[:29] + '...' return "" % (self.execution_count, text_out) def _plaintext(self): execute_result = self.metadata['execute_result'] or {'data': {}} text_out = execute_result['data'].get('text/plain', '') if not text_out: return '' ip = get_ipython() if ip is None: colors = "NoColor" else: colors = ip.colors if colors == "NoColor": out = normal = "" else: out = TermColors.Red normal = TermColors.Normal if '\n' in text_out and not text_out.startswith('\n'): # add newline for multiline reprs text_out = '\n' + text_out return u''.join( [ out, u'Out[%i:%i]: ' % (self.metadata['engine_id'], self.execution_count), normal, text_out, ] ) def _repr_pretty_(self, p, cycle): p.text(self._plaintext()) class Metadata(dict): """Subclass of dict for initializing metadata values. Attribute access works on keys. These objects have a strict set of keys - errors will raise if you try to add new keys. """ def __init__(self, *args, **kwargs): dict.__init__(self) md = { 'msg_id': None, 'submitted': None, 'started': None, 'completed': None, 'received': None, 'engine_uuid': None, 'engine_id': None, 'follow': None, 'after': None, 'status': None, 'execute_input': None, 'execute_result': None, 'error': None, 'stdout': '', 'stderr': '', 'outputs': [], 'data': {}, } self.update(md) self.update(dict(*args, **kwargs)) def __getattr__(self, key): """getattr aliased to getitem""" if key in self: return self[key] else: raise AttributeError(key) def __setattr__(self, key, value): """setattr aliased to setitem, with strict""" if key in self: self[key] = value else: raise AttributeError(key) def __setitem__(self, key, value): """strict static key enforcement""" if key in self: dict.__setitem__(self, key, value) else: raise KeyError(key) def _is_future(f): """light duck-typing check for Futures""" return hasattr(f, 'add_done_callback') # carriage return pattern _cr_pat = re.compile(r'.*\r(?=[^\n])') class Client(HasTraits): """A semi-synchronous client to an IPython parallel cluster Parameters ---------- connection_info : str or dict The path to ipcontroller-client.json, or a dict containing the same information. This JSON file should contain all the information needed to connect to a cluster, and is usually the only argument needed. [Default: use profile] profile : str The name of the Cluster profile to be used to find connector information. If run from an IPython application, the default profile will be the same as the running application, otherwise it will be 'default'. cluster_id : str String id to added to runtime files, to prevent name collisions when using multiple clusters with a single profile simultaneously. When set, will look for files named like: 'ipcontroller--client.json' Since this is text inserted into filenames, typical recommendations apply: Simple character strings are ideal, and spaces are not recommended (but should generally work) context : zmq.Context Pass an existing zmq.Context instance, otherwise the client will create its own. debug : bool flag for lots of message printing for debug purposes timeout : float time (in seconds) to wait for connection replies from the Hub [Default: 10] Other Parameters ---------------- sshserver : str A string of the form passed to ssh, i.e. 'server.tld' or 'user@server.tld:port' If keyfile or password is specified, and this is not, it will default to the ip given in addr. sshkey : str; path to ssh private key file This specifies a key to be used in ssh login, default None. Regular default ssh keys will be used without specifying this argument. password : str Your ssh password to sshserver. Note that if this is left None, you will be prompted for it if passwordless key based login is unavailable. paramiko : bool flag for whether to use paramiko instead of shell ssh for tunneling. [default: True on win32, False else] Attributes ---------- ids : list of int engine IDs requesting the ids attribute always synchronizes the registration state. To request ids without synchronization, use semi-private _ids attributes. history : list of msg_ids a list of msg_ids, keeping track of all the execution messages you have submitted in order. outstanding : set of msg_ids a set of msg_ids that have been submitted, but whose results have not yet been received. results : dict a dict of all our results, keyed by msg_id block : bool determines default behavior when block not specified in execution methods """ block = Bool(False) outstanding = Set() results = Instance('collections.defaultdict', (dict,)) metadata = Instance('collections.defaultdict', (Metadata,)) cluster = Instance('ipyparallel.cluster.Cluster', allow_none=True) history = List() debug = Bool(False) _futures = Dict() _output_futures = Dict() _io_loop = Any() _io_thread = Any() profile = Unicode() def _profile_default(self): if BaseIPythonApplication.initialized(): # an IPython app *might* be running, try to get its profile try: return BaseIPythonApplication.instance().profile except (AttributeError, MultipleInstanceError): # could be a *different* subclass of config.Application, # which would raise one of these two errors. return u'default' else: return u'default' _outstanding_dict = Instance('collections.defaultdict', (set,)) _ids = List() _connected = Bool(False) _ssh = Bool(False) _context = Instance('zmq.Context', allow_none=True) @default("_context") def _default_context(self): return zmq.Context.instance() _config = Dict() _engines = Instance(util.ReverseDict, (), {}) _query_socket = Instance('zmq.Socket', allow_none=True) _control_socket = Instance('zmq.Socket', allow_none=True) _iopub_socket = Instance('zmq.Socket', allow_none=True) _notification_socket = Instance('zmq.Socket', allow_none=True) _mux_socket = Instance('zmq.Socket', allow_none=True) _task_socket = Instance('zmq.Socket', allow_none=True) _broadcast_socket = Instance('zmq.Socket', allow_none=True) _registration_callbacks = List() curve_serverkey = Bytes(allow_none=True) curve_secretkey = Bytes(allow_none=True) curve_publickey = Bytes(allow_none=True) _task_scheme = Unicode() _closed = False def __new__(self, *args, **kw): # don't raise on positional args return HasTraits.__new__(self, **kw) def __init__( self, connection_info=None, *, url_file=None, profile=None, profile_dir=None, ipython_dir=None, context=None, debug=False, sshserver=None, sshkey=None, password=None, paramiko=None, timeout=10, cluster_id=None, cluster=None, **extra_args, ): super_kwargs = {'debug': debug, 'cluster': cluster} if profile: super_kwargs['profile'] = profile super(Client, self).__init__(**super_kwargs) if context is not None: self._context = context for argname in ('url_or_file', 'url_file'): if argname in extra_args: connection_info = extra_args[argname] warnings.warn( f"{argname} arg no longer supported, use positional connection_info argument", DeprecationWarning, stacklevel=2, ) if isinstance(connection_info, str) and util.is_url(connection_info): raise ValueError( f"single urls ({connection_info!r}) cannot be specified, url-files must be used." ) self._setup_profile_dir(self.profile, profile_dir, ipython_dir) no_file_msg = '\n'.join( [ "You have attempted to connect to an IPython Cluster but no Controller could be found.", "Please double-check your configuration and ensure that a cluster is running.", ] ) if connection_info is None and self._profile_dir is not None: # default: find connection info from profile if cluster_id: client_json = f'ipcontroller-{cluster_id}-client.json' else: client_json = 'ipcontroller-client.json' connection_file = pjoin(self._profile_dir.security_dir, client_json) short = compress_user(connection_file) if not os.path.exists(connection_file): print(f"Waiting for connection file: {short}") waiting_time = 0.0 while waiting_time < timeout: time.sleep(min(timeout - waiting_time, 1)) waiting_time += 1 if os.path.exists(connection_file): break if not os.path.exists(connection_file): msg = '\n'.join([f"Connection file {short!r} not found.", no_file_msg]) raise IOError(msg) with open(connection_file) as f: connection_info = json.load(f) if connection_info is None: raise IOError(no_file_msg) if isinstance(connection_info, dict): cfg = connection_info.copy() else: # connection_info given as path to connection file connection_file = connection_info if not os.path.exists(connection_file): # Connection file explicitly specified, but not found raise IOError( f"Connection file {compress_user(connection_file)} not found. Is a controller running?" ) with open(connection_file) as f: connection_info = cfg = json.load(f) self._task_scheme = cfg['task_scheme'] if not cfg.get("curve_serverkey") and "IPP_CURVE_SERVERKEY" in os.environ: # load from env, if not set in connection file cfg["curve_serverkey"] = os.environ["IPP_CURVE_SERVERKEY"] if cfg.get("curve_serverkey"): self.curve_serverkey = cfg["curve_serverkey"].encode('ascii') if not self.curve_publickey or not self.curve_secretkey: # if context: this could crash! # inappropriately closes libsodium random_bytes source # with libzmq <= 4.3.4 self.curve_publickey, self.curve_secretkey = zmq.curve_keypair() # sync defaults from args, json: if sshserver: cfg['ssh'] = sshserver location = cfg.setdefault('location', None) proto, addr = cfg['interface'].split('://') addr = util.disambiguate_ip_address(addr, location) cfg['interface'] = "%s://%s" % (proto, addr) # turn interface,port into full urls: for key in ( 'control', 'task', 'mux', 'iopub', 'notification', 'registration', 'broadcast', ): cfg[key] = cfg['interface'] + ':%i' % cfg[key] url = cfg['registration'] if location is not None and addr == localhost(): # location specified, and connection is expected to be local location_ip = util.ip_for_host(location) if not is_local_ip(location_ip) and not sshserver: # load ssh from JSON *only* if the controller is not on # this machine sshserver = cfg['ssh'] if ( not is_local_ip(location_ip) and not sshserver and location != socket.gethostname() ): # warn if no ssh specified, but SSH is probably needed # This is only a warning, because the most likely cause # is a local Controller on a laptop whose IP is dynamic warnings.warn( """ Controller appears to be listening on localhost, but not on this machine. If this is true, you should specify Client(...,sshserver='you@%s') or instruct your controller to listen on an external IP.""" % location, RuntimeWarning, ) elif not sshserver: # otherwise sync with cfg sshserver = cfg['ssh'] self._config = cfg self._ssh = bool(sshserver or sshkey or password) if self._ssh and sshserver is None: # default to ssh via localhost sshserver = addr if self._ssh and password is None: from zmq.ssh import tunnel if tunnel.try_passwordless_ssh(sshserver, sshkey, paramiko): password = False else: password = getpass("SSH Password for %s: " % sshserver) ssh_kwargs = dict(keyfile=sshkey, password=password, paramiko=paramiko) # configure and construct the session try: extra_args['packer'] = cfg['pack'] extra_args['unpacker'] = cfg['unpack'] extra_args['key'] = cfg['key'].encode("utf8") extra_args['signature_scheme'] = cfg['signature_scheme'] except KeyError as exc: msg = '\n'.join( [ "Connection file is invalid (missing '{}'), possibly from an old version of IPython.", "If you are reusing connection files, remove them and start ipcontroller again.", ] ) raise ValueError(msg.format(exc.message)) self.session = Session(**extra_args) self._query_socket = self._context.socket(zmq.DEALER) if self.curve_serverkey: self._query_socket.curve_serverkey = self.curve_serverkey self._query_socket.curve_secretkey = self.curve_secretkey self._query_socket.curve_publickey = self.curve_publickey if self._ssh: from zmq.ssh import tunnel tunnel.tunnel_connection( self._query_socket, cfg['registration'], sshserver, timeout=timeout, **ssh_kwargs, ) else: self._query_socket.connect(cfg['registration']) self.session.debug = self.debug self._notification_handlers = { 'registration_notification': self._register_engine, 'unregistration_notification': self._unregister_engine, 'shutdown_notification': lambda msg: self.close(), } self._queue_handlers = { 'execute_reply': self._handle_execute_reply, 'apply_reply': self._handle_apply_reply, } try: self._connect(sshserver, ssh_kwargs, timeout) except: self.close(linger=0) raise # last step: setup magics, if we are in IPython: ip = get_ipython() if ip is None: return else: if 'px' not in ip.magics_manager.magics["line"]: # in IPython but we are the first Client. # activate a default view for parallel magics. self.activate() def __del__(self): """cleanup sockets, but _not_ context.""" self.close() def _setup_profile_dir(self, profile, profile_dir, ipython_dir): if ipython_dir is None: ipython_dir = get_ipython_dir() if profile_dir is not None: try: self._profile_dir = ProfileDir.find_profile_dir(profile_dir) return except ProfileDirError: pass elif profile is not None: try: self._profile_dir = ProfileDir.find_profile_dir_by_name( ipython_dir, profile ) return except ProfileDirError: pass self._profile_dir = None def __enter__(self): """A client can be used as a context manager which will close the client on exit .. versionadded: 7.0 """ return self def __exit__(self, exc_type, exc_value, traceback): """Exiting a client context closes the client""" self.close() def _update_engines(self, engines): """Update our engines dict and _ids from a dict of the form: {id:uuid}.""" for k, v in engines.items(): eid = int(k) if eid not in self._engines: self._ids.append(eid) self._engines[eid] = v self._ids = sorted(self._ids) if ( sorted(self._engines.keys()) != list(range(len(self._engines))) and self._task_scheme == 'pure' and self._task_socket ): self._stop_scheduling_tasks() def _stop_scheduling_tasks(self): """Stop scheduling tasks because an engine has been unregistered from a pure ZMQ scheduler. """ self._task_socket.close() self._task_socket = None msg = ( "An engine has been unregistered, and we are using pure " + "ZMQ task scheduling. Task farming will be disabled." ) if self.outstanding: msg += ( " If you were running tasks when this happened, " + "some `outstanding` msg_ids may never resolve." ) warnings.warn(msg, RuntimeWarning) def _build_targets(self, targets): """Turn valid target IDs or 'all' into two lists: (int_ids, uuids). """ if not self._ids: # flush notification socket if no engines yet, just in case if not self.ids: raise error.NoEnginesRegistered( "Can't build targets without any engines" ) if targets is None: targets = self._ids elif isinstance(targets, str): if targets.lower() == 'all': targets = self._ids else: raise TypeError("%r not valid str target, must be 'all'" % (targets)) elif isinstance(targets, int): if targets < 0: targets = self.ids[targets] if targets not in self._ids: raise IndexError("No such engine: %i" % targets) targets = [targets] if isinstance(targets, slice): indices = list(range(len(self._ids))[targets]) ids = self.ids targets = [ids[i] for i in indices] if not isinstance(targets, (tuple, list, range)): raise TypeError( "targets by int/slice/collection of ints only, not %s" % (type(targets)) ) return [self._engines[t].encode("utf8") for t in targets], list(targets) def _connect(self, sshserver, ssh_kwargs, timeout): """setup all our socket connections to the cluster. This is called from __init__.""" # Maybe allow reconnecting? if self._connected: return self._connected = True def connect_socket(s, url): if self._ssh: from zmq.ssh import tunnel return tunnel.tunnel_connection(s, url, sshserver, **ssh_kwargs) else: return util.connect( s, url, curve_serverkey=self.curve_serverkey, curve_secretkey=self.curve_secretkey, curve_publickey=self.curve_publickey, ) self.session.send(self._query_socket, 'connection_request') # use Poller because zmq.select has wrong units in pyzmq 2.1.7 poller = zmq.Poller() poller.register(self._query_socket, zmq.POLLIN) # poll expects milliseconds, timeout is seconds evts = poller.poll(timeout * 1000) if not evts: raise TimeoutError("Hub connection request timed out") idents, msg = self.session.recv(self._query_socket, mode=0) if self.debug: pprint(msg) content = msg['content'] # self._config['registration'] = dict(content) cfg = self._config if content['status'] == 'ok': self._mux_socket = self._context.socket(zmq.DEALER) connect_socket(self._mux_socket, cfg['mux']) self._task_socket = self._context.socket(zmq.DEALER) connect_socket(self._task_socket, cfg['task']) self._broadcast_socket = self._context.socket(zmq.DEALER) connect_socket(self._broadcast_socket, cfg['broadcast']) self._notification_socket = self._context.socket(zmq.SUB) self._notification_socket.RCVHWM = 0 self._notification_socket.setsockopt(zmq.SUBSCRIBE, b'') connect_socket(self._notification_socket, cfg['notification']) self._control_socket = self._context.socket(zmq.DEALER) connect_socket(self._control_socket, cfg['control']) self._iopub_socket = self._context.socket(zmq.SUB) self._iopub_socket.RCVHWM = 0 self._iopub_socket.setsockopt(zmq.SUBSCRIBE, b'') connect_socket(self._iopub_socket, cfg['iopub']) self._update_engines(dict(content['engines'])) else: self._connected = False tb = '\n'.join(content.get('traceback', [])) raise Exception("Failed to connect! %s" % tb) self._start_io_thread() # -------------------------------------------------------------------------- # handlers and callbacks for incoming messages # -------------------------------------------------------------------------- def _unwrap_exception(self, content): """unwrap exception, and remap engine_id to int.""" e = error.unwrap_exception(content) # print e.traceback if e.engine_info: e_uuid = e.engine_info['engine_uuid'] eid = self._engines[e_uuid] e.engine_info['engine_id'] = eid return e def _extract_metadata(self, msg): header = msg['header'] parent = msg['parent_header'] msg_meta = msg['metadata'] content = msg['content'] md = { 'msg_id': parent['msg_id'], 'received': util.utcnow(), 'engine_uuid': msg_meta.get('engine', None), 'follow': msg_meta.get('follow', []), 'after': msg_meta.get('after', []), 'status': content['status'], 'is_broadcast': msg_meta.get('is_broadcast', False), 'is_coalescing': msg_meta.get('is_coalescing', False), } if md['engine_uuid'] is not None: md['engine_id'] = self._engines.get(md['engine_uuid'], None) if md['is_coalescing']: # get destinations from target metadata targets = msg_meta.get("broadcast_targets", []) md['engine_uuid'], md['engine_id'] = map(list, zip(*targets)) if 'date' in parent: md['submitted'] = parent['date'] if 'started' in msg_meta: md['started'] = util._parse_date(msg_meta['started']) if 'date' in header: md['completed'] = header['date'] return md def _register_engine(self, msg): """Register a new engine, and update our connection info.""" content = msg['content'] eid = content['id'] d = {eid: content['uuid']} self._update_engines(d) event = {'event': 'register'} event.update(content) for callback in self._registration_callbacks: callback(event) def _unregister_engine(self, msg): """Unregister an engine that has died.""" content = msg['content'] eid = int(content['id']) if eid in self._ids: self._ids.remove(eid) uuid = self._engines.pop(eid) self._handle_stranded_msgs(eid, uuid) if self._task_socket and self._task_scheme == 'pure': self._stop_scheduling_tasks() event = {"event": "unregister"} event.update(content) for callback in self._registration_callbacks: callback(event) def _handle_stranded_msgs(self, eid, uuid): """Handle messages known to be on an engine when the engine unregisters. It is possible that this will fire prematurely - that is, an engine will go down after completing a result, and the client will be notified of the unregistration and later receive the successful result. """ outstanding = self._outstanding_dict[uuid] for msg_id in list(outstanding): if msg_id in self.results: # we already continue try: raise error.EngineError( "Engine %r died while running task %r" % (eid, msg_id) ) except: content = error.wrap_exception() # build a fake message: msg = self.session.msg('apply_reply', content=content) msg['parent_header']['msg_id'] = msg_id msg['metadata']['engine'] = uuid self._handle_apply_reply(msg) def _handle_execute_reply(self, msg): """Save the reply to an execute_request into our results. execute messages are never actually used. apply is used instead. """ parent = msg['parent_header'] if self._should_use_metadata_msg_id(msg): msg_id = msg['metadata']['original_msg_id'] else: msg_id = parent['msg_id'] future = self._futures.get(msg_id, None) if msg_id not in self.outstanding: if msg_id in self.history: print("got stale result: %s" % msg_id) else: print("got unknown result: %s" % msg_id) else: self.outstanding.remove(msg_id) content = msg['content'] header = msg['header'] # construct metadata: md = self.metadata[msg_id] md.update(self._extract_metadata(msg)) if md['is_coalescing']: engine_uuids = md['engine_uuid'] or [] else: engine_uuids = [md['engine_uuid']] for engine_uuid in engine_uuids: if engine_uuid is not None: e_outstanding = self._outstanding_dict[engine_uuid] if msg_id in e_outstanding: e_outstanding.remove(msg_id) # construct result: if content['status'] == 'ok': self.results[msg_id] = ExecuteReply(msg_id, content, md) elif content['status'] == 'aborted': self.results[msg_id] = error.TaskAborted(msg_id) # aborted tasks will not get output out_future = self._output_futures.get(msg_id) if out_future and not out_future.done(): out_future.set_result(None) elif content['status'] == 'resubmitted': # TODO: handle resubmission pass else: self.results[msg_id] = self._unwrap_exception(content) if content['status'] != 'ok' and not content.get('engine_info'): # not an engine failure, don't expect output out_future = self._output_futures.get(msg_id) if out_future and not out_future.done(): out_future.set_result(None) if future: future.set_result(self.results[msg_id]) def _should_use_metadata_msg_id(self, msg): md = msg['metadata'] return md.get('is_broadcast', False) and md.get('is_coalescing', False) def _handle_apply_reply(self, msg): """Save the reply to an apply_request into our results.""" parent = msg['parent_header'] if self._should_use_metadata_msg_id(msg): msg_id = msg['metadata']['original_msg_id'] else: msg_id = parent['msg_id'] future = self._futures.get(msg_id, None) if msg_id not in self.outstanding: if msg_id in self.history: print("got stale result: %s" % msg_id) print(self.results[msg_id]) print(msg) else: print("got unknown result: %s" % msg_id) else: self.outstanding.remove(msg_id) content = msg['content'] header = msg['header'] # construct metadata: md = self.metadata[msg_id] md.update(self._extract_metadata(msg)) if md['is_coalescing']: engine_uuids = md['engine_uuid'] or [] else: engine_uuids = [md['engine_uuid']] for engine_uuid in engine_uuids: if engine_uuid is not None: e_outstanding = self._outstanding_dict[engine_uuid] if msg_id in e_outstanding: e_outstanding.remove(msg_id) # construct result: if content['status'] == 'ok': if md.get('is_coalescing', False): deserialized_bufs = [] bufs = msg['buffers'] while bufs: deserialized, bufs = serialize.deserialize_object(bufs) deserialized_bufs.append(deserialized) self.results[msg_id] = deserialized_bufs else: self.results[msg_id] = serialize.deserialize_object(msg['buffers'])[0] elif content['status'] == 'aborted': self.results[msg_id] = error.TaskAborted(msg_id) out_future = self._output_futures.get(msg_id) if out_future and not out_future.done(): out_future.set_result(None) elif content['status'] == 'resubmitted': # TODO: handle resubmission pass else: self.results[msg_id] = self._unwrap_exception(content) if content['status'] != 'ok' and not content.get('engine_info'): # not an engine failure, don't expect output out_future = self._output_futures.get(msg_id) if out_future and not out_future.done(): out_future.set_result(None) if future: future.set_result(self.results[msg_id]) def _make_io_loop(self): """Make my IOLoop. Override with IOLoop.current to return""" # runs first thing in the io thread # always create a fresh asyncio loop for the thread asyncio_loop = asyncio.new_event_loop() if hasattr(asyncio, 'ProactorEventLoop') and isinstance( asyncio_loop, asyncio.ProactorEventLoop ): asyncio_loop = asyncio.SelectorEventLoop() asyncio.set_event_loop(asyncio_loop) loop = ioloop.IOLoop() loop.make_current() return loop def _stop_io_thread(self): """Stop my IO thread""" if self._io_loop: self._io_loop.add_callback(self._io_loop.stop) if self._io_thread and self._io_thread is not current_thread(): self._io_thread.join() def _setup_streams(self): self._query_stream = ZMQStream(self._query_socket, self._io_loop) self._query_stream.on_recv(self._dispatch_single_reply, copy=False) self._control_stream = ZMQStream(self._control_socket, self._io_loop) self._control_stream.on_recv(self._dispatch_single_reply, copy=False) self._mux_stream = ZMQStream(self._mux_socket, self._io_loop) self._mux_stream.on_recv(self._dispatch_reply, copy=False) self._task_stream = ZMQStream(self._task_socket, self._io_loop) self._task_stream.on_recv(self._dispatch_reply, copy=False) self._iopub_stream = ZMQStream(self._iopub_socket, self._io_loop) self._iopub_stream.on_recv(self._dispatch_iopub, copy=False) self._notification_stream = ZMQStream(self._notification_socket, self._io_loop) self._notification_stream.on_recv(self._dispatch_notification, copy=False) self._broadcast_stream = ZMQStream(self._broadcast_socket, self._io_loop) self._broadcast_stream.on_recv(self._dispatch_reply, copy=False) def _start_io_thread(self): """Start IOLoop in a background thread.""" evt = Event() self._io_thread = Thread(target=self._io_main, args=(evt,)) self._io_thread.daemon = True self._io_thread.start() # wait for the IOLoop to start for i in range(10): if evt.wait(1): return if not self._io_thread.is_alive(): raise RuntimeError("IO Loop failed to start") else: raise RuntimeError( "Start event was never set. Maybe a problem in the IO thread." ) def _io_main(self, start_evt=None): """main loop for background IO thread""" self._io_loop = self._make_io_loop() self._setup_streams() # signal that start has finished # so that the main thread knows that all our attributes are defined if start_evt: start_evt.set() try: self._io_loop.start() finally: self._io_loop.close(all_fds=True) @unpack_message def _dispatch_single_reply(self, msg): """Dispatch single (non-execution) replies""" msg_id = msg['parent_header'].get('msg_id', None) future = self._futures.get(msg_id) if future is not None: future.set_result(msg) @unpack_message def _dispatch_notification(self, msg): """Dispatch notification messages""" msg_type = msg['header']['msg_type'] handler = self._notification_handlers.get(msg_type, None) if handler is None: raise KeyError("Unhandled notification message type: %s" % msg_type) else: handler(msg) @unpack_message def _dispatch_reply(self, msg): """handle execution replies waiting in ZMQ queue.""" msg_type = msg['header']['msg_type'] handler = self._queue_handlers.get(msg_type, None) if handler is None: raise KeyError("Unhandled reply message type: %s" % msg_type) else: handler(msg) @unpack_message def _dispatch_iopub(self, msg): """handler for IOPub messages""" parent = msg['parent_header'] if not parent or parent['session'] != self.session.session: # ignore IOPub messages not from here return msg_id = parent['msg_id'] content = msg['content'] header = msg['header'] msg_type = msg['header']['msg_type'] if msg_type == 'status' and msg_id not in self.metadata: # ignore status messages if they aren't mine return # init metadata: md = self.metadata[msg_id] ip = get_ipython() if msg_type == 'stream': name = content['name'] new_text = (md[name] or '') + content['text'] if '\r' in content['text']: new_text = _cr_pat.sub('', new_text) md[name] = new_text elif msg_type == 'error': md.update({'error': self._unwrap_exception(content)}) elif msg_type == 'execute_input': md.update({'execute_input': content['code']}) elif msg_type == 'display_data': md['outputs'].append(content) elif msg_type == 'execute_result': md['execute_result'] = content elif msg_type == 'data_message': data, remainder = serialize.deserialize_object(msg['buffers']) md['data'].update(data) elif msg_type == 'status': # idle message comes after all outputs if content['execution_state'] == 'idle': future = self._output_futures.get(msg_id) if future and not future.done(): # TODO: should probably store actual outputs on the Future future.set_result(None) elif msg_type.startswith("comm_") and ip is not None and ip.kernel is not None: # only handle comm messages when we're in an IPython kernel if msg_type == "comm_open": # create proxy comm engine_uuid = msg['metadata'].get('engine', '') engine_ident = engine_uuid.encode("utf8", "replace") # DEBUG: engine_uuid can still be missing?! comm = Comm( comm_id=content['comm_id'], primary=False, ) send_to_engine = partial( self._send, self._mux_socket, ident=engine_ident, ) def relay_comm(msg): send_to_engine( msg["msg_type"], content=msg['content'], metadata=msg['metadata'], buffers=msg["buffers"], ) comm.on_msg(relay_comm) comm.on_close( lambda: send_to_engine( "comm_close", content={ "comm_id": comm.comm_id, }, ) ) ip.kernel.comm_manager.register_comm(comm) # relay all comm msgs ip.kernel.session.send( ip.kernel.iopub_socket, msg_type, content=msg['content'], metadata=msg['metadata'], buffers=msg['buffers'], # different parent! parent=ip.kernel.get_parent("shell"), ) msg_future = self._futures.get(msg_id, None) if msg_future: # Run any callback functions for callback in msg_future.iopub_callbacks: callback(msg) def create_message_futures(self, msg_id, async_result=False, track=False): msg_future = MessageFuture(msg_id, track=track) futures = [msg_future] self._futures[msg_id] = msg_future if async_result: output = MessageFuture(msg_id) # add future for output self._output_futures[msg_id] = output # hook up metadata output.metadata = self.metadata[msg_id] output.metadata['submitted'] = util.utcnow() msg_future.output = output futures.append(output) return futures def _send( self, socket, msg_type, content=None, parent=None, ident=None, buffers=None, track=False, header=None, metadata=None, track_outstanding=False, message_future_hook=None, ): """Send a message in the IO thread returns msg object""" if self._closed: raise IOError("Connections have been closed.") msg = self.session.msg( msg_type, content=content, parent=parent, header=header, metadata=metadata ) msg_id = msg['header']['msg_id'] expect_reply = msg_type not in {"comm_msg", "comm_close", "comm_open"} if expect_reply and track_outstanding: # add to outstanding, history self.outstanding.add(msg_id) self.history.append(msg_id) if ident: # possibly routed to a specific engine ident_str = ident if isinstance(ident_str, list): ident_str = ident_str[-1] ident_str = ident_str.decode("utf-8") if ident_str in self._engines.values(): # save for later, in case of engine death self._outstanding_dict[ident_str].add(msg_id) self.metadata['submitted'] = util.utcnow() if expect_reply: futures = self.create_message_futures( msg_id, async_result=msg_type in {'execute_request', 'apply_request'}, track=track, ) if message_future_hook is not None: message_future_hook(futures[0]) def cleanup(f): """Purge caches on Future resolution""" self.results.pop(msg_id, None) self._futures.pop(msg_id, None) self._output_futures.pop(msg_id, None) self.metadata.pop(msg_id, None) multi_future(futures).add_done_callback(cleanup) def _really_send(): sent = self.session.send( socket, msg, track=track, buffers=buffers, ident=ident ) if track: futures[0].tracker.set_result(sent['tracker']) # hand off actual send to IO thread self._io_loop.add_callback(_really_send) if expect_reply: return futures[0] def _send_recv(self, *args, **kwargs): """Send a message in the IO thread and return its reply""" future = self._send(*args, **kwargs) future.wait() return future.result() # -------------------------------------------------------------------------- # len, getitem # -------------------------------------------------------------------------- def __len__(self): """len(client) returns # of engines.""" return len(self.ids) def __getitem__(self, key): """index access returns DirectView multiplexer objects Must be int, slice, or list/tuple/range of ints""" if not isinstance(key, (int, slice, tuple, list, range)): raise TypeError( "key by int/slice/iterable of ints only, not %s" % (type(key)) ) else: return self.direct_view(key) def __iter__(self): """Since we define getitem, Client is iterable but unless we also define __iter__, it won't work correctly unless engine IDs start at zero and are continuous. """ for eid in self.ids: yield self.direct_view(eid) # -------------------------------------------------------------------------- # Begin public methods # -------------------------------------------------------------------------- @property def ids(self): """Always up-to-date ids property.""" # always copy: return list(self._ids) def activate(self, targets='all', suffix=''): """Create a DirectView and register it with IPython magics Defines the magics `%px, %autopx, %pxresult, %%px` Parameters ---------- targets : int, list of ints, or 'all' The engines on which the view's magics will run suffix : str [default: ''] The suffix, if any, for the magics. This allows you to have multiple views associated with parallel magics at the same time. e.g. ``rc.activate(targets=0, suffix='0')`` will give you the magics ``%px0``, ``%pxresult0``, etc. for running magics just on engine 0. """ view = self.direct_view(targets) view.block = True view.activate(suffix) return view def close(self, linger=None): """Close my zmq Sockets If `linger`, set the zmq LINGER socket option, which allows discarding of messages. """ if self._closed: return self._stop_io_thread() snames = [trait for trait in self.trait_names() if trait.endswith("socket")] for name in snames: socket = getattr(self, name) if socket is not None and not socket.closed: if linger is not None: socket.close(linger=linger) else: socket.close() self._closed = True def spin_thread(self, interval=1): """DEPRECATED, DOES NOTHING""" warnings.warn( "Client.spin_thread is deprecated now that IO is always in a thread", DeprecationWarning, ) def stop_spin_thread(self): """DEPRECATED, DOES NOTHING""" warnings.warn( "Client.spin_thread is deprecated now that IO is always in a thread", DeprecationWarning, ) def spin(self): """DEPRECATED, DOES NOTHING""" warnings.warn( "Client.spin is deprecated now that IO is in a thread", DeprecationWarning ) def _await_futures(self, futures, timeout): """Wait for a collection of futures""" if not futures: return True event = Event() if timeout and timeout < 0: timeout = None f = multi_future(futures) f.add_done_callback(lambda f: event.set()) return event.wait(timeout) def _futures_for_msgs(self, msg_ids): """Turn msg_ids into Futures msg_ids not in futures dict are presumed done. """ futures = [] for msg_id in msg_ids: f = self._futures.get(msg_id, None) if f: futures.append(f) return futures def wait_for_engines( self, n, *, timeout=-1, block=True, interactive=None, widget=None ): """Wait for `n` engines to become available. Returns when `n` engines are available, or raises TimeoutError if `timeout` is reached before `n` engines are ready. Parameters ---------- n : int Number of engines to wait for. timeout : float Time (in seconds) to wait before raising a TimeoutError block : bool if False, return Future instead of waiting interactive : bool default: True if in IPython, False otherwise. if True, show a progress bar while waiting for engines widget : bool default: True if in an IPython kernel (notebook), False otherwise. Only has an effect if `interactive` is True. if True, forces use of widget progress bar. If False, forces use of terminal tqdm. Returns ------ f : concurrent.futures.Future or None Future object to wait on if block is False, None if block is True. Raises ------ TimeoutError : if timeout is reached. """ if len(self.ids) >= n: if block: return else: f = Future() f.set_result(None) return f tic = now = time.perf_counter() if timeout >= 0: deadline = tic + timeout else: deadline = None seconds_remaining = 1000 if interactive is None: interactive = get_ipython() is not None if interactive: progress_bar = util.progress( widget=widget, initial=len(self.ids), total=n, unit='engine', ) # watch for engine-stop events engine_stop_future = Future() if self.cluster and self.cluster.engines: # we have a parent cluster, # monitor for engines stopping def _signal_stopped(stop_data): if not engine_stop_future.done(): engine_stop_future.set_result(stop_data) for es in self.cluster.engines.values(): es.on_stop(_signal_stopped) engine_stop_future.add_done_callback( lambda f: es.stop_callbacks.remove(_signal_stopped) ) future = Future() def cancel_engine_stop(_): if not engine_stop_future.done(): engine_stop_future.cancel() future.add_done_callback(cancel_engine_stop) def notice_engine_stop(f): if future.done(): return stop_data = f.result() future.set_exception(error.EngineError(f"Engine set stopped: {stop_data}")) engine_stop_future.add_done_callback(notice_engine_stop) def notify(event): if future.done(): return if event["event"] == "unregister": future.set_exception( error.EngineError( f"Engine {event['id']} unregistered while waiting for engines." ) ) return current_n = len(self.ids) if interactive: progress_bar.update(current_n - progress_bar.n) if current_n >= n: # ensure we refresh when we finish if interactive: progress_bar.close() future.set_result(None) self._registration_callbacks.append(notify) future.add_done_callback(lambda f: self._registration_callbacks.remove(notify)) def on_timeout(): """Called when timeout is reached""" if future.done(): return current_n = len(self.ids) if current_n >= n: future.set_result(None) else: future.set_exception( TimeoutError( f"{n} engines not ready in {timeout} seconds. Currently ready: {current_n}" ) ) def schedule_timeout(): handle = self._io_loop.add_timeout( self._io_loop.time() + timeout, on_timeout ) future.add_done_callback(lambda f: self._io_loop.remove_timeout(handle)) if timeout >= 0: self._io_loop.add_callback(schedule_timeout) if block: return future.result() else: return future def wait(self, jobs=None, timeout=-1): """waits on one or more `jobs`, for up to `timeout` seconds. Parameters ---------- jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects ints are indices to self.history strs are msg_ids default: wait on all outstanding messages timeout : float a time in seconds, after which to give up. default is -1, which means no timeout Returns ------- True : when all msg_ids are done False : timeout reached, some msg_ids still outstanding """ futures = [] if jobs is None: if not self.outstanding: return True # make a copy, so that we aren't passing a mutable collection to _futures_for_msgs theids = set(self.outstanding) else: if isinstance(jobs, (str, int, AsyncResult)) or not isinstance( jobs, Iterable ): jobs = [jobs] theids = set() for job in jobs: if isinstance(job, int): # index access job = self.history[job] elif isinstance(job, AsyncResult): theids.update(job.msg_ids) continue elif _is_future(job): futures.append(job) continue theids.add(job) if not futures and not theids.intersection(self.outstanding): return True futures.extend(self._futures_for_msgs(theids)) return self._await_futures(futures, timeout) def wait_interactive(self, jobs=None, interval=1.0, timeout=-1.0): """Wait interactively for jobs If no job is specified, will wait for all outstanding jobs to complete. """ if jobs is None: # get futures for results futures = [f for f in self._futures.values() if hasattr(f, 'output')] if not futures: return ar = AsyncResult(self, futures, owner=False) else: ar = self._asyncresult_from_jobs(jobs, owner=False) return ar.wait_interactive(interval=interval, timeout=timeout) # -------------------------------------------------------------------------- # Control methods # -------------------------------------------------------------------------- def _send_control_request(self, targets, msg_type, content, block): """Send a request on the control channel""" target_identities = self._build_targets(targets)[0] futures = [] for ident in target_identities: futures.append( self._send(self._control_stream, msg_type, content=content, ident=ident) ) if not block: return multi_future(futures) for future in futures: future.wait() msg = future.result() if msg['content']['status'] != 'ok': raise self._unwrap_exception(msg['content']) def send_signal(self, sig, targets=None, block=None): """Send a signal target(s). Parameters ---------- sig: int or str The signal number or name to send. If a str, will evaluate to getattr(signal, sig) on the engine, which is useful for sending signals cross-platform. .. versionadded:: 7.0 """ block = self.block if block is None else block return self._send_control_request( targets=targets, msg_type='signal_request', content={'sig': sig}, block=block, ) def clear(self, targets=None, block=None): """Clear the namespace in target(s).""" block = self.block if block is None else block return self._send_control_request( targets=targets, msg_type='clear_request', content={}, block=block ) def abort(self, jobs=None, targets=None, block=None): """Abort specific jobs from the execution queues of target(s). This is a mechanism to prevent jobs that have already been submitted from executing. To halt a running job, you must interrupt the engine(s) by sending a signal. This can be done via os.kill for local engines, or :meth:`.Cluster.signal_engines` for multiple engines. Parameters ---------- jobs : msg_id, list of msg_ids, or AsyncResult The jobs to be aborted If unspecified/None: abort all outstanding jobs. """ block = self.block if block is None else block jobs = jobs if jobs is not None else list(self.outstanding) msg_ids = [] if isinstance(jobs, (str, AsyncResult)): jobs = [jobs] bad_ids = [obj for obj in jobs if not isinstance(obj, (str, AsyncResult))] if bad_ids: raise TypeError( "Invalid msg_id type %r, expected str or AsyncResult" % bad_ids[0] ) for j in jobs: if isinstance(j, AsyncResult): msg_ids.extend(j.msg_ids) else: msg_ids.append(j) content = dict(msg_ids=msg_ids) return self._send_control_request( targets, msg_type='abort_request', content=content, block=block, ) def shutdown(self, targets='all', restart=False, hub=False, block=None): """Terminates one or more engine processes, optionally including the hub. Parameters ---------- targets : list of ints or 'all' [default: all] Which engines to shutdown. hub : bool [default: False] Whether to include the Hub. hub=True implies targets='all'. block : bool [default: self.block] Whether to wait for clean shutdown replies or not. restart : bool [default: False] NOT IMPLEMENTED whether to restart engines after shutting them down. """ from ipyparallel.error import NoEnginesRegistered if restart: raise NotImplementedError("Engine restart is not yet implemented") block = self.block if block is None else block if hub: targets = 'all' try: targets = self._build_targets(targets)[0] except NoEnginesRegistered: targets = [] futures = [] for t in targets: futures.append( self._send( self._control_stream, 'shutdown_request', content={'restart': restart}, ident=t, ) ) error = False if block or hub: for f in futures: f.wait() msg = f.result() if msg['content']['status'] != 'ok': error = self._unwrap_exception(msg['content']) if hub: # don't trigger close on shutdown notification, which will prevent us from receiving the reply self._notification_handlers['shutdown_notification'] = lambda msg: None msg = self._send_recv(self._query_stream, 'shutdown_request') if msg['content']['status'] != 'ok': error = self._unwrap_exception(msg['content']) if not error: self.close() if error: raise error def become_dask( self, targets='all', port=0, nanny=False, scheduler_args=None, **worker_args ): """Turn the IPython cluster into a dask.distributed cluster Parameters ---------- targets : target spec (default: all) Which engines to turn into dask workers. port : int (default: random) Which port nanny : bool (default: False) Whether to start workers as subprocesses instead of in the engine process. Using a nanny allows restarting the worker processes via ``executor.restart``. scheduler_args : dict Keyword arguments (e.g. ip) to pass to the distributed.Scheduler constructor. **worker_args Any additional keyword arguments (e.g. ncores) are passed to the distributed.Worker constructor. Returns ------- client = distributed.Client A dask.distributed.Client connected to the dask cluster. """ import distributed dview = self.direct_view(targets) if scheduler_args is None: scheduler_args = {} else: scheduler_args = dict(scheduler_args) # copy # Start a Scheduler on the Hub: reply = self._send_recv( self._query_stream, 'become_dask_request', {'scheduler_args': scheduler_args}, ) if reply['content']['status'] != 'ok': raise self._unwrap_exception(reply['content']) distributed_info = reply['content'] # Start a Worker on the selected engines: worker_args['address'] = distributed_info['address'] worker_args['nanny'] = nanny # set default ncores=1, since that's how an IPython cluster is typically set up. worker_args.setdefault('ncores', 1) dview.apply_sync(util.become_dask_worker, **worker_args) # Finally, return a Client connected to the Scheduler try: distributed_Client = distributed.Client except AttributeError: # For distributed pre-1.18.1 distributed_Client = distributed.Executor client = distributed_Client('{address}'.format(**distributed_info)) return client def stop_dask(self, targets='all'): """Stop the distributed Scheduler and Workers started by become_dask. Parameters ---------- targets : target spec (default: all) Which engines to stop dask workers on. """ dview = self.direct_view(targets) # Start a Scheduler on the Hub: reply = self._send_recv(self._query_stream, 'stop_distributed_request') if reply['content']['status'] != 'ok': raise self._unwrap_exception(reply['content']) # Finally, stop all the Workers on the engines dview.apply_sync(util.stop_distributed_worker) # aliases: become_distributed = become_dask stop_distributed = stop_dask # -------------------------------------------------------------------------- # Execution related methods # -------------------------------------------------------------------------- def _maybe_raise(self, result): """wrapper for maybe raising an exception if apply failed.""" if isinstance(result, error.RemoteError): raise result return result def send_apply_request( self, socket, f, args=None, kwargs=None, metadata=None, track=False, ident=None, message_future_hook=None, ): """construct and send an apply message via a socket. This is the principal method with which all engine execution is performed by views. """ if self._closed: raise RuntimeError( "Client cannot be used after its sockets have been closed" ) # defaults: args = args if args is not None else [] kwargs = kwargs if kwargs is not None else {} metadata = metadata if metadata is not None else {} # validate arguments if not callable(f) and not isinstance(f, (Reference, PrePickled)): raise TypeError("f must be callable, not %s" % type(f)) if not isinstance(args, (tuple, list)): raise TypeError("args must be tuple or list, not %s" % type(args)) if not isinstance(kwargs, dict): raise TypeError("kwargs must be dict, not %s" % type(kwargs)) if not isinstance(metadata, dict): raise TypeError("metadata must be dict, not %s" % type(metadata)) bufs = serialize.pack_apply_message( f, args, kwargs, buffer_threshold=self.session.buffer_threshold, item_threshold=self.session.item_threshold, ) future = self._send( socket, "apply_request", buffers=bufs, ident=ident, metadata=metadata, track=track, track_outstanding=True, message_future_hook=message_future_hook, ) msg_id = future.msg_id return future def send_execute_request( self, socket, code, silent=True, metadata=None, ident=None, message_future_hook=None, ): """construct and send an execute request via a socket.""" if self._closed: raise RuntimeError( "Client cannot be used after its sockets have been closed" ) # defaults: metadata = metadata if metadata is not None else {} # validate arguments if not isinstance(code, str): raise TypeError("code must be text, not %s" % type(code)) if not isinstance(metadata, dict): raise TypeError("metadata must be dict, not %s" % type(metadata)) content = dict(code=code, silent=bool(silent), user_expressions={}) future = self._send( socket, "execute_request", content=content, ident=ident, metadata=metadata, track_outstanding=True, message_future_hook=message_future_hook, ) return future # -------------------------------------------------------------------------- # construct a View object # -------------------------------------------------------------------------- def load_balanced_view(self, targets=None, **kwargs): """construct a DirectView object. If no arguments are specified, create a LoadBalancedView using all engines. Parameters ---------- targets : list,slice,int,etc. [default: use all engines] The subset of engines across which to load-balance execution **kwargs : passed to LoadBalancedView """ if targets == 'all': targets = None if targets is not None: targets = self._build_targets(targets)[1] return LoadBalancedView( client=self, socket=self._task_stream, targets=targets, **kwargs ) def executor(self, targets=None): """Construct a PEP-3148 Executor with a LoadBalancedView Parameters ---------- targets : list,slice,int,etc. [default: use all engines] The subset of engines across which to load-balance execution Returns ------- executor: Executor The Executor object """ return self.load_balanced_view(targets).executor def direct_view(self, targets='all', **kwargs): """construct a DirectView object. If no targets are specified, create a DirectView using all engines. rc.direct_view('all') is distinguished from rc[:] in that 'all' will evaluate the target engines at each execution, whereas rc[:] will connect to all *current* engines, and that list will not change. That is, 'all' will always use all engines, whereas rc[:] will not use engines added after the DirectView is constructed. Parameters ---------- targets : list,slice,int,etc. [default: use all engines] The engines to use for the View **kwargs : passed to DirectView """ single = isinstance(targets, int) # allow 'all' to be lazily evaluated at each execution if targets != 'all': targets = self._build_targets(targets)[1] if single: targets = targets[0] return DirectView( client=self, socket=self._mux_stream, targets=targets, **kwargs ) def broadcast_view(self, targets='all', is_coalescing=False, **kwargs): """construct a BroadCastView object. If no arguments are specified, create a BroadCastView using all engines using all engines. Parameters ---------- targets : list,slice,int,etc. [default: use all engines] The subset of engines across which to load-balance execution is_coalescing : scheduler collects all messages from engines and returns them as one **kwargs : passed to BroadCastView """ targets = self._build_targets(targets)[1] bcast_view = BroadcastView( client=self, socket=self._broadcast_stream, targets=targets, ) bcast_view.is_coalescing = is_coalescing return bcast_view # -------------------------------------------------------------------------- # Query methods # -------------------------------------------------------------------------- def get_result(self, indices_or_msg_ids=None, block=None, owner=True): """Retrieve a result by msg_id or history index, wrapped in an AsyncResult object. If the client already has the results, no request to the Hub will be made. This is a convenient way to construct AsyncResult objects, which are wrappers that include metadata about execution, and allow for awaiting results that were not submitted by this Client. It can also be a convenient way to retrieve the metadata associated with blocking execution, since it always retrieves Examples -------- :: In [10]: r = client.apply() Parameters ---------- indices_or_msg_ids : integer history index, str msg_id, AsyncResult, or a list of same. The indices or msg_ids of indices to be retrieved block : bool Whether to wait for the result to be done owner : bool [default: True] Whether this AsyncResult should own the result. If so, calling `ar.get()` will remove data from the client's result and metadata cache. There should only be one owner of any given msg_id. Returns ------- AsyncResult A single AsyncResult object will always be returned. AsyncHubResult A subclass of AsyncResult that retrieves results from the Hub """ block = self.block if block is None else block if indices_or_msg_ids is None: indices_or_msg_ids = -1 ar = self._asyncresult_from_jobs(indices_or_msg_ids, owner=owner) if block: ar.wait() return ar def resubmit(self, indices_or_msg_ids=None, metadata=None, block=None): """Resubmit one or more tasks. in-flight tasks may not be resubmitted. Parameters ---------- indices_or_msg_ids : integer history index, str msg_id, or list of either The indices or msg_ids of indices to be retrieved block : bool Whether to wait for the result to be done Returns ------- AsyncHubResult A subclass of AsyncResult that retrieves results from the Hub """ block = self.block if block is None else block if indices_or_msg_ids is None: indices_or_msg_ids = -1 theids = self._msg_ids_from_jobs(indices_or_msg_ids) content = dict(msg_ids=theids) reply = self._send_recv(self._query_stream, 'resubmit_request', content) content = reply['content'] if content['status'] != 'ok': raise self._unwrap_exception(content) mapping = content['resubmitted'] new_ids = [mapping[msg_id] for msg_id in theids] ar = AsyncHubResult(self, new_ids) if block: ar.wait() return ar def result_status(self, msg_ids, status_only=True): """Check on the status of the result(s) of the apply request with `msg_ids`. If status_only is False, then the actual results will be retrieved, else only the status of the results will be checked. Parameters ---------- msg_ids : list of msg_ids if int: Passed as index to self.history for convenience. status_only : bool (default: True) if False: Retrieve the actual results of completed tasks. Returns ------- results : dict There will always be the keys 'pending' and 'completed', which will be lists of msg_ids that are incomplete or complete. If `status_only` is False, then completed results will be keyed by their `msg_id`. """ theids = self._msg_ids_from_jobs(msg_ids) completed = [] local_results = {} # comment this block out to temporarily disable local shortcut: for msg_id in theids: if msg_id in self.results: completed.append(msg_id) local_results[msg_id] = self.results[msg_id] theids.remove(msg_id) if theids: # some not locally cached content = dict(msg_ids=theids, status_only=status_only) reply = self._send_recv( self._query_stream, "result_request", content=content ) content = reply['content'] if content['status'] != 'ok': raise self._unwrap_exception(content) buffers = reply['buffers'] else: content = dict(completed=[], pending=[]) content['completed'].extend(completed) if status_only: return content failures = [] # load cached results into result: content.update(local_results) # update cache with results: for msg_id in sorted(theids): if msg_id in content['completed']: rec = content[msg_id] parent = util.extract_dates(rec['header']) header = util.extract_dates(rec['result_header']) rcontent = rec['result_content'] iodict = rec['io'] if isinstance(rcontent, str): rcontent = self.session.unpack(rcontent) md = self.metadata[msg_id] md_msg = dict( content=rcontent, parent_header=parent, header=header, metadata=rec['result_metadata'], ) md.update(self._extract_metadata(md_msg)) if rec.get('received'): md['received'] = util._parse_date(rec['received']) md.update(iodict) if rcontent['status'] == 'ok': if header['msg_type'] == 'apply_reply': res, buffers = serialize.deserialize_object(buffers) elif header['msg_type'] == 'execute_reply': res = ExecuteReply(msg_id, rcontent, md) else: raise KeyError("unhandled msg type: %r" % header['msg_type']) else: res = self._unwrap_exception(rcontent) failures.append(res) self.results[msg_id] = res content[msg_id] = res if len(theids) == 1 and failures: raise failures[0] error.collect_exceptions(failures, "result_status") return content def queue_status(self, targets='all', verbose=False): """Fetch the status of engine queues. Parameters ---------- targets : int/str/list of ints/strs the engines whose states are to be queried. default : all verbose : bool Whether to return lengths only, or lists of ids for each element """ if targets == 'all': # allow 'all' to be evaluated on the engine engine_ids = None else: engine_ids = self._build_targets(targets)[1] content = dict(targets=engine_ids, verbose=verbose) reply = self._send_recv(self._query_stream, "queue_request", content=content) content = reply['content'] status = content.pop('status') if status != 'ok': raise self._unwrap_exception(content) content = util.int_keys(content) if isinstance(targets, int): return content[targets] else: return content def _msg_ids_from_target(self, targets=None): """Build a list of msg_ids from the list of engine targets""" if not targets: # needed as _build_targets otherwise uses all engines return [] target_ids = self._build_targets(targets)[0] return [ md_id for md_id in self.metadata if self.metadata[md_id]["engine_uuid"] in target_ids ] def _msg_ids_from_jobs(self, jobs=None): """Given a 'jobs' argument, convert it to a list of msg_ids. Can be either one or a list of: - msg_id strings - integer indices to this Client's history - AsyncResult objects """ if not isinstance(jobs, (list, tuple, set, types.GeneratorType)): jobs = [jobs] msg_ids = [] for job in jobs: if isinstance(job, int): msg_ids.append(self.history[job]) elif isinstance(job, str): msg_ids.append(job) elif isinstance(job, AsyncResult): msg_ids.extend(job.msg_ids) else: raise TypeError("Expected msg_id, int, or AsyncResult, got %r" % job) return msg_ids def _asyncresult_from_jobs(self, jobs=None, owner=False): """Construct an AsyncResult from msg_ids or asyncresult objects""" if not isinstance(jobs, (list, tuple, set, types.GeneratorType)): single = True jobs = [jobs] else: single = False futures = [] msg_ids = [] for job in jobs: if isinstance(job, int): job = self.history[job] if isinstance(job, str): if job in self._futures: futures.append(job) elif job in self.results: f = MessageFuture(job) f.set_result(self.results[job]) f.output = Future() f.output.metadata = self.metadata[job] f.output.set_result(None) futures.append(f) else: msg_ids.append(job) elif isinstance(job, AsyncResult): if job._children: futures.extend(job._children) else: msg_ids.extend(job.msg_ids) else: raise TypeError("Expected msg_id, int, or AsyncResult, got %r" % job) if msg_ids: if single: msg_ids = msg_ids[0] return AsyncHubResult(self, msg_ids, owner=owner) else: if single and futures: futures = futures[0] return AsyncResult(self, futures, owner=owner) def purge_local_results(self, jobs=[], targets=[]): """Clears the client caches of results and their metadata. Individual results can be purged by msg_id, or the entire history of specific targets can be purged. Use `purge_local_results('all')` to scrub everything from the Clients's results and metadata caches. After this call all `AsyncResults` are invalid and should be discarded. If you must "reget" the results, you can still do so by using `client.get_result(msg_id)` or `client.get_result(asyncresult)`. This will redownload the results from the hub if they are still available (i.e `client.purge_hub_results(...)` has not been called. Parameters ---------- jobs : str or list of str or AsyncResult objects the msg_ids whose results should be purged. targets : int/list of ints The engines, by integer ID, whose entire result histories are to be purged. Raises ------ RuntimeError : if any of the tasks to be purged are still outstanding. """ if not targets and not jobs: raise ValueError("Must specify at least one of `targets` and `jobs`") if jobs == 'all': if self.outstanding: raise RuntimeError( "Can't purge outstanding tasks: %s" % self.outstanding ) self.results.clear() self.metadata.clear() self._futures.clear() self._output_futures.clear() else: msg_ids = set() msg_ids.update(self._msg_ids_from_target(targets)) msg_ids.update(self._msg_ids_from_jobs(jobs)) still_outstanding = self.outstanding.intersection(msg_ids) if still_outstanding: raise RuntimeError( "Can't purge outstanding tasks: %s" % still_outstanding ) for mid in msg_ids: self.results.pop(mid, None) self.metadata.pop(mid, None) self._futures.pop(mid, None) self._output_futures.pop(mid, None) def purge_hub_results(self, jobs=[], targets=[]): """Tell the Hub to forget results. Individual results can be purged by msg_id, or the entire history of specific targets can be purged. Use `purge_results('all')` to scrub everything from the Hub's db. Parameters ---------- jobs : str or list of str or AsyncResult objects the msg_ids whose results should be forgotten. targets : int/str/list of ints/strs The targets, by int_id, whose entire history is to be purged. default : None """ if not targets and not jobs: raise ValueError("Must specify at least one of `targets` and `jobs`") if targets: targets = self._build_targets(targets)[1] # construct msg_ids from jobs if jobs == 'all': msg_ids = jobs else: msg_ids = self._msg_ids_from_jobs(jobs) content = dict(engine_ids=targets, msg_ids=msg_ids) reply = self._send_recv(self._query_stream, "purge_request", content=content) content = reply['content'] if content['status'] != 'ok': raise self._unwrap_exception(content) def purge_results(self, jobs=[], targets=[]): """Clears the cached results from both the hub and the local client Individual results can be purged by msg_id, or the entire history of specific targets can be purged. Use `purge_results('all')` to scrub every cached result from both the Hub's and the Client's db. Equivalent to calling both `purge_hub_results()` and `purge_client_results()` with the same arguments. Parameters ---------- jobs : str or list of str or AsyncResult objects the msg_ids whose results should be forgotten. targets : int/str/list of ints/strs The targets, by int_id, whose entire history is to be purged. default : None """ self.purge_local_results(jobs=jobs, targets=targets) self.purge_hub_results(jobs=jobs, targets=targets) def purge_everything(self): """Clears all content from previous Tasks from both the hub and the local client In addition to calling `purge_results("all")` it also deletes the history and other bookkeeping lists. """ self.purge_results("all") self.history = [] self.session.digest_history.clear() def hub_history(self): """Get the Hub's history Just like the Client, the Hub has a history, which is a list of msg_ids. This will contain the history of all clients, and, depending on configuration, may contain history across multiple cluster sessions. Any msg_id returned here is a valid argument to `get_result`. Returns ------- msg_ids : list of strs list of all msg_ids, ordered by task submission time. """ reply = self._send_recv(self._query_stream, "history_request", content={}) content = reply['content'] if content['status'] != 'ok': raise self._unwrap_exception(content) else: return content['history'] def db_query(self, query, keys=None): """Query the Hub's TaskRecord database This will return a list of task record dicts that match `query` Parameters ---------- query : mongodb query dict The search dict. See mongodb query docs for details. keys : list of strs [optional] The subset of keys to be returned. The default is to fetch everything but buffers. 'msg_id' will *always* be included. """ if isinstance(keys, str): keys = [keys] content = dict(query=query, keys=keys) reply = self._send_recv(self._query_stream, "db_request", content=content) content = reply['content'] if content['status'] != 'ok': raise self._unwrap_exception(content) records = content['records'] buffer_lens = content['buffer_lens'] result_buffer_lens = content['result_buffer_lens'] buffers = reply['buffers'] has_bufs = buffer_lens is not None has_rbufs = result_buffer_lens is not None for i, rec in enumerate(records): # unpack datetime objects for hkey in ('header', 'result_header'): if hkey in rec: rec[hkey] = util.extract_dates(rec[hkey]) for dtkey in ('submitted', 'started', 'completed', 'received'): if dtkey in rec: rec[dtkey] = util._parse_date(rec[dtkey]) # relink buffers if has_bufs: blen = buffer_lens[i] rec['buffers'], buffers = buffers[:blen], buffers[blen:] if has_rbufs: blen = result_buffer_lens[i] rec['result_buffers'], buffers = buffers[:blen], buffers[blen:] return records __all__ = ['Client'] ipyparallel-7.1.0/ipyparallel/client/futures.py000066400000000000000000000065321412533144600217110ustar00rootroot00000000000000"""Future-related utils""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import sys from concurrent.futures import Future from threading import Event from tornado.log import app_log class MessageFuture(Future): """Future class to wrap async messages""" def __init__(self, msg_id, track=False): super(MessageFuture, self).__init__() self.msg_id = msg_id self._evt = Event() self.track = track self._tracker = None self.tracker = Future() self.iopub_callbacks = [] if not track: self.tracker.set_result(None) self.add_done_callback(lambda f: self._evt.set()) def wait(self, timeout=None): if not self.done(): return self._evt.wait(timeout) return True # The following are from tornado 5.0b1 # avoids hang using gen.multi_future on asyncio, # because Futures cannot be created in another thread def future_set_result_unless_cancelled(future, value): """Set the given ``value`` as the `Future`'s result, if not cancelled. Avoids asyncio.InvalidStateError when calling set_result() on a cancelled `asyncio.Future`. .. versionadded:: 5.0 """ if not future.cancelled(): future.set_result(value) def future_set_exc_info(future, exc_info): """Set the given ``exc_info`` as the `Future`'s exception. Understands both `asyncio.Future` and Tornado's extensions to enable better tracebacks on Python 2. .. versionadded:: 5.0 """ if hasattr(future, 'set_exc_info'): # Tornado's Future future.set_exc_info(exc_info) else: # asyncio.Future future.set_exception(exc_info[1]) def future_add_done_callback(future, callback): """Arrange to call ``callback`` when ``future`` is complete. ``callback`` is invoked with one argument, the ``future``. If ``future`` is already done, ``callback`` is invoked immediately. This may differ from the behavior of ``Future.add_done_callback``, which makes no such guarantee. .. versionadded:: 5.0 """ if future.done(): callback(future) else: future.add_done_callback(callback) def multi_future(children): """Wait for multiple asynchronous futures in parallel. This function is similar to `multi`, but does not support `YieldPoints `. .. versionadded:: 4.0 """ unfinished_children = set(children) future = Future() if not children: future_set_result_unless_cancelled(future, []) def callback(f): unfinished_children.remove(f) if not unfinished_children: result_list = [] for f in children: try: result_list.append(f.result()) except Exception as e: if future.done(): app_log.error( "Multiple exceptions in yield list", exc_info=True ) else: future_set_exc_info(future, sys.exc_info()) if not future.done(): future_set_result_unless_cancelled(future, result_list) listening = set() for f in children: if f not in listening: listening.add(f) future_add_done_callback(f, callback) return future ipyparallel-7.1.0/ipyparallel/client/magics.py000066400000000000000000000403101412533144600214470ustar00rootroot00000000000000# encoding: utf-8 """ ============= parallelmagic ============= Magic command interface for interactive parallel work. Usage ===== ``%autopx`` {AUTOPX_DOC} ``%px`` {PX_DOC} ``%pxresult`` {RESULT_DOC} ``%pxconfig`` {CONFIG_DOC} """ import time from contextlib import contextmanager # Python 3.6 doesn't have nullcontext, so we define our own @contextmanager def nullcontext(): yield # ----------------------------------------------------------------------------- # Copyright (C) 2008 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. # ----------------------------------------------------------------------------- import inspect import re from textwrap import dedent from IPython.core.error import UsageError from IPython.core.magic import Magics from IPython.core import magic_arguments # ----------------------------------------------------------------------------- # Definitions of magic functions for use with IPython # ----------------------------------------------------------------------------- def _iscoroutinefunction(f): """Check if a callable is a coroutine function (either generator-style or async def) """ if inspect.isgeneratorfunction(f): return True if hasattr(inspect, 'iscoroutinefunction') and inspect.iscoroutinefunction(f): return True return False def _asyncify(f): """Wrap a blocking call in a coroutine Does not make the call non-blocking, just conforms to the API assuming it is awaitable. For use when patching-in replacement methods. """ async def async_f(*args, **kwargs): return f(*args, **kwargs) return async_f NO_LAST_RESULT = "%pxresult recalls last %px result, which has not yet been used." def exec_args(f): """decorator for adding block/targets args for execution applied to %pxconfig and %%px """ args = [ magic_arguments.argument( '-b', '--block', action="store_const", const=True, dest='block', help="use blocking (sync) execution", ), magic_arguments.argument( '-a', '--noblock', action="store_const", const=False, dest='block', help="use non-blocking (async) execution", ), magic_arguments.argument( '--stream', action="store_const", const=True, dest='stream', help="stream stdout/stderr in real-time (only valid when using blocking execution)", ), magic_arguments.argument( '--no-stream', action="store_const", const=False, dest='stream', help="do not stream stdout/stderr in real-time", ), magic_arguments.argument( '-t', '--targets', type=str, help="specify the targets on which to execute", ), magic_arguments.argument( '--local', action="store_const", const=True, dest="local", help="also execute the cell in the local namespace", ), magic_arguments.argument( '--verbose', action="store_const", const=True, dest="set_verbose", help="print a message at each execution", ), magic_arguments.argument( '--no-verbose', action="store_const", const=False, dest="set_verbose", help="don't print any messages", ), magic_arguments.argument( '--progress-after', dest="progress_after_seconds", type=float, default=None, help="""Wait this many seconds before showing a progress bar for task completion. Use -1 for no progress, 0 for always showing progress immediately. """, ), ] for a in args: f = a(f) return f def output_args(f): """decorator for output-formatting args applied to %pxresult and %%px """ args = [ magic_arguments.argument( '-r', action="store_const", dest='groupby', const='order', help="collate outputs in order (same as group-outputs=order)", ), magic_arguments.argument( '-e', action="store_const", dest='groupby', const='engine', help="group outputs by engine (same as group-outputs=engine)", ), magic_arguments.argument( '--group-outputs', dest='groupby', type=str, choices=['engine', 'order', 'type'], default='type', help="""Group the outputs in a particular way. Choices are: **type**: group outputs of all engines by type (stdout, stderr, displaypub, etc.). **engine**: display all output for each engine together. **order**: like type, but individual displaypub output from each engine is collated. For example, if multiple plots are generated by each engine, the first figure of each engine will be displayed, then the second of each, etc. """, ), magic_arguments.argument( '-o', '--out', dest='save_name', type=str, help="""store the AsyncResult object for this computation in the global namespace under this name. """, ), ] for a in args: f = a(f) return f class ParallelMagics(Magics): """A set of magics useful when controlling a parallel IPython cluster.""" # magic-related magics = None registered = True # suffix for magics suffix = '' # A flag showing if autopx is activated or not _autopx = False # the current view used by the magics: view = None # last result cache for %pxresult last_result = None # verbose flag verbose = False # streaming output flag stream_ouput = True # seconds to wait before showing progress bar for blocking execution progress_after_seconds = 2 def __init__(self, shell, view, suffix=''): self.view = view self.suffix = suffix # register magics self.magics = dict(cell={}, line={}) line_magics = self.magics['line'] px = 'px' + suffix if not suffix: # keep %result for legacy compatibility line_magics['result'] = self.result line_magics['pxresult' + suffix] = self.result line_magics[px] = self.px line_magics['pxconfig' + suffix] = self.pxconfig line_magics['auto' + px] = self.autopx self.magics['cell'][px] = self.cell_px super(ParallelMagics, self).__init__(shell=shell) def _eval_target_str(self, ts): if ':' in ts: targets = eval("self.view.client.ids[%s]" % ts) elif 'all' in ts: targets = 'all' else: targets = eval(ts) return targets @magic_arguments.magic_arguments() @exec_args def pxconfig(self, line): """configure default targets/blocking for %px magics""" args = magic_arguments.parse_argstring(self.pxconfig, line) if args.targets: self.view.targets = self._eval_target_str(args.targets) if args.block is not None: self.view.block = args.block if args.set_verbose is not None: self.verbose = args.set_verbose if args.stream is not None: self.stream_ouput = args.stream if args.progress_after_seconds is not None: self.progress_after_seconds = args.progress_after_seconds @magic_arguments.magic_arguments() @output_args def result(self, line=''): """Print the result of the last asynchronous %px command. This lets you recall the results of %px computations after asynchronous submission (block=False). Examples -------- :: In [23]: %px os.getpid() Async parallel execution on engine(s): all In [24]: %pxresult Out[8:10]: 60920 Out[9:10]: 60921 Out[10:10]: 60922 Out[11:10]: 60923 """ args = magic_arguments.parse_argstring(self.result, line) if self.last_result is None: raise UsageError(NO_LAST_RESULT) self.last_result.get() self.last_result.display_outputs(groupby=args.groupby) def px(self, line=''): """Executes the given python command in parallel. Examples -------- :: In [24]: %px a = os.getpid() Parallel execution on engine(s): all In [25]: %px print a [stdout:0] 1234 [stdout:1] 1235 [stdout:2] 1236 [stdout:3] 1237 """ return self.parallel_execute(line) def parallel_execute( self, cell, block=None, groupby='type', save_name=None, stream_output=None, progress_after=None, ): """implementation used by %px and %%parallel""" # defaults: block = self.view.block if block is None else block stream_output = self.stream_ouput if stream_output is None else stream_output base = "Parallel" if block else "Async parallel" targets = self.view.targets if isinstance(targets, list) and len(targets) > 10: str_targets = str(targets[:4])[:-1] + ', ..., ' + str(targets[-4:])[1:] else: str_targets = str(targets) if self.verbose: print(base + " execution on engine(s): %s" % str_targets) result = self.view.execute(cell, silent=False, block=False) result._fname = "%px" self.last_result = result if save_name: self.shell.user_ns[save_name] = result if block: if progress_after is None: progress_after = self.progress_after_seconds cm = result.stream_output() if stream_output else nullcontext() with cm: finished_waiting = False if progress_after > 0: # finite progress-after timeout # wait for 'quick' results before showing progress tic = time.perf_counter() deadline = tic + progress_after try: result.wait_for_output(timeout=progress_after) remaining = max(deadline - time.perf_counter(), 0) result.get(timeout=remaining) except TimeoutError: pass else: finished_waiting = True if not finished_waiting: if progress_after >= 0: # not an immediate result, start interactive progress result.wait_interactive() result.wait_for_output() result.get() # Skip stdout/stderr if streaming output result.display_outputs(groupby, result_only=stream_output) else: # return AsyncResult only on non-blocking submission return result @magic_arguments.magic_arguments() @exec_args @output_args def cell_px(self, line='', cell=None): """Executes the cell in parallel. Examples -------- :: In [24]: %%px --noblock ....: a = os.getpid() Async parallel execution on engine(s): all In [25]: %%px ....: print a [stdout:0] 1234 [stdout:1] 1235 [stdout:2] 1236 [stdout:3] 1237 """ args = magic_arguments.parse_argstring(self.cell_px, line) if args.targets: save_targets = self.view.targets self.view.targets = self._eval_target_str(args.targets) # if running local, don't block until after local has run block = False if args.local else args.block try: ar = self.parallel_execute( cell, block=block, groupby=args.groupby, save_name=args.save_name, stream_output=args.stream, progress_after=args.progress_after_seconds, ) finally: if args.targets: self.view.targets = save_targets # run locally after submitting remote block = self.view.block if args.block is None else args.block if args.local: self.shell.run_cell(cell) # now apply blocking behavor to remote execution if block: ar.get() ar.display_outputs(args.groupby) if not block: return ar def autopx(self, line=''): """Toggles auto parallel mode. Once this is called, all commands typed at the command line are send to the engines to be executed in parallel. To control which engine are used, the ``targets`` attribute of the view before entering ``%autopx`` mode. Then you can do the following:: In [25]: %autopx %autopx to enabled In [26]: a = 10 Parallel execution on engine(s): [0,1,2,3] In [27]: print a Parallel execution on engine(s): [0,1,2,3] [stdout:0] 10 [stdout:1] 10 [stdout:2] 10 [stdout:3] 10 In [27]: %autopx %autopx disabled """ if self._autopx: self._disable_autopx() else: self._enable_autopx() def _enable_autopx(self): """Enable %autopx mode by saving the original run_cell and installing pxrun_cell. """ self._original_run_cell = self.shell.run_cell self._original_run_nodes = self.shell.run_ast_nodes pxrun_cell = self.pxrun_cell if _iscoroutinefunction(self.shell.run_cell): # original is a coroutine, # wrap ours in a coroutine pxrun_cell = _asyncify(pxrun_cell) self.shell.run_cell = pxrun_cell pxrun_nodes = self.pxrun_nodes if _iscoroutinefunction(self.shell.run_ast_nodes): # original is a coroutine, # wrap ours in a coroutine pxrun_nodes = _asyncify(pxrun_nodes) self.shell.run_ast_nodes = pxrun_nodes self._autopx = True print("%autopx enabled") def _disable_autopx(self): """Disable %autopx by restoring the original InteractiveShell.run_cell.""" if self._autopx: self.shell.run_cell = self._original_run_cell self.shell.run_ast_nodes = self._original_run_nodes self._autopx = False print("%autopx disabled") def pxrun_nodes(self, *args, **kwargs): cell = self._px_cell if re.search(r'^\s*%autopx\b', cell): self._disable_autopx() return False else: try: result = self.view.execute(cell, silent=False, block=False) except: self.shell.showtraceback() return True else: if self.view.block: try: result.get() except: self.shell.showtraceback() return True else: result.display_outputs() return False def pxrun_cell(self, raw_cell, *args, **kwargs): """drop-in replacement for InteractiveShell.run_cell. This executes code remotely, instead of in the local namespace. See InteractiveShell.run_cell for details. """ self._px_cell = raw_cell return self._original_run_cell(raw_cell, *args, **kwargs) __doc__ = __doc__.format( AUTOPX_DOC=dedent(ParallelMagics.autopx.__doc__), PX_DOC=dedent(ParallelMagics.px.__doc__), RESULT_DOC=dedent(ParallelMagics.result.__doc__), CONFIG_DOC=dedent(ParallelMagics.pxconfig.__doc__), ) ipyparallel-7.1.0/ipyparallel/client/map.py000066400000000000000000000070551412533144600207720ustar00rootroot00000000000000# encoding: utf-8 """Classes used in scattering and gathering sequences. Scattering consists of partitioning a sequence and sending the various pieces to individual nodes in a cluster. """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import division import sys from itertools import chain from itertools import islice numpy = None def is_array(obj): """Is an object a numpy array? Avoids importing numpy until it is requested """ global numpy if 'numpy' not in sys.modules: return False if numpy is None: import numpy return isinstance(obj, numpy.ndarray) class Map: """A class for partitioning a sequence using a map.""" def getPartition(self, seq, p, q, n=None): """Returns the pth partition of q partitions of seq. The length can be specified as `n`, otherwise it is the value of `len(seq)` """ n = len(seq) if n is None else n # Test for error conditions here if p < 0 or p >= q: raise ValueError("must have 0 <= p <= q, but have p=%s,q=%s" % (p, q)) remainder = n % q basesize = n // q if p < remainder: low = p * (basesize + 1) high = low + basesize + 1 else: low = p * basesize + remainder high = low + basesize try: result = seq[low:high] except TypeError: # some objects (iterators) can't be sliced, # use islice: result = list(islice(seq, low, high)) return result def joinPartitions(self, listOfPartitions): return self.concatenate(listOfPartitions) def concatenate(self, listOfPartitions): testObject = listOfPartitions[0] # First see if we have a known array type if is_array(testObject): return numpy.concatenate(listOfPartitions) # Next try for Python sequence types if isinstance(testObject, (list, tuple)): return list(chain.from_iterable(listOfPartitions)) # If we have scalars, just return listOfPartitions return listOfPartitions class RoundRobinMap(Map): """Partitions a sequence in a round robin fashion. This currently does not work! """ def getPartition(self, seq, p, q, n=None): n = len(seq) if n is None else n return seq[p:n:q] def joinPartitions(self, listOfPartitions): testObject = listOfPartitions[0] # First see if we have a known array type if is_array(testObject): return self.flatten_array(listOfPartitions) if isinstance(testObject, (list, tuple)): return self.flatten_list(listOfPartitions) return listOfPartitions def flatten_array(self, listOfPartitions): test = listOfPartitions[0] shape = list(test.shape) shape[0] = sum([p.shape[0] for p in listOfPartitions]) A = numpy.ndarray(shape) N = shape[0] q = len(listOfPartitions) for p, part in enumerate(listOfPartitions): A[p:N:q] = part return A def flatten_list(self, listOfPartitions): flat = [] for i in range(len(listOfPartitions[0])): flat.extend([part[i] for part in listOfPartitions if len(part) > i]) return flat def mappable(obj): """return whether an object is mappable or not.""" if isinstance(obj, (tuple, list)): return True if is_array(obj): return True return False dists = {'b': Map, 'r': RoundRobinMap} ipyparallel-7.1.0/ipyparallel/client/remotefunction.py000066400000000000000000000227101412533144600232510ustar00rootroot00000000000000"""Remote Functions and decorators for Views.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import division import sys import warnings from inspect import signature from decorator import decorator from . import map as Map from ..serialize import PrePickled from .asyncresult import AsyncMapResult # ----------------------------------------------------------------------------- # Functions and Decorators # ----------------------------------------------------------------------------- def remote(view, block=None, **flags): """Turn a function into a remote function. This method can be used for map:: In [1]: @remote(view,block=True) ...: def func(a): ...: pass """ def remote_function(f): return RemoteFunction(view, f, block=block, **flags) return remote_function def parallel(view, dist='b', block=None, ordered=True, **flags): """Turn a function into a parallel remote function. This method can be used for map:: In [1]: @parallel(view, block=True) ...: def func(a): ...: pass """ def parallel_function(f): return ParallelFunction( view, f, dist=dist, block=block, ordered=ordered, **flags ) return parallel_function def getname(f): """Get the name of an object. For use in case of callables that are not functions, and thus may not have __name__ defined. Order: f.__name__ > f.name > str(f) """ try: return f.__name__ except: pass try: return f.name except: pass return str(f) @decorator def sync_view_results(f, self, *args, **kwargs): """sync relevant results from self.client to our results attribute. This is a clone of view.sync_results, but for remote functions """ view = self.view if view._in_sync_results: return f(self, *args, **kwargs) view._in_sync_results = True try: ret = f(self, *args, **kwargs) finally: view._in_sync_results = False view._sync_results() return ret # -------------------------------------------------------------------------- # Classes # -------------------------------------------------------------------------- class RemoteFunction: """Turn an existing function into a remote function. Parameters ---------- view : View instance The view to be used for execution f : callable The function to be wrapped into a remote function block : bool [default: None] Whether to wait for results or not. The default behavior is to use the current `block` attribute of `view` **flags : remaining kwargs are passed to View.temp_flags """ view = None # the remote connection func = None # the wrapped function block = None # whether to block flags = None # dict of extra kwargs for temp_flags def __init__(self, view, f, block=None, **flags): self.view = view self.func = f self.block = block self.flags = flags # copy function attributes for nicer inspection # of decorated functions self.__name__ = getname(f) if getattr(f, '__doc__', None): self.__doc__ = '{} wrapping:\n{}'.format( self.__class__.__name__, f.__doc__, ) if getattr(f, '__signature__', None): self.__signature__ = f.__signature__ else: try: self.__signature__ = signature(f) except Exception: # no signature, but that's okay pass def __call__(self, *args, **kwargs): block = self.view.block if self.block is None else self.block with self.view.temp_flags(block=block, **self.flags): return self.view.apply(self.func, *args, **kwargs) if sys.version_info[0] >= 3: _map = lambda f, *sequences: list(map(f, *sequences)) else: _map = map _prepickled_map = None class ParallelFunction(RemoteFunction): """Class for mapping a function to sequences. This will distribute the sequences according the a mapper, and call the function on each sub-sequence. If called via map, then the function will be called once on each element, rather that each sub-sequence. Parameters ---------- view : View instance The view to be used for execution f : callable The function to be wrapped into a remote function dist : str [default: 'b'] The key for which mapObject to use to distribute sequences options are: * 'b' : use contiguous chunks in order * 'r' : use round-robin striping block : bool [default: None] Whether to wait for results or not. The default behavior is to use the current `block` attribute of `view` chunksize : int or None The size of chunk to use when breaking up sequences in a load-balanced manner ordered : bool [default: True] Whether the result should be kept in order. If False, results become available as they arrive, regardless of submission order. return_exceptions : bool [default: False] **flags remaining kwargs are passed to View.temp_flags """ chunksize = None ordered = None mapObject = None def __init__( self, view, f, dist='b', block=None, chunksize=None, ordered=True, return_exceptions=False, **flags, ): super(ParallelFunction, self).__init__(view, f, block=block, **flags) self.chunksize = chunksize self.ordered = ordered self.return_exceptions = return_exceptions mapClass = Map.dists[dist] self.mapObject = mapClass() @sync_view_results def __call__(self, *sequences, **kwargs): global _prepickled_map if _prepickled_map is None: _prepickled_map = PrePickled(_map) client = self.view.client _mapping = kwargs.pop('__ipp_mapping', False) if kwargs: raise TypeError("Unexpected keyword arguments: %s" % kwargs) lens = [] maxlen = minlen = -1 for i, seq in enumerate(sequences): try: n = len(seq) except Exception: seq = list(seq) if isinstance(sequences, tuple): # can't alter a tuple sequences = list(sequences) sequences[i] = seq n = len(seq) if n > maxlen: maxlen = n if minlen == -1 or n < minlen: minlen = n lens.append(n) if maxlen == 0: # nothing to iterate over return [] # check that the length of sequences match if not _mapping and minlen != maxlen: msg = 'all sequences must have equal length, but have %s' % lens raise ValueError(msg) balanced = 'Balanced' in self.view.__class__.__name__ if balanced: if self.chunksize: nparts = maxlen // self.chunksize + int(maxlen % self.chunksize > 0) else: nparts = maxlen targets = [None] * nparts else: if self.chunksize: warnings.warn( "`chunksize` is ignored unless load balancing", UserWarning ) # multiplexed: targets = self.view.targets # 'all' is lazily evaluated at execution time, which is now: if targets == 'all': targets = client._build_targets(targets)[1] elif isinstance(targets, int): # single-engine view, targets must be iterable targets = [targets] nparts = len(targets) futures = [] pf = PrePickled(self.func) for index, t in enumerate(targets): args = [] for seq in sequences: part = self.mapObject.getPartition(seq, index, nparts, maxlen) args.append(part) if sum([len(arg) for arg in args]) == 0: continue args = [PrePickled(arg) for arg in args] if _mapping: f = _prepickled_map args = [pf] + args else: f = pf view = self.view if balanced else client[t] with view.temp_flags(block=False, **self.flags): ar = view.apply(f, *args) ar.owner = False futures.extend(ar._children) r = AsyncMapResult( self.view.client, futures, self.mapObject, fname=getname(self.func), ordered=self.ordered, return_exceptions=self.return_exceptions, ) if self.block: try: return r.get() except KeyboardInterrupt: return r else: return r def map(self, *sequences): """call a function on each element of one or more sequence(s) remotely. This should behave very much like the builtin map, but return an AsyncMapResult if self.block is False. That means it can take generators (will be cast to lists locally), and mismatched sequence lengths will be padded with None. """ return self(*sequences, __ipp_mapping=True) __all__ = ['remote', 'parallel', 'RemoteFunction', 'ParallelFunction'] ipyparallel-7.1.0/ipyparallel/client/view.py000066400000000000000000001441051412533144600211650ustar00rootroot00000000000000"""Views of remote engines.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import builtins import concurrent.futures import inspect import warnings from collections import deque from contextlib import contextmanager from decorator import decorator from IPython import get_ipython from traitlets import Any from traitlets import Bool from traitlets import CFloat from traitlets import Dict from traitlets import HasTraits from traitlets import Instance from traitlets import Integer from traitlets import List from traitlets import Set from . import map as Map from .. import serialize from ..serialize import PrePickled from .asyncresult import AsyncMapResult from .asyncresult import AsyncResult from .remotefunction import getname from .remotefunction import parallel from .remotefunction import ParallelFunction from .remotefunction import remote from ipyparallel import util from ipyparallel.controller.dependency import Dependency from ipyparallel.controller.dependency import dependent # ----------------------------------------------------------------------------- # Decorators # ----------------------------------------------------------------------------- @decorator def save_ids(f, self, *args, **kwargs): """Keep our history and outstanding attributes up to date after a method call.""" n_previous = len(self.client.history) try: ret = f(self, *args, **kwargs) finally: nmsgs = len(self.client.history) - n_previous msg_ids = self.client.history[-nmsgs:] self.history.extend(msg_ids) self.outstanding.update(msg_ids) return ret @decorator def sync_results(f, self, *args, **kwargs): """sync relevant results from self.client to our results attribute.""" if self._in_sync_results: return f(self, *args, **kwargs) self._in_sync_results = True try: ret = f(self, *args, **kwargs) finally: self._in_sync_results = False self._sync_results() return ret # ----------------------------------------------------------------------------- # Classes # ----------------------------------------------------------------------------- class View(HasTraits): """Base View class for more convenint apply(f,*args,**kwargs) syntax via attributes. Don't use this class, use subclasses. Methods ------- spin flushes incoming results and registration state changes control methods spin, and requesting `ids` also ensures up to date wait wait on one or more msg_ids execution methods apply legacy: execute, run data movement push, pull, scatter, gather query methods get_result, queue_status, purge_results, result_status control methods abort, shutdown """ # flags block = Bool(False) track = Bool(False) targets = Any() history = List() outstanding = Set() results = Dict() client = Instance('ipyparallel.Client', allow_none=True) _socket = Any() _flag_names = List(['targets', 'block', 'track']) _in_sync_results = Bool(False) _targets = Any() _idents = Any() def __init__(self, client=None, socket=None, **flags): super(View, self).__init__(client=client, _socket=socket) self.results = client.results self.block = client.block self.executor = ViewExecutor(self) self.set_flags(**flags) assert not self.__class__ is View, "Don't use base View objects, use subclasses" def __repr__(self): strtargets = str(self.targets) if len(strtargets) > 16: strtargets = strtargets[:12] + '...]' return "<%s %s>" % (self.__class__.__name__, strtargets) def __len__(self): if isinstance(self.targets, list): return len(self.targets) elif isinstance(self.targets, int): return 1 else: return len(self.client) def set_flags(self, **kwargs): """set my attribute flags by keyword. Views determine behavior with a few attributes (`block`, `track`, etc.). These attributes can be set all at once by name with this method. Parameters ---------- block : bool whether to wait for results track : bool whether to create a MessageTracker to allow the user to safely edit after arrays and buffers during non-copying sends. """ for name, value in kwargs.items(): if name not in self._flag_names: raise KeyError("Invalid name: %r" % name) else: setattr(self, name, value) @contextmanager def temp_flags(self, **kwargs): """temporarily set flags, for use in `with` statements. See set_flags for permanent setting of flags Examples -------- >>> view.track=False ... >>> with view.temp_flags(track=True): ... ar = view.apply(dostuff, my_big_array) ... ar.tracker.wait() # wait for send to finish >>> view.track False """ # preflight: save flags, and set temporaries saved_flags = {} for f in self._flag_names: saved_flags[f] = getattr(self, f) self.set_flags(**kwargs) # yield to the with-statement block try: yield finally: # postflight: restore saved flags self.set_flags(**saved_flags) # ---------------------------------------------------------------- # apply # ---------------------------------------------------------------- def _sync_results(self): """to be called by @sync_results decorator after submitting any tasks. """ delta = self.outstanding.difference(self.client.outstanding) completed = self.outstanding.intersection(delta) self.outstanding = self.outstanding.difference(completed) @sync_results @save_ids def _really_apply(self, f, args, kwargs, block=None, **options): """wrapper for client.send_apply_request""" raise NotImplementedError("Implement in subclasses") def apply(self, __ipp_f, *args, **kwargs): """calls ``f(*args, **kwargs)`` on remote engines, returning the result. This method sets all apply flags via this View's attributes. Returns :class:`~ipyparallel.client.asyncresult.AsyncResult` instance if ``self.block`` is False, otherwise the return value of ``f(*args, **kwargs)``. """ return self._really_apply(__ipp_f, args, kwargs) def apply_async(self, __ipp_f, *args, **kwargs): """calls ``f(*args, **kwargs)`` on remote engines in a nonblocking manner. Returns :class:`~ipyparallel.client.asyncresult.AsyncResult` instance. """ return self._really_apply(__ipp_f, args, kwargs, block=False) def apply_sync(self, __ipp_f, *args, **kwargs): """calls ``f(*args, **kwargs)`` on remote engines in a blocking manner, returning the result. """ return self._really_apply(__ipp_f, args, kwargs, block=True) # ---------------------------------------------------------------- # wrappers for client and control methods # ---------------------------------------------------------------- @sync_results def spin(self): """spin the client, and sync""" self.client.spin() @sync_results def wait(self, jobs=None, timeout=-1): """waits on one or more `jobs`, for up to `timeout` seconds. Parameters ---------- jobs : int, str, or list of ints and/or strs, or one or more AsyncResult objects ints are indices to self.history strs are msg_ids default: wait on all outstanding messages timeout : float a time in seconds, after which to give up. default is -1, which means no timeout Returns ------- True : when all msg_ids are done False : timeout reached, some msg_ids still outstanding """ if jobs is None: jobs = self.history return self.client.wait(jobs, timeout) def abort(self, jobs=None, targets=None, block=None): """Abort jobs on my engines. Note: only jobs that have not started yet can be aborted. To halt a running job, you must interrupt the engine(s) via the Cluster API. Parameters ---------- jobs : None, str, list of strs, optional if None: abort all jobs. else: abort specific msg_id(s). """ block = block if block is not None else self.block targets = targets if targets is not None else self.targets jobs = jobs if jobs is not None else list(self.outstanding) return self.client.abort(jobs=jobs, targets=targets, block=block) def queue_status(self, targets=None, verbose=False): """Fetch the Queue status of my engines""" targets = targets if targets is not None else self.targets return self.client.queue_status(targets=targets, verbose=verbose) def purge_results(self, jobs=[], targets=[]): """Instruct the controller to forget specific results.""" if targets is None or targets == 'all': targets = self.targets return self.client.purge_results(jobs=jobs, targets=targets) def shutdown(self, targets=None, restart=False, hub=False, block=None): """Terminates one or more engine processes, optionally including the hub.""" block = self.block if block is None else block if targets is None or targets == 'all': targets = self.targets return self.client.shutdown( targets=targets, restart=restart, hub=hub, block=block ) def get_result(self, indices_or_msg_ids=None, block=None, owner=False): """return one or more results, specified by history index or msg_id. See :meth:`ipyparallel.client.client.Client.get_result` for details. """ if indices_or_msg_ids is None: indices_or_msg_ids = -1 if isinstance(indices_or_msg_ids, int): indices_or_msg_ids = self.history[indices_or_msg_ids] elif isinstance(indices_or_msg_ids, (list, tuple, set)): indices_or_msg_ids = list(indices_or_msg_ids) for i, index in enumerate(indices_or_msg_ids): if isinstance(index, int): indices_or_msg_ids[i] = self.history[index] return self.client.get_result(indices_or_msg_ids, block=block, owner=owner) # ------------------------------------------------------------------- # Map # ------------------------------------------------------------------- @sync_results def map(self, f, *sequences, **kwargs): """override in subclasses""" raise NotImplementedError() def map_async(self, f, *sequences, **kwargs): """Parallel version of builtin :func:`python:map`, using this view's engines. This is equivalent to ``map(...block=False)``. See `self.map` for details. """ if 'block' in kwargs: raise TypeError("map_async doesn't take a `block` keyword argument.") kwargs['block'] = False return self.map(f, *sequences, **kwargs) def map_sync(self, f, *sequences, **kwargs): """Parallel version of builtin :func:`python:map`, using this view's engines. This is equivalent to ``map(...block=True)``. See `self.map` for details. """ if 'block' in kwargs: raise TypeError("map_sync doesn't take a `block` keyword argument.") kwargs['block'] = True return self.map(f, *sequences, **kwargs) def imap(self, f, *sequences, **kwargs): """Parallel version of :func:`itertools.imap`. See `self.map` for details. """ return iter(self.map_async(f, *sequences, **kwargs)) # ------------------------------------------------------------------- # Decorators # ------------------------------------------------------------------- def remote(self, block=None, **flags): """Decorator for making a RemoteFunction""" block = self.block if block is None else block return remote(self, block=block, **flags) def parallel(self, dist='b', block=None, **flags): """Decorator for making a ParallelFunction""" block = self.block if block is None else block return parallel(self, dist=dist, block=block, **flags) class DirectView(View): """Direct Multiplexer View of one or more engines. These are created via indexed access to a client: >>> dv_1 = client[1] >>> dv_all = client[:] >>> dv_even = client[::2] >>> dv_some = client[1:3] This object provides dictionary access to engine namespaces: # push a=5: >>> dv['a'] = 5 # pull 'foo': >>> dv['foo'] """ def __init__(self, client=None, socket=None, targets=None): super(DirectView, self).__init__(client=client, socket=socket, targets=targets) @property def importer(self): """sync_imports(local=True) as a property. See sync_imports for details. """ return self.sync_imports(True) @contextmanager def sync_imports(self, local=True, quiet=False): """Context Manager for performing simultaneous local and remote imports. 'import x as y' will *not* work. The 'as y' part will simply be ignored. If `local=True`, then the package will also be imported locally. If `quiet=True`, no output will be produced when attempting remote imports. Note that remote-only (`local=False`) imports have not been implemented. >>> with view.sync_imports(): ... from numpy import recarray importing recarray from numpy on engine(s) """ local_import = builtins.__import__ modules = set() results = [] # get the calling frame # that's two steps up due to `@contextmanager` context_frame = inspect.getouterframes(inspect.currentframe())[2].frame @util.interactive def remote_import(name, fromlist, level): """the function to be passed to apply, that actually performs the import on the engine, and loads up the user namespace. """ import sys user_ns = globals() mod = __import__(name, fromlist=fromlist, level=level) if fromlist: for key in fromlist: user_ns[key] = getattr(mod, key) else: user_ns[name] = sys.modules[name] def view_import(name, globals={}, locals={}, fromlist=[], level=0): """the drop-in replacement for __import__, that optionally imports locally as well. """ # don't override nested imports save_import = builtins.__import__ builtins.__import__ = local_import import_frame = inspect.getouterframes(inspect.currentframe())[1].frame if import_frame is not context_frame: # only forward imports from the context frame, # not secondary imports # TODO: does this ever happen, or is the above `__import__` enough? return local_import(name, globals, locals, fromlist, level) if local: mod = local_import(name, globals, locals, fromlist, level) else: raise NotImplementedError("remote-only imports not yet implemented") key = name + ':' + ','.join(fromlist or []) if level <= 0 and key not in modules: modules.add(key) if not quiet: if fromlist: print( "importing %s from %s on engine(s)" % (','.join(fromlist), name) ) else: print("importing %s on engine(s)" % name) results.append(self.apply_async(remote_import, name, fromlist, level)) # restore override builtins.__import__ = save_import return mod # override __import__ builtins.__import__ = view_import try: # enter the block yield except ImportError: if local: raise else: # ignore import errors if not doing local imports pass finally: # always restore __import__ builtins.__import__ = local_import for r in results: # raise possible remote ImportErrors here r.get() def use_dill(self): """Expand serialization support with dill adds support for closures, etc. This calls ipyparallel.serialize.use_dill() here and on each engine. """ serialize.use_dill() return self.apply(serialize.use_dill) def use_cloudpickle(self): """Expand serialization support with cloudpickle. This calls ipyparallel.serialize.use_cloudpickle() here and on each engine. """ serialize.use_cloudpickle() return self.apply(serialize.use_cloudpickle) def use_pickle(self): """Restore This reverts changes to serialization caused by `use_dill|.cloudpickle`. """ serialize.use_pickle() return self.apply(serialize.use_pickle) @sync_results @save_ids def _really_apply( self, f, args=None, kwargs=None, targets=None, block=None, track=None ): """calls f(*args, **kwargs) on remote engines, returning the result. This method sets all of `apply`'s flags via this View's attributes. Parameters ---------- f : callable args : list [default: empty] kwargs : dict [default: empty] targets : target list [default: self.targets] where to run block : bool [default: self.block] whether to block track : bool [default: self.track] whether to ask zmq to track the message, for safe non-copying sends Returns ------- if self.block is False: returns AsyncResult else: returns actual result of f(*args, **kwargs) on the engine(s) This will be a list of self.targets is also a list (even length 1), or the single result if self.targets is an integer engine id """ args = [] if args is None else args kwargs = {} if kwargs is None else kwargs block = self.block if block is None else block track = self.track if track is None else track targets = self.targets if targets is None else targets _idents, _targets = self.client._build_targets(targets) futures = [] pf = PrePickled(f) pargs = [PrePickled(arg) for arg in args] pkwargs = {k: PrePickled(v) for k, v in kwargs.items()} for ident in _idents: future = self.client.send_apply_request( self._socket, pf, pargs, pkwargs, track=track, ident=ident ) futures.append(future) if track: trackers = [_.tracker for _ in futures] else: trackers = [] if isinstance(targets, int): futures = futures[0] ar = AsyncResult( self.client, futures, fname=getname(f), targets=_targets, owner=True ) if block: try: return ar.get() except KeyboardInterrupt: pass return ar @sync_results def map(self, f, *sequences, block=None, track=False, return_exceptions=False): """Parallel version of builtin `map`, using this View's `targets`. There will be one task per target, so work will be chunked if the sequences are longer than `targets`. Results can be iterated as they are ready, but will become available in chunks. .. versionadded:: 7.0 `return_exceptions` Parameters ---------- f : callable function to be mapped *sequences : one or more sequences of matching length the sequences to be distributed and passed to `f` block : bool [default self.block] whether to wait for the result or not track : bool [default False] Track underlying zmq send to indicate when it is safe to modify memory. Only for zero-copy sends such as numpy arrays that are going to be modified in-place. return_exceptions : bool [default False] Return remote Exceptions in the result sequence instead of raising them. Returns ------- If block=False An :class:`~ipyparallel.client.asyncresult.AsyncMapResult` instance. An object like AsyncResult, but which reassembles the sequence of results into a single list. AsyncMapResults can be iterated through before all results are complete. else A list, the result of ``map(f,*sequences)`` """ if block is None: block = self.block assert len(sequences) > 0, "must have some sequences to map onto!" pf = ParallelFunction( self, f, block=block, track=track, return_exceptions=return_exceptions ) return pf.map(*sequences) @sync_results @save_ids def execute(self, code, silent=True, targets=None, block=None): """Executes `code` on `targets` in blocking or nonblocking manner. ``execute`` is always `bound` (affects engine namespace) Parameters ---------- code : str the code string to be executed block : bool whether or not to wait until done to return default: self.block """ block = self.block if block is None else block targets = self.targets if targets is None else targets _idents, _targets = self.client._build_targets(targets) futures = [] for ident in _idents: future = self.client.send_execute_request( self._socket, code, silent=silent, ident=ident ) futures.append(future) if isinstance(targets, int): futures = futures[0] ar = AsyncResult( self.client, futures, fname='execute', targets=_targets, owner=True ) if block: try: ar.get() ar.wait_for_output() except KeyboardInterrupt: pass return ar def run(self, filename, targets=None, block=None): """Execute contents of `filename` on my engine(s). This simply reads the contents of the file and calls `execute`. Parameters ---------- filename : str The path to the file targets : int/str/list of ints/strs the engines on which to execute default : all block : bool whether or not to wait until done default: self.block """ with open(filename, 'r') as f: # add newline in case of trailing indented whitespace # which will cause SyntaxError code = f.read() + '\n' return self.execute(code, block=block, targets=targets) def update(self, ns): """update remote namespace with dict `ns` See `push` for details. """ return self.push(ns, block=self.block, track=self.track) def push(self, ns, targets=None, block=None, track=None): """update remote namespace with dict `ns` Parameters ---------- ns : dict dict of keys with which to update engine namespace(s) block : bool [default : self.block] whether to wait to be notified of engine receipt """ block = block if block is not None else self.block track = track if track is not None else self.track targets = targets if targets is not None else self.targets # applier = self.apply_sync if block else self.apply_async if not isinstance(ns, dict): raise TypeError("Must be a dict, not %s" % type(ns)) return self._really_apply( util._push, kwargs=ns, block=block, track=track, targets=targets ) def get(self, key_s): """get object(s) by `key_s` from remote namespace see `pull` for details. """ # block = block if block is not None else self.block return self.pull(key_s, block=True) def pull(self, names, targets=None, block=None): """get object(s) by `name` from remote namespace will return one object if it is a key. can also take a list of keys, in which case it will return a list of objects. """ block = block if block is not None else self.block targets = targets if targets is not None else self.targets if isinstance(names, str): pass elif isinstance(names, (list, tuple, set)): for key in names: if not isinstance(key, str): raise TypeError("keys must be str, not type %r" % type(key)) else: raise TypeError("names must be strs, not %r" % names) return self._really_apply(util._pull, (names,), block=block, targets=targets) def scatter( self, key, seq, dist='b', flatten=False, targets=None, block=None, track=None ): """ Partition a Python sequence and send the partitions to a set of engines. """ block = block if block is not None else self.block track = track if track is not None else self.track targets = targets if targets is not None else self.targets # construct integer ID list: targets = self.client._build_targets(targets)[1] mapObject = Map.dists[dist]() nparts = len(targets) futures = [] trackers = [] for index, engineid in enumerate(targets): partition = mapObject.getPartition(seq, index, nparts) if flatten and len(partition) == 1: ns = {key: partition[0]} else: ns = {key: partition} r = self.push(ns, block=False, track=track, targets=engineid) r.owner = False futures.extend(r._children) r = AsyncResult( self.client, futures, fname='scatter', targets=targets, owner=True ) if block: r.wait() else: return r @sync_results @save_ids def gather(self, key, dist='b', targets=None, block=None): """ Gather a partitioned sequence on a set of engines as a single local seq. """ block = block if block is not None else self.block targets = targets if targets is not None else self.targets mapObject = Map.dists[dist]() msg_ids = [] # construct integer ID list: targets = self.client._build_targets(targets)[1] futures = [] for index, engineid in enumerate(targets): ar = self.pull(key, block=False, targets=engineid) ar.owner = False futures.extend(ar._children) r = AsyncMapResult(self.client, futures, mapObject, fname='gather') if block: try: return r.get() except KeyboardInterrupt: pass return r def __getitem__(self, key): return self.get(key) def __setitem__(self, key, value): self.update({key: value}) def clear(self, targets=None, block=None): """Clear the remote namespaces on my engines.""" block = block if block is not None else self.block targets = targets if targets is not None else self.targets return self.client.clear(targets=targets, block=block) # ---------------------------------------- # activate for %px, %autopx, etc. magics # ---------------------------------------- def activate(self, suffix=''): """Activate IPython magics associated with this View Defines the magics `%px, %autopx, %pxresult, %%px, %pxconfig` Parameters ---------- suffix : str [default: ''] The suffix, if any, for the magics. This allows you to have multiple views associated with parallel magics at the same time. e.g. ``rc[::2].activate(suffix='_even')`` will give you the magics ``%px_even``, ``%pxresult_even``, etc. for running magics on the even engines. """ from ipyparallel.client.magics import ParallelMagics ip = get_ipython() if ip is None: warnings.warn( "The IPython parallel magics (%px, etc.) only work within IPython." ) return M = ParallelMagics(ip, self, suffix) ip.magics_manager.register(M) @decorator def _not_coalescing(method, self, *args, **kwargs): """Decorator for broadcast methods that can't use reply coalescing""" is_coalescing = self.is_coalescing try: self.is_coalescing = False return method(self, *args, **kwargs) finally: self.is_coalescing = is_coalescing class BroadcastView(DirectView): is_coalescing = Bool(False) def _init_metadata(self, target_tuples): """initialize request metadata""" return dict( targets=target_tuples, is_broadcast=True, is_coalescing=self.is_coalescing, ) def _make_async_result(self, message_future, s_idents, **kwargs): original_msg_id = message_future.msg_id if not self.is_coalescing: futures = [] for ident in s_idents: msg_and_target_id = f'{original_msg_id}_{ident}' future = self.client.create_message_futures( msg_and_target_id, async_result=True, track=True ) self.client.outstanding.add(msg_and_target_id) self.client._outstanding_dict[ident].add(msg_and_target_id) self.outstanding.add(msg_and_target_id) futures.append(future[0]) if original_msg_id in self.outstanding: self.outstanding.remove(original_msg_id) else: self.client.outstanding.add(original_msg_id) for ident in s_idents: self.client._outstanding_dict[ident].add(original_msg_id) futures = message_future ar = AsyncResult(self.client, futures, owner=True, **kwargs) if self.is_coalescing: # if coalescing, discard outstanding-tracking when we are done def _rm_outstanding(_): for ident in s_idents: if ident in self.client._outstanding_dict: self.client._outstanding_dict[ident].discard(original_msg_id) ar.add_done_callback(_rm_outstanding) return ar @sync_results @save_ids def _really_apply( self, f, args=None, kwargs=None, block=None, track=None, targets=None ): args = [] if args is None else args kwargs = {} if kwargs is None else kwargs block = self.block if block is None else block track = self.track if track is None else track targets = self.targets if targets is None else targets idents, _targets = self.client._build_targets(targets) futures = [] pf = PrePickled(f) pargs = [PrePickled(arg) for arg in args] pkwargs = {k: PrePickled(v) for k, v in kwargs.items()} s_idents = [ident.decode("utf8") for ident in idents] target_tuples = list(zip(s_idents, _targets)) metadata = self._init_metadata(target_tuples) ar = None def make_asyncresult(message_future): nonlocal ar ar = self._make_async_result( message_future, s_idents, fname=getname(f), targets=_targets ) self.client.send_apply_request( self._socket, pf, pargs, pkwargs, track=track, metadata=metadata, message_future_hook=make_asyncresult, ) if block: try: return ar.get() except KeyboardInterrupt: pass return ar @sync_results @save_ids @_not_coalescing def execute(self, code, silent=True, targets=None, block=None): """Executes `code` on `targets` in blocking or nonblocking manner. ``execute`` is always `bound` (affects engine namespace) Parameters ---------- code : str the code string to be executed block : bool whether or not to wait until done to return default: self.block """ block = self.block if block is None else block targets = self.targets if targets is None else targets _idents, _targets = self.client._build_targets(targets) s_idents = [ident.decode("utf8") for ident in _idents] target_tuples = list(zip(s_idents, _targets)) metadata = self._init_metadata(target_tuples) ar = None def make_asyncresult(message_future): nonlocal ar ar = self._make_async_result( message_future, s_idents, fname='execute', targets=_targets ) message_future = self.client.send_execute_request( self._socket, code, silent=silent, metadata=metadata, message_future_hook=make_asyncresult, ) if block: try: ar.get() ar.wait_for_output() except KeyboardInterrupt: pass return ar def map(self, f, *sequences, **kwargs): raise NotImplementedError("BroadcastView.map not yet implemented") # scatter/gather cannot be coalescing yet scatter = _not_coalescing(DirectView.scatter) gather = _not_coalescing(DirectView.gather) class LoadBalancedView(View): """An load-balancing View that only executes via the Task scheduler. Load-balanced views can be created with the client's `view` method: >>> v = client.load_balanced_view() or targets can be specified, to restrict the potential destinations: >>> v = client.load_balanced_view([1,3]) which would restrict loadbalancing to between engines 1 and 3. """ follow = Any() after = Any() timeout = CFloat() retries = Integer(0) _task_scheme = Any() _flag_names = List( ['targets', 'block', 'track', 'follow', 'after', 'timeout', 'retries'] ) def __init__(self, client=None, socket=None, **flags): super(LoadBalancedView, self).__init__(client=client, socket=socket, **flags) self._task_scheme = client._task_scheme def _validate_dependency(self, dep): """validate a dependency. For use in `set_flags`. """ if dep is None or isinstance(dep, (str, AsyncResult, Dependency)): return True elif isinstance(dep, (list, set, tuple)): for d in dep: if not isinstance(d, (str, AsyncResult)): return False elif isinstance(dep, dict): if set(dep.keys()) != set(Dependency().as_dict().keys()): return False if not isinstance(dep['msg_ids'], list): return False for d in dep['msg_ids']: if not isinstance(d, str): return False else: return False return True def _render_dependency(self, dep): """helper for building jsonable dependencies from various input forms.""" if isinstance(dep, Dependency): return dep.as_dict() elif isinstance(dep, AsyncResult): return dep.msg_ids elif dep is None: return [] else: # pass to Dependency constructor return list(Dependency(dep)) def set_flags(self, **kwargs): """set my attribute flags by keyword. A View is a wrapper for the Client's apply method, but with attributes that specify keyword arguments, those attributes can be set by keyword argument with this method. Parameters ---------- block : bool whether to wait for results track : bool whether to create a MessageTracker to allow the user to safely edit after arrays and buffers during non-copying sends. after : Dependency or collection of msg_ids Only for load-balanced execution (targets=None) Specify a list of msg_ids as a time-based dependency. This job will only be run *after* the dependencies have been met. follow : Dependency or collection of msg_ids Only for load-balanced execution (targets=None) Specify a list of msg_ids as a location-based dependency. This job will only be run on an engine where this dependency is met. timeout : float/int or None Only for load-balanced execution (targets=None) Specify an amount of time (in seconds) for the scheduler to wait for dependencies to be met before failing with a DependencyTimeout. retries : int Number of times a task will be retried on failure. """ super(LoadBalancedView, self).set_flags(**kwargs) for name in ('follow', 'after'): if name in kwargs: value = kwargs[name] if self._validate_dependency(value): setattr(self, name, value) else: raise ValueError("Invalid dependency: %r" % value) if 'timeout' in kwargs: t = kwargs['timeout'] if not isinstance(t, (int, float, type(None))): raise TypeError("Invalid type for timeout: %r" % type(t)) if t is not None: if t < 0: raise ValueError("Invalid timeout: %s" % t) self.timeout = t @sync_results @save_ids def _really_apply( self, f, args=None, kwargs=None, block=None, track=None, after=None, follow=None, timeout=None, targets=None, retries=None, ): """calls f(*args, **kwargs) on a remote engine, returning the result. This method temporarily sets all of `apply`'s flags for a single call. Parameters ---------- f : callable args : list [default: empty] kwargs : dict [default: empty] block : bool [default: self.block] whether to block track : bool [default: self.track] whether to ask zmq to track the message, for safe non-copying sends !!!!!! TODO : THE REST HERE !!!! Returns ------- if self.block is False: returns AsyncResult else: returns actual result of f(*args, **kwargs) on the engine(s) This will be a list of self.targets is also a list (even length 1), or the single result if self.targets is an integer engine id """ # validate whether we can run if self._socket.closed(): msg = "Task farming is disabled" if self._task_scheme == 'pure': msg += " because the pure ZMQ scheduler cannot handle" msg += " disappearing engines." raise RuntimeError(msg) if self._task_scheme == 'pure': # pure zmq scheme doesn't support extra features msg = "Pure ZMQ scheduler doesn't support the following flags:" "follow, after, retries, targets, timeout" if follow or after or retries or targets or timeout: # hard fail on Scheduler flags raise RuntimeError(msg) if isinstance(f, dependent): # soft warn on functional dependencies warnings.warn(msg, RuntimeWarning) # build args args = [] if args is None else args kwargs = {} if kwargs is None else kwargs block = self.block if block is None else block track = self.track if track is None else track after = self.after if after is None else after retries = self.retries if retries is None else retries follow = self.follow if follow is None else follow timeout = self.timeout if timeout is None else timeout targets = self.targets if targets is None else targets if not isinstance(retries, int): raise TypeError('retries must be int, not %r' % type(retries)) if targets is None: idents = [] else: idents = self.client._build_targets(targets)[0] # ensure *not* bytes idents = [ident.decode() for ident in idents] after = self._render_dependency(after) follow = self._render_dependency(follow) metadata = dict( after=after, follow=follow, timeout=timeout, targets=idents, retries=retries ) future = self.client.send_apply_request( self._socket, f, args, kwargs, track=track, metadata=metadata ) ar = AsyncResult( self.client, future, fname=getname(f), targets=None, owner=True, ) if block: try: return ar.get() except KeyboardInterrupt: pass return ar @sync_results @save_ids def map( self, f, *sequences, block=None, chunksize=1, ordered=True, return_exceptions=False, ): """Parallel version of builtin `map`, load-balanced by this View. Each `chunksize` elements will be a separate task, and will be load-balanced. This lets individual elements be available for iteration as soon as they arrive. .. versionadded:: 7.0 `return_exceptions` Parameters ---------- f : callable function to be mapped *sequences : one or more sequences of matching length the sequences to be distributed and passed to `f` block : bool [default self.block] whether to wait for the result or not chunksize : int [default 1] how many elements should be in each task. ordered : bool [default True] Whether the results should be gathered as they arrive, or enforce the order of submission. Only applies when iterating through AsyncMapResult as results arrive. Has no effect when block=True. return_exceptions: bool [default False] Return Exceptions instead of raising on the first exception. Returns ------- if block=False An :class:`~ipyparallel.client.asyncresult.AsyncMapResult` instance. An object like AsyncResult, but which reassembles the sequence of results into a single list. AsyncMapResults can be iterated through before all results are complete. else A list, the result of ``map(f,*sequences)`` """ # default if block is None: block = self.block assert len(sequences) > 0, "must have some sequences to map onto!" pf = ParallelFunction( self, f, block=block, chunksize=chunksize, ordered=ordered, return_exceptions=return_exceptions, ) return pf.map(*sequences) def imap( self, f, *sequences, ordered=True, max_outstanding='auto', return_exceptions=False, ): """Parallel version of lazily-evaluated `imap`, load-balanced by this View. `ordered`, and `max_outstanding` can be specified by keyword only. Unlike other map functions in IPython Parallel, this one does not consume the full iterable before submitting work, returning a single 'AsyncMapResult' representing the full computation. Instead, it consumes iterables as they come, submitting up to `max_outstanding` tasks to the cluster before waiting on results (default: one task per engine). This allows it to work with infinite generators, and avoid potentially expensive read-ahead for large streams of inputs that may not fit in memory all at once. .. versionadded:: 7.0 Parameters ---------- f : callable function to be mapped *sequences : one or more sequences of matching length the sequences to be distributed and passed to `f` ordered : bool [default True] Whether the results should be yielded on a first-come-first-yield basis, or preserve the order of submission. max_outstanding : int [default len(engines)] The maximum number of tasks to be outstanding. max_outstanding=0 will greedily consume the whole generator (map_async may be more efficient). A limit of 1 should be strictly worse than running a local map, as there will be no parallelism. Use this to tune how greedily input generator should be consumed. return_exceptions : bool [default False] Return Exceptions instead of raising them. Returns ------- lazily-evaluated generator, yielding results of `f` on each item of sequences. Yield-order depends on `ordered` argument. """ assert len(sequences) > 0, "must have some sequences to map onto!" if max_outstanding == 'auto': max_outstanding = len(self) pf = PrePickled(f) if ordered: outstanding = deque() def wait_for_ready(): ar = outstanding.popleft() return [ar] def should_yield(): # ordered: yield first result if it's ready if outstanding[0].ready(): return True if max_outstanding == 0: # no limit return False # or if we've reached capacity (only counting still-outstanding computations) # not counting locally available, but not yet yielded results # TODO: should we limit the local? # if consumers are much slower than producers, # this can fill up local memory return sum(not ar.ready() for ar in outstanding) >= max_outstanding else: outstanding = [] def wait_for_ready(): # unordered, yield whatever finishes first, as soon as it's ready done, outstanding[:] = concurrent.futures.wait( outstanding, return_when=concurrent.futures.FIRST_COMPLETED ) return done def should_yield(): # unordered, we are ready to yield if any result is ready if any(ar.ready() for ar in outstanding): return True if max_outstanding == 0: # no limit return False # or wait if we are full if len(outstanding) >= max_outstanding: return True return False # zip is a lazy iterator for args in zip(*sequences): # submit one work item ar = self.apply_async(pf, *args) outstanding.append(ar) # count 'pending' tasks # yield first result if it's ready # *or* the number of outstanding tasks has reached our limit # yielding immediately means if should_yield(): for ready_ar in wait_for_ready(): yield ready_ar.get(return_exceptions=return_exceptions) # we've filled the buffer, wait for at least one result before continuing if len(outstanding) == max_outstanding: for ready_ar in wait_for_ready(): yield ready_ar.get(return_exceptions=return_exceptions) # yield any remaining results if ordered: for ar in outstanding: yield ar.get(return_exceptions=return_exceptions) else: while outstanding: done, outstanding = concurrent.futures.wait(outstanding) for ar in done: yield ar.get(return_exceptions=return_exceptions) def register_joblib_backend(self, name='ipyparallel', make_default=False): """Register this View as a joblib parallel backend To make this the default backend, set make_default=True. Use with:: p = Parallel(backend='ipyparallel') ... See joblib docs for details Requires joblib >= 0.10 .. versionadded:: 5.1 """ from joblib.parallel import register_parallel_backend from ._joblib import IPythonParallelBackend register_parallel_backend( name, lambda **kwargs: IPythonParallelBackend(view=self, **kwargs), make_default=make_default, ) class ViewExecutor(concurrent.futures.Executor): """A PEP-3148 Executor API for Views Access as view.executor """ def __init__(self, view): self.view = view self._max_workers = len(self.view) def submit(self, fn, *args, **kwargs): """Same as View.apply_async""" return self.view.apply_async(fn, *args, **kwargs) def map(self, func, *iterables, **kwargs): """Return generator for View.map_async""" if 'timeout' in kwargs: warnings.warn("timeout unsupported in ViewExecutor.map") kwargs.pop('timeout') for r in self.view.imap(func, *iterables, **kwargs): yield r def shutdown(self, wait=True): """ViewExecutor does *not* shutdown engines results are awaited if wait=True, but engines are *not* shutdown. """ if wait: self.view.wait() __all__ = ['LoadBalancedView', 'DirectView', 'ViewExecutor', 'BroadcastView'] ipyparallel-7.1.0/ipyparallel/cluster/000077500000000000000000000000001412533144600200375ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/cluster/__init__.py000066400000000000000000000000371412533144600221500ustar00rootroot00000000000000from .cluster import * # noqa ipyparallel-7.1.0/ipyparallel/cluster/__main__.py000066400000000000000000000001011412533144600221210ustar00rootroot00000000000000if __name__ == '__main__': from .app import main main() ipyparallel-7.1.0/ipyparallel/cluster/_winhpcjob.py000066400000000000000000000247001412533144600225360ustar00rootroot00000000000000# encoding: utf-8 """ Job and task components for writing .xml files that the Windows HPC Server 2008 can use to start jobs. """ import os import re import uuid from xml.etree import ElementTree as ET from traitlets import Bool from traitlets import Enum from traitlets import Instance from traitlets import Integer from traitlets import List from traitlets import Unicode from traitlets.config.configurable import Configurable # ----------------------------------------------------------------------------- # Job and Task classes # ----------------------------------------------------------------------------- def as_str(value): if isinstance(value, str): return value elif isinstance(value, bool): if value: return 'true' else: return 'false' elif isinstance(value, (int, float)): return repr(value) else: return value def indent(elem, level=0): i = "\n" + level * " " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: indent(elem, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i def find_username(): domain = os.environ.get('USERDOMAIN') username = os.environ.get('USERNAME', '') if domain is None: return username else: return '%s\\%s' % (domain, username) class WinHPCJob(Configurable): job_id = Unicode('') job_name = Unicode('MyJob', config=True) min_cores = Integer(1, config=True) max_cores = Integer(1, config=True) min_sockets = Integer(1, config=True) max_sockets = Integer(1, config=True) min_nodes = Integer(1, config=True) max_nodes = Integer(1, config=True) unit_type = Unicode("Core", config=True) auto_calculate_min = Bool(True, config=True) auto_calculate_max = Bool(True, config=True) run_until_canceled = Bool(False, config=True) is_exclusive = Bool(False, config=True) username = Unicode(find_username(), config=True) job_type = Unicode('Batch', config=True) priority = Enum( ('Lowest', 'BelowNormal', 'Normal', 'AboveNormal', 'Highest'), default_value='Highest', config=True, ) requested_nodes = Unicode('', config=True) project = Unicode('IPython', config=True) xmlns = Unicode('http://schemas.microsoft.com/HPCS2008/scheduler/') version = Unicode("2.000") tasks = List([]) @property def owner(self): return self.username def _write_attr(self, root, attr, key): s = as_str(getattr(self, attr, '')) if s: root.set(key, s) def as_element(self): # We have to add _A_ type things to get the right order than # the MSFT XML parser expects. root = ET.Element('Job') self._write_attr(root, 'version', '_A_Version') self._write_attr(root, 'job_name', '_B_Name') self._write_attr(root, 'unit_type', '_C_UnitType') self._write_attr(root, 'min_cores', '_D_MinCores') self._write_attr(root, 'max_cores', '_E_MaxCores') self._write_attr(root, 'min_sockets', '_F_MinSockets') self._write_attr(root, 'max_sockets', '_G_MaxSockets') self._write_attr(root, 'min_nodes', '_H_MinNodes') self._write_attr(root, 'max_nodes', '_I_MaxNodes') self._write_attr(root, 'run_until_canceled', '_J_RunUntilCanceled') self._write_attr(root, 'is_exclusive', '_K_IsExclusive') self._write_attr(root, 'username', '_L_UserName') self._write_attr(root, 'job_type', '_M_JobType') self._write_attr(root, 'priority', '_N_Priority') self._write_attr(root, 'requested_nodes', '_O_RequestedNodes') self._write_attr(root, 'auto_calculate_max', '_P_AutoCalculateMax') self._write_attr(root, 'auto_calculate_min', '_Q_AutoCalculateMin') self._write_attr(root, 'project', '_R_Project') self._write_attr(root, 'owner', '_S_Owner') self._write_attr(root, 'xmlns', '_T_xmlns') dependencies = ET.SubElement(root, "Dependencies") etasks = ET.SubElement(root, "Tasks") for t in self.tasks: etasks.append(t.as_element()) return root def tostring(self): """Return the string representation of the job description XML.""" root = self.as_element() indent(root) txt = ET.tostring(root, encoding="utf-8").decode('utf-8') # Now remove the tokens used to order the attributes. txt = re.sub(r'_[A-Z]_', '', txt) txt = '\n' + txt return txt def write(self, filename): """Write the XML job description to a file.""" txt = self.tostring() with open(filename, 'w') as f: f.write(txt) def add_task(self, task): """Add a task to the job. Parameters ---------- task : :class:`WinHPCTask` The task object to add. """ self.tasks.append(task) class WinHPCTask(Configurable): task_id = Unicode('') task_name = Unicode('') version = Unicode("2.000") min_cores = Integer(1, config=True) max_cores = Integer(1, config=True) min_sockets = Integer(1, config=True) max_sockets = Integer(1, config=True) min_nodes = Integer(1, config=True) max_nodes = Integer(1, config=True) unit_type = Unicode("Core", config=True) command_line = Unicode('', config=True) work_directory = Unicode('', config=True) is_rerunnaable = Bool(True, config=True) std_out_file_path = Unicode('', config=True) std_err_file_path = Unicode('', config=True) is_parametric = Bool(False, config=True) environment_variables = Instance(dict, args=(), config=True) def _write_attr(self, root, attr, key): s = as_str(getattr(self, attr, '')) if s: root.set(key, s) def as_element(self): root = ET.Element('Task') self._write_attr(root, 'version', '_A_Version') self._write_attr(root, 'task_name', '_B_Name') self._write_attr(root, 'min_cores', '_C_MinCores') self._write_attr(root, 'max_cores', '_D_MaxCores') self._write_attr(root, 'min_sockets', '_E_MinSockets') self._write_attr(root, 'max_sockets', '_F_MaxSockets') self._write_attr(root, 'min_nodes', '_G_MinNodes') self._write_attr(root, 'max_nodes', '_H_MaxNodes') self._write_attr(root, 'command_line', '_I_CommandLine') self._write_attr(root, 'work_directory', '_J_WorkDirectory') self._write_attr(root, 'is_rerunnaable', '_K_IsRerunnable') self._write_attr(root, 'std_out_file_path', '_L_StdOutFilePath') self._write_attr(root, 'std_err_file_path', '_M_StdErrFilePath') self._write_attr(root, 'is_parametric', '_N_IsParametric') self._write_attr(root, 'unit_type', '_O_UnitType') root.append(self.get_env_vars()) return root def get_env_vars(self): env_vars = ET.Element('EnvironmentVariables') for k, v in self.environment_variables.items(): variable = ET.SubElement(env_vars, "Variable") name = ET.SubElement(variable, "Name") name.text = k value = ET.SubElement(variable, "Value") value.text = v return env_vars # By declaring these, we can configure the controller and engine separately! class IPControllerJob(WinHPCJob): job_name = Unicode('IPController', config=False) is_exclusive = Bool(False, config=True) username = Unicode(find_username(), config=True) priority = Enum( ('Lowest', 'BelowNormal', 'Normal', 'AboveNormal', 'Highest'), default_value='Highest', config=True, ) requested_nodes = Unicode('', config=True) project = Unicode('IPython', config=True) class IPEngineSetJob(WinHPCJob): job_name = Unicode('IPEngineSet', config=False) is_exclusive = Bool(False, config=True) username = Unicode(find_username(), config=True) priority = Enum( ('Lowest', 'BelowNormal', 'Normal', 'AboveNormal', 'Highest'), default_value='Highest', config=True, ) requested_nodes = Unicode('', config=True) project = Unicode('IPython', config=True) class IPControllerTask(WinHPCTask): task_name = Unicode('IPController', config=True) controller_cmd = List(['ipcontroller.exe'], config=True) controller_args = List(['--log-level=40'], config=True) # I don't want these to be configurable std_out_file_path = Unicode('', config=False) std_err_file_path = Unicode('', config=False) min_cores = Integer(1, config=False) max_cores = Integer(1, config=False) min_sockets = Integer(1, config=False) max_sockets = Integer(1, config=False) min_nodes = Integer(1, config=False) max_nodes = Integer(1, config=False) unit_type = Unicode("Core", config=False) work_directory = Unicode('', config=False) def __init__(self, **kwargs): super(IPControllerTask, self).__init__(**kwargs) the_uuid = uuid.uuid1() self.std_out_file_path = os.path.join('log', 'ipcontroller-%s.out' % the_uuid) self.std_err_file_path = os.path.join('log', 'ipcontroller-%s.err' % the_uuid) @property def command_line(self): return ' '.join(self.controller_cmd + self.controller_args) class IPEngineTask(WinHPCTask): task_name = Unicode('IPEngine', config=True) engine_cmd = List(['ipengine.exe'], config=True) engine_args = List(['--log-level=40'], config=True) # I don't want these to be configurable std_out_file_path = Unicode('', config=False) std_err_file_path = Unicode('', config=False) min_cores = Integer(1, config=False) max_cores = Integer(1, config=False) min_sockets = Integer(1, config=False) max_sockets = Integer(1, config=False) min_nodes = Integer(1, config=False) max_nodes = Integer(1, config=False) unit_type = Unicode("Core", config=False) work_directory = Unicode('', config=False) def __init__(self, **kwargs): super(IPEngineTask, self).__init__(**kwargs) the_uuid = uuid.uuid1() self.std_out_file_path = os.path.join('log', 'ipengine-%s.out' % the_uuid) self.std_err_file_path = os.path.join('log', 'ipengine-%s.err' % the_uuid) @property def command_line(self): return ' '.join(self.engine_cmd + self.engine_args) ipyparallel-7.1.0/ipyparallel/cluster/app.py000077500000000000000000000570031412533144600212010ustar00rootroot00000000000000#!/usr/bin/env python # encoding: utf-8 """The ipcluster application.""" from __future__ import print_function import asyncio import errno import json import logging import os import re import signal import sys from functools import partial import entrypoints import zmq from IPython.core.profiledir import ProfileDir from traitlets import Bool from traitlets import CaselessStrEnum from traitlets import default from traitlets import Dict from traitlets import Integer from traitlets import List from traitlets.config.application import catch_config_error from ipyparallel._version import __version__ from ipyparallel.apps.baseapp import base_aliases from ipyparallel.apps.baseapp import base_flags from ipyparallel.apps.baseapp import BaseParallelApplication from ipyparallel.cluster import clean_cluster_files from ipyparallel.cluster import Cluster from ipyparallel.cluster import ClusterManager from ipyparallel.util import abbreviate_profile_dir # ----------------------------------------------------------------------------- # Module level variables # ----------------------------------------------------------------------------- _description = """Start an IPython cluster for parallel computing. An IPython cluster consists of 1 controller and 1 or more engines. This command automates the startup of these processes using a wide range of startup methods (SSH, local processes, PBS, mpiexec, SGE, LSF, HTCondor, Slurm, Windows HPC Server 2008). To start a cluster with 4 engines on your local host simply do 'ipcluster start --n=4'. For more complex usage you will typically do 'ipython profile create mycluster --parallel', then edit configuration files, followed by 'ipcluster start --profile=mycluster --n=4'. """ _main_examples = """ ipcluster start --n=4 # start a 4 node cluster on localhost ipcluster start -h # show the help string for the start subcmd ipcluster stop -h # show the help string for the stop subcmd ipcluster engines -h # show the help string for the engines subcmd """ _start_examples = """ ipython profile create mycluster --parallel # create mycluster profile ipcluster start --profile=mycluster --n=4 # start mycluster with 4 nodes # args to `ipcluster start` after `--` are passed through to the controller # see `ipcontroller -h` for options ipcluster start -- --sqlitedb """ _stop_examples = """ ipcluster stop --profile=mycluster # stop a running cluster by profile name """ _engines_examples = """ ipcluster engines --profile=mycluster --n=4 # start 4 engines only """ # Exit codes for ipcluster # This will be the exit code if the ipcluster appears to be running because # a .pid file exists ALREADY_STARTED = 10 # This will be the exit code if ipcluster stop is run, but there is not .pid # file to be found. ALREADY_STOPPED = 11 # This will be the exit code if ipcluster engines is run, but there is not .pid # file to be found. NO_CLUSTER = 12 # ----------------------------------------------------------------------------- # Main application # ----------------------------------------------------------------------------- start_help = """Start an IPython cluster for parallel computing Start an ipython cluster by its profile name or cluster directory. Cluster directories contain configuration, log and security related files and are named using the convention 'profile_' and should be creating using the 'start' subcommand of 'ipcluster'. If your cluster directory is in the cwd or the ipython directory, you can simply refer to it using its profile name, 'ipcluster start --n=4 --profile=`, otherwise use the 'profile-dir' option. """ stop_help = """Stop a running IPython cluster Stop a running ipython cluster by its profile name or cluster directory. Cluster directories are named using the convention 'profile_'. If your cluster directory is in the cwd or the ipython directory, you can simply refer to it using its profile name, 'ipcluster stop --profile=`, otherwise use the '--profile-dir' option. """ engines_help = """Start engines connected to an existing IPython cluster Start one or more engines to connect to an existing Cluster by profile name or cluster directory. Cluster directories contain configuration, log and security related files and are named using the convention 'profile_' and should be creating using the 'start' subcommand of 'ipcluster'. If your cluster directory is in the cwd or the ipython directory, you can simply refer to it using its profile name, 'ipcluster engines --n=4 --profile=`, otherwise use the 'profile-dir' option. """ stop_aliases = dict( signal='IPClusterStop.signal', ) stop_aliases.update(base_aliases) stop_flags = dict( all=( {'IPClusterStop': {'all_cluster_ids': True}}, "stop all clusters for the selected profile, instead of just one", ) ) stop_flags.update(base_flags) class IPClusterStop(BaseParallelApplication): name = u'ipcluster' description = stop_help examples = _stop_examples signal = Integer( signal.SIGINT, config=True, help="signal to use for stopping processes." ) all_cluster_ids = Bool( False, config=True, help="stop all clusters for the selected profile, instead of just one", ) aliases = stop_aliases flags = stop_flags def start(self): """Start the app for the stop subcommand.""" if self.all_cluster_ids: clusters = list( ClusterManager(parent=self) .load_clusters(profile_dir=self.profile_dir.location) .values() ) else: if self.extra_args: cluster_ids = self.extra_args else: cluster_ids = [self.cluster_id] clusters = [] for cluster_id in cluster_ids: try: cluster = Cluster.from_file( profile_dir=self.profile_dir.location, cluster_id=cluster_id, parent=self, ) except FileNotFoundError as s: self.log.critical(f"Could not find cluster file {s}") self.exit(ALREADY_STOPPED) else: clusters.append(cluster) if not clusters: self.log.info("No clusters to stop") self.exit(0) async def _stop_all(): tasks = [] for cluster in clusters: self.log.info(f"Stopping cluster {cluster.cluster_id}") tasks.append(cluster.stop_cluster()) await asyncio.gather(*tasks) asyncio.get_event_loop().run_until_complete(_stop_all()) list_aliases = {} list_aliases.update(base_aliases) list_aliases.update({"o": "IPClusterList.output_format"}) class IPClusterList(BaseParallelApplication): name = 'ipcluster' description = "List available clusters" aliases = list_aliases output_format = CaselessStrEnum( ["text", "json"], default_value="text", config=True, help="Output format" ) def start(self): profile_dirs = None if ( self.profile != "default" or "ProfileDir" in self.config and "location" in self.config.ProfileDir ): # profile-directory specified, only consider profile_dirs = [self.profile_dir.location] cluster_manager = ClusterManager(parent=self) clusters = cluster_manager.load_clusters(profile_dirs=profile_dirs) if self.output_format == "text": # TODO: measure needed profile/cluster id width print( f"{'PROFILE':16} {'CLUSTER ID':32} {'RUNNING':7} {'ENGINES':7} {'LAUNCHER'}" ) for cluster in sorted( clusters.values(), key=lambda c: ( c.profile_dir, c.cluster_id, ), ): profile = abbreviate_profile_dir(cluster.profile_dir) cluster_id = cluster.cluster_id running = bool(cluster.controller) # TODO: URL? engines = 0 if cluster.engines: engines = sum( engine_set.n for engine_set in cluster.engines.values() ) launcher = cluster.engine_launcher_class.__name__ if launcher.endswith("EngineSetLauncher"): launcher = launcher[: -len("EngineSetLauncher")] print( f"{profile:16} {cluster_id or repr(''):32} {str(running):7} {engines:7} {launcher}" ) elif self.output_format == "json": json.dump( [cluster.to_dict() for cluster in clusters.values()], sys.stdout, ) else: raise NotImplementedError(f"No such output format: {self.output_format}") clean_flags = {} for key in ('debug', 'quiet'): clean_flags[key] = base_flags[key] clean_flags.update( { "force": ( {"IPClusterClean": {"force": True}}, """ Force removal of files, even if clusters are running. WARNING: can leave orphan processes """, ), "all": ( {"IPClusterClean": {"all_profiles": True}}, "Clean all IPython profiles, not just current.", ), } ) clean_aliases = {} for key in ('log-level', 'ipython-dir', 'profile-dir', 'profile'): clean_aliases[key] = base_aliases[key] class IPClusterClean(BaseParallelApplication): name = 'ipcluster' description = "Cleanup cluster files" flags = clean_flags aliases = clean_aliases force = Bool( False, config=True, help=""" Force removal of cluster files, even if clusters appear to be running. WARNING: this can leave orphan processes. """, ) all_profiles = Bool( False, config=True, help="Clean files in all profiles, instead of just the current profile.", ) def start(self): if self.all_profiles: profile_dirs = None else: profile_dirs = [self.profile_dir.location] clean_cluster_files(profile_dirs, log=self.log, force=self.force) engine_aliases = {} engine_aliases.update(base_aliases) engine_aliases.update( dict( n='Cluster.n', engines='Cluster.engine_launcher_class', daemonize='IPClusterEngines.daemonize', ) ) engine_flags = {} engine_flags.update(base_flags) engine_flags.update( dict( daemonize=( {'IPClusterEngines': {'daemonize': True}}, """run the cluster into the background (not available on Windows)""", ) ) ) class IPClusterEngines(BaseParallelApplication): name = u'ipcluster' description = engines_help examples = _engines_examples usage = None default_log_level = logging.INFO classes = List() @default("classes") def _classes_default(self): launcher_classes = [] for kind in ('controller', 'engine'): group_name = f'ipyparallel.{kind}_launchers' group = entrypoints.get_group_named(group_name) for key, value in group.items(): try: cls = value.load() except Exception as e: self.log.error( f"Failed to load entrypoint {group_name}: {key} = {value}\n{e}" ) else: launcher_classes.append(cls) return [ProfileDir, Cluster] + launcher_classes daemonize = Bool( False, config=True, help="""Launch the cluster and immediately exit. .. versionchanged:: 7.0 No longer leaves the ipcluster process itself running. Prior to 7.0, --daemonize did not work on Windows. """, ) early_shutdown = Integer( 30, config=True, help="If engines stop in this time frame, assume something is wrong and tear down the cluster.", ) _stopping = False aliases = Dict(engine_aliases) flags = Dict(engine_flags) @catch_config_error def initialize(self, argv=None): super(IPClusterEngines, self).initialize(argv) self.init_signal() self.init_cluster() def init_deprecated_config(self): super().init_deprecated_config() cluster_config = self.config.Cluster for clsname in ['IPClusterStart', 'IPClusterEngines']: if clsname not in self.config: continue cls_config = self.config[clsname] for traitname in [ 'delay', 'engine_launcher_class', 'controller_launcher_class', 'controller_ip', 'controller_location', 'n', ]: if traitname in cls_config and traitname not in cluster_config: value = cls_config[traitname] self.log.warning( f"{clsname}.{traitname} = {value} configuration is deprecated in ipyparallel 7. Use Cluster.{traitname} = {value}" ) cluster_config[traitname] = value cls_config.pop(traitname) def init_cluster(self): self.cluster = Cluster( parent=self, profile_dir=self.profile_dir.location, cluster_id=self.cluster_id, controller_args=self.extra_args, shutdown_atexit=not self.daemonize, ) def init_signal(self): # Setup signals for signame in ("SIGUSR1", "SIGUSR2", "SIGINFO"): try: signum = getattr(signal, signame) except AttributeError: self.log.debug(f"Not forwarding {signame}") pass else: self.log.debug(f"Forwarding {signame} to engines") signal.signal(signum, self.relay_signal) signal.signal(signal.SIGINT, self.sigint_handler) signal.signal(signal.SIGTERM, self.sigint_handler) def relay_signal(self, signum, frame): self.log.debug(f"Received signal {signum} received, relaying to engines...") self.loop.add_callback_from_signal(partial(self.cluster.signal_engines, signum)) def sigint_handler(self, signum, frame): return self.relay_signal(signum, frame) def sigterm_handler(self, signum, frame): self.log.debug(f"Received signal {signum} received, stopping launchers...") self.loop.add_callback_from_signal(self.stop_cluster) def engines_started_ok(self): self.log.info("Engines appear to have started successfully") self.early_shutdown = 0 async def start_engines(self): try: await self.cluster.start_engines() except: self.log.exception("Engine start failed") self.exit(1) if self.daemonize: self.loop.add_callback(self.loop.stop) return self.watch_engines() def watch_engines(self): """Watch for early engine shutdown""" # FIXME: public API to get launcher instances? self.engine_launcher = next(iter(self.cluster.engines.values())) if not self.early_shutdown: self.engine_launcher.on_stop(self.engines_stopped) return # TODO: enable 'engines stopped early' with new cluster API self.engine_launcher.on_stop(self.engines_stopped_early) if self.early_shutdown: self.loop.add_timeout( self.loop.time() + self.early_shutdown, self.engines_started_ok ) def engines_stopped_early(self, stop_data): if self.early_shutdown and not self._stopping: self.log.error( """ Engines shutdown early, they probably failed to connect. Check the engine log files for output. If your controller and engines are not on the same machine, you probably have to instruct the controller to listen on an interface other than localhost. You can set this by adding "--ip=*" to your ControllerLauncher.controller_args. Be sure to read our security docs before instructing your controller to listen on a public interface. """ ) engine_output = self.engine_launcher.get_output(remove=True) if engine_output: self.log.error(f"Engine output:\n{engine_output}") self.loop.add_callback(self.stop_cluster) return self.engines_stopped(stop_data) def engines_stopped(self, r): return self.loop.stop() async def stop_cluster(self, r=None): if not self._stopping: self._stopping = True self.log.error("IPython cluster: stopping") await self.cluster.stop_cluster() self.loop.add_callback(self.loop.stop) def start_logging(self): # Remove old log files of the controller and engine if self.clean_logs: log_dir = self.profile_dir.log_dir for f in os.listdir(log_dir): if re.match(r'ip(engine|controller)-.+\.(log|err|out)', f): os.remove(os.path.join(log_dir, f)) def start(self): """Start the app for the engines subcommand.""" self.log.info(f"IPython cluster start: {self.cluster_id}") # First see if the cluster is already running # Now log and daemonize self.log.info('Starting engines with [daemon=%r]' % self.daemonize) self.loop.add_callback(self.start_engines) # Now write the new pid file AFTER our new forked pid is active. # self.write_pid_file() try: self.loop.start() except KeyboardInterrupt: pass except zmq.ZMQError as e: if e.errno == errno.EINTR: pass else: raise start_aliases = {} start_aliases.update(engine_aliases) start_aliases.update( dict( delay='Cluster.delay', controller='Cluster.controller_launcher_class', ip='Cluster.controller_ip', location='Cluster.controller_location', ) ) start_aliases['clean-logs'] = 'IPClusterStart.clean_logs' class IPClusterStart(IPClusterEngines): name = u'ipcluster' description = start_help examples = _start_examples default_log_level = logging.INFO auto_create = Bool( True, config=True, help="whether to create the profile_dir if it doesn't exist" ) clean_logs = Bool( True, config=True, help="whether to cleanup old logs before starting" ) # flags = Dict(flags) aliases = Dict(start_aliases) def engines_stopped(self, r): """prevent parent.engines_stopped from stopping everything on engine shutdown""" pass async def start_cluster(self): await self.cluster.start_cluster() if self.daemonize: print( f"Leaving cluster running: {self.cluster.cluster_file}", file=sys.stderr ) self.loop.add_callback(self.loop.stop) self.cluster.controller.on_stop(self.controller_stopped) self.watch_engines() def controller_stopped(self, stop_data): if not self._stopping: self.log.warning("Controller stopped. Shutting down.") self.loop.add_callback(self.stop_cluster) def sigint_handler(self, signum, frame): """Unlike engines, SIGINT shuts down `ipcluster start`""" self.log.debug(f"Received signal {signum} received, stopping launchers...") self.loop.add_callback_from_signal(self.stop_cluster) def start(self): """Start the app for the start subcommand.""" # First see if the cluster is already running cluster_file = self.cluster.cluster_file if os.path.isfile(cluster_file): try: cluster = Cluster.from_file(cluster_file) except Exception as e: # TODO: define special ClusterNotRunning exception to handle here self.log.error( f"Error loading cluster from file {cluster_file}: {e}. Assuming stopped cluster." ) else: self.log.critical( f'Cluster is already running at {self.cluster.cluster_file}. ' 'use `ipcluster stop` to stop the cluster.' ) # Here I exit with a unusual exit status that other processes # can watch for to learn how I existed. self.exit(ALREADY_STARTED) # Now log and daemonize self.log.info('Starting ipcluster with [daemonize=%r]' % self.daemonize) self.loop.add_callback(self.start_cluster) try: self.loop.start() except KeyboardInterrupt: pass except zmq.ZMQError as e: if e.errno == errno.EINTR: pass else: raise finally: if not self.daemonize: self.cluster.stop_cluster_sync() class IPClusterNBExtension(BaseParallelApplication): """Enable/disable ipcluster tab extension in Jupyter notebook""" name = 'ipcluster-nbextension' description = """Enable/disable IPython clusters tab in Jupyter notebook for Jupyter Notebook >= 4.2, you can use the new nbextension API: jupyter serverextension enable --py ipyparallel jupyter nbextension install --py ipyparallel jupyter nbextension enable --py ipyparallel """ examples = """ ipcluster nbextension enable ipcluster nbextension disable """ version = __version__ user = Bool(False, help="Apply the operation only for the given user").tag( config=True ) flags = Dict( { 'user': ( {'IPClusterNBExtension': {'user': True}}, 'Apply the operation only for the given user', ) } ) def start(self): from ipyparallel.nbextension.install import install_extensions if len(self.extra_args) != 1: self.exit("Must specify 'enable' or 'disable'") action = self.extra_args[0].lower() if action == 'enable': print("Enabling IPython clusters tab") install_extensions(enable=True, user=self.user) elif action == 'disable': print("Disabling IPython clusters tab") install_extensions(enable=False, user=self.user) else: self.exit("Must specify 'enable' or 'disable', not '%s'" % action) class IPCluster(BaseParallelApplication): name = u'ipcluster' description = _description examples = _main_examples version = __version__ _deprecated_classes = ["IPClusterApp"] subcommands = { 'start': (IPClusterStart, start_help), 'stop': (IPClusterStop, stop_help), 'engines': (IPClusterEngines, engines_help), 'list': (IPClusterList, IPClusterList.description), 'clean': (IPClusterClean, IPClusterClean.description), 'nbextension': (IPClusterNBExtension, IPClusterNBExtension.description), } # no aliases or flags for parent App aliases = Dict() flags = Dict() def start(self): if self.subapp is None: keys = ', '.join("'{}'".format(key) for key in self.subcommands.keys()) print("No subcommand specified. Must specify one of: %s" % keys) print() self.print_description() self.print_subcommands() self.exit(1) else: return self.subapp.start() main = IPCluster.launch_instance if __name__ == '__main__': main() ipyparallel-7.1.0/ipyparallel/cluster/cluster.py000066400000000000000000001013121412533144600220700ustar00rootroot00000000000000"""Cluster class defines the basic interface to a single IPython Parallel cluster starts/stops/polls controllers, engines, etc. """ import asyncio import atexit import glob import inspect import json import logging import os import random import string import sys import time import traceback from functools import partial from multiprocessing import cpu_count from weakref import WeakSet import IPython from traitlets import Any from traitlets import Bool from traitlets import default from traitlets import Dict from traitlets import Float from traitlets import import_item from traitlets import Instance from traitlets import Integer from traitlets import List from traitlets import Unicode from traitlets import validate from traitlets.config import Application from traitlets.config import Config from traitlets.config import LoggingConfigurable from . import launcher from .._async import AsyncFirst from ..traitlets import Launcher from ..util import _all_profile_dirs from ..util import _default_profile_dir from ..util import _locate_profiles from ..util import _traitlet_signature from ..util import abbreviate_profile_dir _suffix_chars = string.ascii_lowercase + string.digits # weak set of clusters to be cleaned up at exit _atexit_clusters = WeakSet() def _atexit_cleanup_clusters(*args): """Cleanup clusters during process shutdown""" for cluster in _atexit_clusters: if not cluster.shutdown_atexit: # overridden after register continue if cluster.controller or cluster.engines: print(f"Stopping cluster {cluster}", file=sys.stderr) try: cluster.stop_cluster_sync() except Exception: print(f"Error stopping cluster {cluster}", file=sys.stderr) traceback.print_exception(*sys.exc_info()) _atexit_cleanup_clusters.registered = False @_traitlet_signature class Cluster(AsyncFirst, LoggingConfigurable): """Class representing an IPP cluster i.e. one controller and a groups of engines Can start/stop/monitor/poll cluster resources All async methods can be called synchronously with a `_sync` suffix, e.g. `cluster.start_cluster_sync()` """ # general configuration shutdown_atexit = Bool( True, help=""" Shutdown the cluster at process exit. Set to False if you want to launch a cluster and leave it running after the launching process exits. """, ) cluster_id = Unicode(help="The id of the cluster (default: random string)").tag( to_dict=True ) @default("cluster_id") def _default_cluster_id(self): return f"{int(time.time())}-{''.join(random.choice(_suffix_chars) for i in range(4))}" profile_dir = Unicode( help="""The profile directory. Default priority: - specified explicitly - current IPython session - use profile name (default: 'default') """ ).tag(to_dict=True) @default("profile_dir") def _default_profile_dir(self): return _default_profile_dir(profile=self.profile) @validate("profile_dir") def _validate_profile_dir(self, proposal): path = proposal.value if path: return os.path.abspath(path) return path profile = Unicode( "", help="""The profile name, a shortcut for specifying profile_dir within $IPYTHONDIR.""", ) cluster_file = Unicode( help="The path to the cluster file for saving this cluster to disk" ) @default("cluster_file") def _default_cluster_file(self): return os.path.join( self.profile_dir, "security", f"cluster-{self.cluster_id}.json" ) engine_timeout = Integer( 60, help="""Timeout to use when waiting for engines to register before giving up. """, config=True, ) controller_launcher_class = Launcher( default_value=launcher.LocalControllerLauncher, entry_point_group='ipyparallel.controller_launchers', help="""The class for launching a Controller. Change this value if you want your controller to also be launched by a batch system, such as PBS,SGE,MPI,etc. Each launcher class has its own set of configuration options, for making sure it will work in your environment. Note that using a batch launcher for the controller *does not* put it in the same batch job as the engines, so they will still start separately. Third-party engine launchers can be registered via `ipyparallel.engine_launchers` entry point. They can be selected via case-insensitive abbreviation, e.g. c.Cluster.controller_launcher_class = 'SSH' or: ipcluster start --controller=MPI """, config=True, ) engine_launcher_class = Launcher( default_value=launcher.LocalEngineSetLauncher, entry_point_group='ipyparallel.engine_launchers', help="""The class for launching a set of Engines. Change this value to use various batch systems to launch your engines, such as PBS,SGE,MPI,etc. Each launcher class has its own set of configuration options, for making sure it will work in your environment. Third-party engine launchers can be registered via `ipyparallel.engine_launchers` entry point. They can be selected via case-insensitive abbreviation, e.g. c.Cluster.engine_launcher_class = 'ssh' or: ipcluster start --engines=mpi """, config=True, ) # controller configuration controller_args = List( Unicode(), config=True, help="Additional CLI args to pass to the controller.", ).tag(to_dict=True) controller_ip = Unicode( config=True, help="Set the IP address of the controller." ).tag(to_dict=True) controller_location = Unicode( config=True, help="""Set the location (hostname or ip) of the controller. This is used by engines and clients to locate the controller when the controller listens on all interfaces """, ).tag(to_dict=True) # engine configuration delay = Float( 1.0, config=True, help="delay (in s) between starting the controller and the engines", ).tag(to_dict=True) n = Integer( None, allow_none=True, config=True, help="The number of engines to start" ).tag(to_dict=True) @default("parent") def _default_parent(self): """Default to inheriting config from current IPython session""" return IPython.get_ipython() log_level = Integer(logging.INFO) @default("log") def _default_log(self): if self.parent and self.parent is IPython.get_ipython(): # log to stdout in an IPython session log = logging.getLogger(f"{__name__}.{self.cluster_id}") log.setLevel(self.log_level) handler = logging.StreamHandler(sys.stdout) log.handlers = [handler] return log elif self.parent and getattr(self.parent, 'log', None) is not None: return self.parent.log elif Application.initialized(): return Application.instance().log else: # set up our own logger log = logging.getLogger(f"{__name__}.{self.cluster_id}") log.setLevel(self.log_level) return log load_profile = Bool( True, config=True, help=""" If True (default) load ipcluster config from profile directory, if present. """, ) # private state controller = Any() engines = Dict() profile_config = Instance(Config, allow_none=False) @default("profile_config") def _profile_config_default(self): """Load config from our profile""" if not self.load_profile or not os.path.isdir(self.profile_dir): # no profile dir, nothing to load return Config() from .app import BaseParallelApplication, IPClusterStart # look up if we are descended from an 'ipcluster' app # avoids repeated load of the current profile dir parents = [] parent = self.parent while parent is not None: parents.append(parent) parent = parent.parent app_parents = list( filter(lambda p: isinstance(p, BaseParallelApplication), parents) ) if app_parents: app_parent = app_parents[0] else: app_parent = None if ( app_parent and app_parent.name == 'ipcluster' and app_parent.profile_dir.location == self.profile_dir ): # profile config already loaded by parent, nothing new to load return Config() self.log.debug(f"Loading profile {self.profile_dir}") # set profile dir via config config = Config() config.ProfileDir.location = self.profile_dir # load profile config via IPCluster app = IPClusterStart(config=config, log=self.log) # adds profile dir to config_files_path app.init_profile_dir() # adds system to config_files_path app.init_config_files() # actually load the config app.load_config_file(suppress_errors=False) return app.config @validate("config") def _merge_profile_config(self, proposal): direct_config = proposal.value if not self.load_profile: return direct_config profile_config = self.profile_config if not profile_config: return direct_config # priority ?! direct > profile config = Config() if profile_config: config.merge(profile_config) config.merge(direct_config) return config def __init__(self, **kwargs): """Construct a Cluster""" if 'parent' not in kwargs and 'config' not in kwargs: kwargs['parent'] = self._default_parent() super().__init__(**kwargs) def __del__(self): if not self.shutdown_atexit: return if self.controller or self.engines: self.stop_cluster_sync() def __repr__(self): fields = { "cluster_id": repr(self.cluster_id), } profile_dir = self.profile_dir profile_prefix = os.path.join(IPython.paths.get_ipython_dir(), "profile_") if profile_dir.startswith(profile_prefix): fields["profile"] = repr(profile_dir[len(profile_prefix) :]) else: home_dir = os.path.expanduser("~") if profile_dir.startswith(home_dir + os.path.sep): # truncate $HOME/. -> ~/... profile_dir = "~" + profile_dir[len(home_dir) :] fields["profile_dir"] = repr(profile_dir) if self.controller: fields["controller"] = f"<{self.controller.state}>" if self.engines: fields["engine_sets"] = list(self.engines) fields_str = ', '.join(f"{key}={value}" for key, value in fields.items()) return f"<{self.__class__.__name__}({fields_str})>" def to_dict(self): """Serialize a Cluster object for later reconstruction""" cluster_info = {} d = {"cluster": cluster_info} for attr in self.traits(to_dict=True): cluster_info[attr] = getattr(self, attr) def _cls_str(cls): return f"{cls.__module__}.{cls.__name__}" cluster_info["class"] = _cls_str(self.__class__) if self.controller and self.controller.state != 'after': d["controller"] = { "class": launcher.abbreviate_launcher_class( self.controller_launcher_class ), "state": None, } d["controller"]["state"] = self.controller.to_dict() d["engines"] = { "class": launcher.abbreviate_launcher_class(self.engine_launcher_class), "sets": {}, } sets = d["engines"]["sets"] for engine_set_id, engine_launcher in self.engines.items(): if engine_launcher.state != 'after': sets[engine_set_id] = engine_launcher.to_dict() return d @classmethod def from_dict(cls, d, **kwargs): """Construct a Cluster from serialized state""" cluster_info = d["cluster"] if cluster_info.get("class"): specified_cls = import_item(cluster_info["class"]) if specified_cls is not cls: # specified a custom Cluster class, # dispatch to from_dict from that class return specified_cls.from_dict(d, **kwargs) kwargs.setdefault("shutdown_atexit", False) self = cls(**kwargs) for attr in self.traits(to_dict=True): if attr in cluster_info: setattr(self, attr, cluster_info[attr]) for attr in self.traits(to_dict=True): if attr in d: setattr(self, attr, d[attr]) cluster_key = ClusterManager._cluster_key(self) if d.get("controller"): controller_info = d["controller"] self.controller_launcher_class = controller_info["class"] # after traitlet coercion, which imports strings cls = self.controller_launcher_class if controller_info["state"]: try: self.controller = cls.from_dict( controller_info["state"], parent=self ) except launcher.NotRunning as e: self.log.error(f"Controller for {cluster_key} not running: {e}") else: self.controller.on_stop(self._controller_stopped) engine_info = d.get("engines") if engine_info: self.engine_launcher_class = engine_info["class"] # after traitlet coercion, which imports strings cls = self.engine_launcher_class for engine_set_id, engine_state in engine_info.get("sets", {}).items(): try: self.engines[engine_set_id] = engine_set = cls.from_dict( engine_state, engine_set_id=engine_set_id, parent=self, ) except launcher.NotRunning as e: self.log.error( f"Engine set {cluster_key}{engine_set_id} not running: {e}" ) else: engine_set.on_stop(partial(self._engines_stopped, engine_set_id)) # check if state changed if self.to_dict() != d: # if so, update our cluster file self.update_cluster_file() return self @classmethod def from_file( cls, cluster_file=None, *, profile=None, profile_dir=None, cluster_id='', **kwargs, ): """Load a Cluster object from a file Can specify a full path, or combination of profile, profile_dir, and/or cluster_id. With no arguments given, it will connect to a cluster created with `ipcluster start`. """ if cluster_file is None: # determine cluster_file from profile/profile_dir kwargs['cluster_id'] = cluster_id if profile is not None: kwargs['profile'] = profile if profile_dir is not None: kwargs['profile_dir'] = profile_dir cluster_file = Cluster(**kwargs).cluster_file # ensure from_file preserves cluster_file, even if it moved kwargs.setdefault("cluster_file", cluster_file) with open(cluster_file) as f: return cls.from_dict(json.load(f), **kwargs) def write_cluster_file(self): """Write cluster info to disk for later loading""" os.makedirs(os.path.dirname(self.cluster_file), exist_ok=True) self.log.debug(f"Updating {self.cluster_file}") with open(self.cluster_file, "w") as f: json.dump(self.to_dict(), f) def remove_cluster_file(self): """Remove my cluster file.""" try: os.remove(self.cluster_file) except FileNotFoundError: pass else: self.log.debug(f"Removed cluster file: {self.cluster_file}") def _is_running(self): """Return if we have any running components""" if self.controller and self.controller.state != 'after': return True if any(es.state != 'after' for es in self.engines.values()): return True return False def update_cluster_file(self): """Update my cluster file If cluster_file is disabled, do nothing If cluster is fully stopped, remove the file """ if not self.cluster_file: # setting cluster_file='' disables saving to disk return if not self._is_running(): self.remove_cluster_file() else: self.write_cluster_file() async def start_controller(self, **kwargs): """Start the controller Keyword arguments are passed to the controller launcher constructor """ # start controller # retrieve connection info # webhook? if self.controller is not None: raise RuntimeError( "controller is already running. Call stopcontroller() first." ) if self.shutdown_atexit: _atexit_clusters.add(self) if not _atexit_cleanup_clusters.registered: atexit.register(_atexit_cleanup_clusters) self.controller = controller = self.controller_launcher_class( work_dir=u'.', parent=self, log=self.log, profile_dir=self.profile_dir, cluster_id=self.cluster_id, **kwargs, ) controller_args = getattr(controller, 'controller_args', None) if controller_args is None: def add_args(args): # only some Launchers support modifying controller args self.log.warning( "Not adding controller args %s. " "controller_args passthrough is not supported by %s", args, self.controller_launcher_class.__name__, ) else: add_args = controller_args.extend if self.controller_ip: add_args(['--ip=%s' % self.controller_ip]) if self.controller_location: add_args(['--location=%s' % self.controller_location]) if self.controller_args: add_args(self.controller_args) if controller_args is not None: # ensure we trigger trait observers after we are done self.controller.controller_args = list(controller_args) self.controller.on_stop(self._controller_stopped) r = self.controller.start() if inspect.isawaitable(r): await r self.update_cluster_file() def _controller_stopped(self, stop_data=None): """Callback when a controller stops""" if stop_data and stop_data.get("exit_code"): log = self.log.warning else: log = self.log.info log(f"Controller stopped: {stop_data}") self.update_cluster_file() def _new_engine_set_id(self): """Generate a new engine set id""" engine_set_id = base = f"{int(time.time())}" i = 1 while engine_set_id in self.engines: engine_set_id = f"{base}-{i}" i += 1 return engine_set_id async def start_engines(self, n=None, engine_set_id=None, **kwargs): """Start an engine set Returns an engine set id which can be used in stop_engines """ # TODO: send engines connection info if engine_set_id is None: engine_set_id = self._new_engine_set_id() engine_set = self.engines[engine_set_id] = self.engine_launcher_class( work_dir=u'.', parent=self, log=self.log, profile_dir=self.profile_dir, cluster_id=self.cluster_id, engine_set_id=engine_set_id, **kwargs, ) if n is None: n = self.n n = getattr(engine_set, 'engine_count', n) if n is None: n = cpu_count() self.log.info(f"Starting {n or ''} engines with {self.engine_launcher_class}") r = engine_set.start(n) engine_set.on_stop(partial(self._engines_stopped, engine_set_id)) if inspect.isawaitable(r): await r self.update_cluster_file() return engine_set_id def _engines_stopped(self, engine_set_id, stop_data=None): if stop_data and stop_data.get("exit_code"): log = self.log.warning else: log = self.log.info log(f"engine set stopped {engine_set_id}: {stop_data}") self.update_cluster_file() async def start_and_connect(self, n=None): """Single call to start a cluster and connect a client Equivalent to:: await self.start_cluster(n) client = await self.connect_client() await client.wait_for_engines(n, block=False) .. versionadded: 7.1 """ if n is None: n = self.n await self.start_cluster(n=n) client = await self.connect_client() if n: await asyncio.wrap_future( client.wait_for_engines(n, block=False, timeout=self.engine_timeout) ) return client async def start_cluster(self, n=None): """Start a cluster starts one controller and n engines (default: self.n) .. versionchanged:: 7.1 return self, to allow method chaining """ if self.controller is None: await self.start_controller() if self.delay: await asyncio.sleep(self.delay) await self.start_engines(n) # return self to allow chaining return self async def stop_engines(self, engine_set_id=None): """Stop an engine set If engine_set_id is not given, all engines are stopped. """ if engine_set_id is None: futures = [] for engine_set_id in list(self.engines): futures.append(self.stop_engines(engine_set_id)) if futures: await asyncio.gather(*futures) return self.log.info(f"Stopping engine(s): {engine_set_id}") engine_set = self.engines[engine_set_id] r = engine_set.stop() if inspect.isawaitable(r): await r # retrieve and cleanup output files engine_set.get_output(remove=True) self.engines.pop(engine_set_id) self.update_cluster_file() async def stop_engine(self, engine_id): """Stop one engine *May* stop all engines in a set, depending on EngineSet features (e.g. mpiexec) """ raise NotImplementedError("How do we find an engine by id?") async def restart_engines(self, engine_set_id=None): """Restart an engine set""" if engine_set_id is None: for engine_set_id in list(self.engines): await self.restart_engines(engine_set_id) return engine_set = self.engines[engine_set_id] n = engine_set.n await self.stop_engines(engine_set_id) await self.start_engines(n, engine_set_id) async def restart_engine(self, engine_id): """Restart one engine *May* stop all engines in a set, depending on EngineSet features (e.g. mpiexec) """ raise NotImplementedError("How do we find an engine by id?") async def signal_engine(self, signum, engine_id): """Signal one engine *May* signal all engines in a set, depending on EngineSet features (e.g. mpiexec) """ raise NotImplementedError("How do we find an engine by id?") async def signal_engines(self, signum, engine_set_id=None): """Signal all engines in a set If no engine set is specified, signal all engine sets. """ if engine_set_id is None: for engine_set_id in list(self.engines): await self.signal_engines(signum, engine_set_id) return self.log.info(f"Sending signal {signum} to engine(s) {engine_set_id}") engine_set = self.engines[engine_set_id] r = engine_set.signal(signum) if inspect.isawaitable(r): await r async def stop_controller(self): """Stop the controller""" if self.controller and self.controller.running: self.log.info("Stopping controller") r = self.controller.stop() if inspect.isawaitable(r): await r if self.controller: self.controller.get_output(remove=True) self.controller = None self.update_cluster_file() async def stop_cluster(self): """Stop the controller and all engines""" await asyncio.gather(self.stop_controller(), self.stop_engines()) async def connect_client(self, **client_kwargs): """Return a client connected to the cluster""" # TODO: get connect info directly from controller # this assumes local files exist from ipyparallel import Client connection_info = self.controller.get_connection_info() if inspect.isawaitable(connection_info): connection_info = await connection_info return Client( connection_info['client'], cluster=self, profile_dir=self.profile_dir, cluster_id=self.cluster_id, **client_kwargs, ) # context managers (both async and sync) _context_client = None async def __aenter__(self): client = self._context_client = await self.start_and_connect() return client async def __aexit__(self, *args): if self._context_client is not None: self._context_client.close() self._context_client = None await self.stop_engines() await self.stop_controller() def __enter__(self): client = self._context_client = self.start_and_connect_sync() return client def __exit__(self, *args): if self._context_client: self._context_client.close() self._context_client = None self.stop_engines_sync() self.stop_controller_sync() class ClusterManager(LoggingConfigurable): """A manager of clusters Wraps Cluster, adding lookup/list by cluster id """ clusters = Dict(help="My cluster objects") @staticmethod def _cluster_key(cluster): """Return a unique cluster key for a cluster Default is {profile}:{cluster_id} """ return f"{abbreviate_profile_dir(cluster.profile_dir)}:{cluster.cluster_id}" @staticmethod def _cluster_files_in_profile_dir(profile_dir): """List clusters in a profile directory Returns list of cluster *files* """ return glob.glob(os.path.join(profile_dir, "security", "cluster-*.json")) def load_clusters( self, *, profile_dirs=None, profile_dir=None, profiles=None, profile=None, init_default_clusters=False, **kwargs, ): """Populate a ClusterManager from cluster files on disk Load all cluster objects from the given profile directory(ies). Default is to find clusters in all IPython profiles, but profile directories or profile names can be specified explicitly. If `init_default_clusters` is True, a stopped Cluster object is loaded for every profile dir with cluster_id="" if no running cluster is found. Priority: - profile_dirs list - single profile_dir - profiles list by name - single profile by name - all IPython profiles, if nothing else specified """ # first, check our current clusters for key, cluster in list(self.clusters.items()): # remove stopped clusters # but not *new* clusters that haven't started yet # if `cluster.controller` is present # that means it was running at some point if cluster.controller and not cluster._is_running(): self.log.info(f"Removing stopped cluster {key}") self.clusters.pop(key) if profile_dirs is None: if profile_dir is not None: profile_dirs = [profile_dir] else: if profiles is None: if profile is not None: profiles = [profile] if profiles is not None: profile_dirs = _locate_profiles(profiles) if profile_dirs is None: # totally unspecified, default to all profile_dirs = _all_profile_dirs() by_cluster_file = {c.cluster_file: c for c in self.clusters.values()} for profile_dir in profile_dirs: cluster_files = self._cluster_files_in_profile_dir(profile_dir) # load default cluster for each profile # TODO: only if it has any ipyparallel config files # *or* it's the default profile if init_default_clusters and not cluster_files: cluster = Cluster(profile_dir=profile_dir, cluster_id="") cluster_key = self._cluster_key(cluster) if cluster_key not in self.clusters: self.clusters[cluster_key] = cluster for cluster_file in cluster_files: if cluster_file in by_cluster_file: # already loaded, skip it continue self.log.debug(f"Loading cluster file {cluster_file}") try: cluster = Cluster.from_file(cluster_file, parent=self) except Exception as e: self.log.warning(f"Failed to load cluster from {cluster_file}: {e}") continue else: cluster_key = self._cluster_key(cluster) self.clusters[cluster_key] = cluster return self.clusters def new_cluster(self, **kwargs): """Create a new cluster""" cluster = Cluster(parent=self, **kwargs) cluster_key = self._cluster_key(cluster) if cluster_key in self.clusters: raise KeyError(f"Cluster {cluster_key} already exists!") self.clusters[cluster_key] = cluster return cluster_key, cluster def get_cluster(self, cluster_id): """Get a Cluster object by id""" return self.clusters[cluster_id] def remove_cluster(self, cluster_id): """Delete a cluster by id""" # TODO: check running? del self.clusters[cluster_id] def clean_cluster_files(profile_dirs=None, *, log=None, force=False): """Find all files related to clusters, and remove them Cleans up stale logs, etc. if force: remove cluster files even for running If not force, will raise if any cluster files are found, because it's not safe to clean up cluster files while processes are running """ if profile_dirs is None: # default to all profiles profile_dirs = _all_profile_dirs() if isinstance(profile_dirs, str): profile_dirs = [profile_dirs] def _remove(f): if log: log.debug(f"Removing {f}") try: os.remove(f) except OSError as e: log.error(f"Error removing {f}: {e}") for profile_dir in profile_dirs: if log: log.info(f"Cleaning ipython cluster files in {profile_dir}") for cluster_file in ClusterManager._cluster_files_in_profile_dir(profile_dir): if force: _remove(cluster_file) else: # check if running first try: cluster = Cluster.from_file(cluster_file=cluster_file) except Exception as e: if log: log.error( f"Removing cluster file, which failed to load {cluster_file}: {e}" ) _remove(cluster_file) else: if cluster._is_running(): raise ValueError( "f{cluster} is still running. Use force=True to cleanup files for running clusters (may leave orphan processes!)" ) else: _remove(cluster_file) for log_file in glob.glob(os.path.join(profile_dir, 'log', '*')): _remove(log_file) for security_file in glob.glob( os.path.join(profile_dir, 'security', 'ipcontroller-*.json') ): _remove(security_file) ipyparallel-7.1.0/ipyparallel/cluster/launcher.py000066400000000000000000002400011412533144600222070ustar00rootroot00000000000000# encoding: utf-8 """Facilities for launching IPython Parallel processes asynchronously.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import asyncio import copy import inspect import json import logging import os import re import shutil import signal import stat import sys import threading import time from concurrent.futures import ThreadPoolExecutor from functools import lru_cache from functools import partial from signal import SIGTERM from subprocess import check_output from subprocess import PIPE from subprocess import Popen from subprocess import STDOUT from tempfile import TemporaryDirectory from textwrap import indent import entrypoints import psutil from IPython.utils.path import ensure_dir_exists from IPython.utils.path import get_home_dir from IPython.utils.text import EvalFormatter from tornado import ioloop from traitlets import Any from traitlets import CRegExp from traitlets import default from traitlets import Dict from traitlets import Float from traitlets import Instance from traitlets import Integer from traitlets import List from traitlets import observe from traitlets import Unicode from traitlets.config.configurable import LoggingConfigurable from ..util import shlex_join from ._winhpcjob import IPControllerJob from ._winhpcjob import IPControllerTask from ._winhpcjob import IPEngineSetJob from ._winhpcjob import IPEngineTask WINDOWS = os.name == 'nt' SIGKILL = getattr(signal, "SIGKILL", -1) # ----------------------------------------------------------------------------- # Paths to the kernel apps # ----------------------------------------------------------------------------- ipcluster_cmd_argv = [sys.executable, "-m", "ipyparallel.cluster"] ipengine_cmd_argv = [sys.executable, "-m", "ipyparallel.engine"] ipcontroller_cmd_argv = [sys.executable, "-m", "ipyparallel.controller"] # ----------------------------------------------------------------------------- # Base launchers and errors # ----------------------------------------------------------------------------- class LauncherError(Exception): pass class ProcessStateError(LauncherError): pass class UnknownStatus(LauncherError): pass class NotRunning(LauncherError): """Raised when a launcher is no longer running""" pass class BaseLauncher(LoggingConfigurable): """An abstraction for starting, stopping and signaling a process.""" stop_timeout = Integer( 60, config=True, help="The number of seconds to wait for a process to exit before raising a TimeoutError in stop", ) # In all of the launchers, the work_dir is where child processes will be # run. This will usually be the profile_dir, but may not be. any work_dir # passed into the __init__ method will override the config value. # This should not be used to set the work_dir for the actual engine # and controller. Instead, use their own config files or the # controller_args, engine_args attributes of the launchers to add # the work_dir option. work_dir = Unicode(u'.') # used in various places for labeling. often 'ipengine' or 'ipcontroller' name = Unicode("process") start_data = Any() stop_data = Any() identifier = Unicode( help="Used for lookup in e.g. EngineSetLauncher during notify_stop and default log files" ) @default("identifier") def _default_identifier(self): identifier = f"{self.name}" if self.cluster_id: identifier = f"{identifier}-{self.cluster_id}" if getattr(self, 'engine_set_id', None): identifier = f"{identifier}-{self.engine_set_id}" identifier = f"{identifier}-{os.getpid()}" return identifier loop = Instance(ioloop.IOLoop, allow_none=True) def _loop_default(self): return ioloop.IOLoop.current() profile_dir = Unicode('').tag(to_dict=True) cluster_id = Unicode('').tag(to_dict=True) state = Unicode("before").tag(to_dict=True) stop_callbacks = List() def to_dict(self): """Serialize a Launcher to a dict, for later restoration""" d = {} for attr in self.traits(to_dict=True): d[attr] = getattr(self, attr) return d @classmethod def from_dict(cls, d, *, config=None, parent=None, **kwargs): """Restore a Launcher from a dict Subclasses should always call `launcher = super().from_dict(*args, **kwargs)` and finish initialization after that. After calling from_dict(), the launcher should be in the same state as after `.start()` (i.e. monitoring for exit, etc.) Returns: Launcher The instantiated and fully configured Launcher. Raises: NotRunning e.g. if the process has stopped and is no longer running. """ launcher = cls(config=config, parent=parent, **kwargs) for attr in launcher.traits(to_dict=True): if attr in d: setattr(launcher, attr, d[attr]) return launcher @property def cluster_args(self): """Common cluster arguments""" return ['--profile-dir', self.profile_dir, '--cluster-id', self.cluster_id] @property def connection_files(self): """Dict of connection file paths""" security_dir = os.path.join(self.profile_dir, 'security') name_prefix = "ipcontroller" if self.cluster_id: name_prefix = f"{name_prefix}-{self.cluster_id}" return { kind: os.path.join(security_dir, f"{name_prefix}-{kind}.json") for kind in ("client", "engine") } @property def args(self): """A list of cmd and args that will be used to start the process. This is what is passed to :func:`spawnProcess` and the first element will be the process name. """ return self.find_args() def find_args(self): """The ``.args`` property calls this to find the args list. Subcommand should implement this to construct the cmd and args. """ raise NotImplementedError('find_args must be implemented in a subclass') @property def arg_str(self): """The string form of the program arguments.""" return ' '.join(self.args) @property def running(self): """Am I running.""" if self.state == 'running': return True else: return False async def start(self): """Start the process. Should be an `async def` coroutine. When start completes, the process should be requested (it need not be running yet), and waiting should begin in the background such that :meth:`.notify_stop` will be called when the process finishes. """ raise NotImplementedError('start must be implemented in a subclass') async def stop(self): """Stop the process and notify observers of stopping. This method should be an `async def` coroutine, and return only after the process has stopped. All resources should be cleaned up by the time this returns. """ raise NotImplementedError('stop must be implemented in a subclass') def on_stop(self, f): """Register a callback to be called with this Launcher's stop_data when the process actually finishes. """ if self.state == 'after': return f(self.stop_data) else: self.stop_callbacks.append(f) def notify_start(self, data): """Call this to trigger startup actions. This logs the process startup and sets the state to 'running'. It is a pass-through so it can be used as a callback. """ self.log.debug(f"{self.__class__.__name__} {self.args[0]} started: {data}") self.start_data = data self.state = 'running' return data def notify_stop(self, data): """Call this to trigger process stop actions. This logs the process stopping and sets the state to 'after'. Call this to trigger callbacks registered via :meth:`on_stop`.""" if self.state == 'after': self.log.debug("Already notified stop (data)") return data self.log.debug(f"{self.__class__.__name__} {self.args[0]} stopped: {data}") self.stop_data = data self.state = 'after' self._log_output(data) for f in self.stop_callbacks: f(data) return data def signal(self, sig): """Signal the process. Parameters ---------- sig : str or int 'KILL', 'INT', etc., or any signal number """ raise NotImplementedError('signal must be implemented in a subclass') async def join(self, timeout=None): """Wait for the process to finish""" raise NotImplementedError('join must be implemented in a subclass') output_limit = Integer( 100, config=True, help=""" When a process exits, display up to this many lines of output """, ) def get_output(self, remove=False): """Retrieve the output form the Launcher. If remove: remove the file, if any, where it was being stored. """ # override in subclasses to retrieve output return "" def _log_output(self, stop_data=None): output = self.get_output(remove=True) if self.output_limit: output = "".join(output.splitlines(True)[-self.output_limit :]) if output: self.log.debug(f"Output for {self.identifier}:\n{output}") class ControllerLauncher(BaseLauncher): """Base class for launching ipcontroller""" name = Unicode("ipcontroller") controller_cmd = List( list(ipcontroller_cmd_argv), config=True, help="""Popen command to launch ipcontroller.""", ) # Command line arguments to ipcontroller. controller_args = List( Unicode(), config=True, help="""command-line args to pass to ipcontroller""", ) async def get_connection_info(self): """Retrieve connection info for the controller Default implementation assumes profile_dir and cluster_id are local. """ connection_files = self.connection_files paths = list(connection_files.values()) first_run = True while not all(os.path.isfile(f) for f in paths): if first_run: first_run = False self.log.debug(f"Waiting for {paths}") await asyncio.sleep(0.1) status = self.poll() if inspect.isawaitable(status): status = await status if status is not None: raise RuntimeError( f"Controller stopped with {status} while waiting for {paths}" ) self.log.debug(f"Loading {paths}") connection_info = {} for key, path in connection_files.items(): try: with open(path) as f: connection_info[key] = json.load(f) except ValueError: # possible race while controller is still writing the file # give it half a second before trying again time.sleep(0.5) with open(path) as f: connection_info[key] = json.load(f) return connection_info class EngineLauncher(BaseLauncher): """Base class for launching one engine""" name = Unicode("ipengine") engine_cmd = List( ipengine_cmd_argv, config=True, help="""command to launch the Engine.""" ) # Command line arguments for ipengine. engine_args = List( ['--log-level=%i' % logging.INFO], config=True, help="command-line arguments to pass to ipengine", ) n = Integer(1).tag(to_dict=True) engine_set_id = Unicode() # ----------------------------------------------------------------------------- # Local process launchers # ----------------------------------------------------------------------------- class LocalProcessLauncher(BaseLauncher): """Start and stop an external process in an asynchronous manner. This will launch the external process with a working directory of ``self.work_dir``. """ # This is used to to construct self.args, which is passed to # spawnProcess. cmd_and_args = List(Unicode()) poll_seconds = Integer( 30, config=True, help="""Interval on which to poll processes (. Note: process exit should be noticed immediately, due to use of Process.wait(), but this interval should ensure we aren't leaving threads running forever, as other signals/events are checked on this interval """, ) pid = Integer(-1).tag(to_dict=True) output_file = Unicode().tag(to_dict=True) @default("output_file") def _default_output_file(self): log_dir = os.path.join(self.profile_dir, "log") os.makedirs(log_dir, exist_ok=True) return os.path.join(log_dir, f'{self.identifier}.log') stop_seconds_until_kill = Integer( 5, config=True, help="""The number of seconds to wait for a process to exit after sending SIGTERM before sending SIGKILL""", ) stdout = None stderr = None process = None _wait_thread = None def find_args(self): return self.cmd_and_args @classmethod def from_dict(cls, d, **kwargs): self = super().from_dict(d, **kwargs) self._reconstruct_process(d) return self def _reconstruct_process(self, d): """Reconstruct our process""" if 'pid' in d and d['pid'] > 0: try: self.process = psutil.Process(d['pid']) except psutil.NoSuchProcess as e: raise NotRunning(f"Process {d['pid']}") self._start_waiting() def _wait(self): """Background thread waiting for a process to exit""" exit_code = None while not self._stop_waiting.is_set() and self.state == 'running': try: # use a timeout so we can check the _stop_waiting event exit_code = self.process.wait(timeout=self.poll_seconds) except psutil.TimeoutExpired: continue else: break stop_data = dict(exit_code=exit_code, pid=self.pid, identifier=self.identifier) self.loop.add_callback(lambda: self.notify_stop(stop_data)) def _start_waiting(self): """Start background thread waiting on the process to exit""" # ensure self.loop is accessed on the main thread before waiting self.loop self._stop_waiting = threading.Event() self._wait_thread = threading.Thread( target=self._wait, daemon=True, name=f"wait(pid={self.pid})" ) self._wait_thread.start() def start(self): self.log.debug("Starting %s: %r", self.__class__.__name__, self.args) if self.state != 'before': raise ProcessStateError( 'The process was already started and has state: {self.state}' ) self.log.debug(f"Sending output for {self.identifier} to {self.output_file}") with open(self.output_file, "ab") as f: proc = Popen( self.args, stdout=f.fileno(), stderr=STDOUT, stdin=PIPE, env=os.environ, cwd=self.work_dir, start_new_session=True, # don't forward signals ) self.pid = proc.pid # use psutil API for self.process self.process = psutil.Process(proc.pid) self.notify_start(self.process.pid) self._start_waiting() if 1 <= self.log.getEffectiveLevel() <= logging.DEBUG: self._start_streaming() async def join(self, timeout=None): """Wait for the process to exit""" if self._wait_thread is not None: self._wait_thread.join(timeout=timeout) def _stream_file(self, path): """Stream one file""" with open(path, 'r') as f: while self.state == 'running' and not self._stop_waiting.is_set(): line = f.readline() # log prefix? # or stream directly to sys.stderr if line: sys.stderr.write(line) else: # pause while we are at the end of the file time.sleep(0.1) def _start_streaming(self): self._stream_thread = t = threading.Thread( target=partial(self._stream_file, self.output_file), name=f"Stream Output {self.identifier}", daemon=True, ) t.start() _output = None def get_output(self, remove=False): if self._output is None: if self.output_file: try: with open(self.output_file) as f: self._output = f.read() except FileNotFoundError: self.log.debug(f"Missing output file: {self.output_file}") self._output = "" else: self._output = "" if remove and os.path.isfile(self.output_file): self.log.debug(f"Removing {self.output_file}") try: os.remove(self.output_file) except Exception as e: # don't crash on failure to remove a file, # e.g. due to another processing having it open self.log.error(f"Failed to remove {self.output_file}: {e}") return self._output async def stop(self): try: self.signal(SIGTERM) except Exception as e: self.log.debug(f"TERM failed: {e!r}") try: await self.join(timeout=self.stop_seconds_until_kill) except TimeoutError: self.log.warning( f"Process {self.pid} did not exit in {self.stop_seconds_until_kill} seconds after TERM" ) else: return try: self.signal(SIGKILL) except Exception as e: self.log.debug(f"KILL failed: {e!r}") await self.join(timeout=self.stop_timeout) def signal(self, sig): if self.state == 'running': if WINDOWS and sig in {SIGTERM, SIGKILL}: # use Windows tree-kill for better child cleanup cmd = ['taskkill', '/pid', str(self.process.pid), '/t', '/F'] check_output(cmd) else: self.process.send_signal(sig) # callbacks, etc: def handle_stdout(self, fd, events): if WINDOWS: line = self.stdout.recv().decode('utf8', 'replace') else: line = self.stdout.readline().decode('utf8', 'replace') # a stopped process will be readable but return empty strings if line: self.log.debug(line.rstrip()) def handle_stderr(self, fd, events): if WINDOWS: line = self.stderr.recv().decode('utf8', 'replace') else: line = self.stderr.readline().decode('utf8', 'replace') # a stopped process will be readable but return empty strings if line: self.log.debug(line.rstrip()) else: self.poll() def poll(self): if self.process.is_running(): return None status = self.process.wait(0) if status is None: # return code cannot always be retrieved. # but we need to not return None if it's still running status = 'unknown' self.notify_stop( dict(exit_code=status, pid=self.process.pid, identifier=self.identifier) ) return status class LocalControllerLauncher(LocalProcessLauncher, ControllerLauncher): """Launch a controller as a regular external process.""" def find_args(self): return self.controller_cmd + self.cluster_args + self.controller_args def start(self): """Start the controller by profile_dir.""" return super(LocalControllerLauncher, self).start() class LocalEngineLauncher(LocalProcessLauncher, EngineLauncher): """Launch a single engine as a regular external process.""" def find_args(self): return self.engine_cmd + self.cluster_args + self.engine_args class LocalEngineSetLauncher(LocalEngineLauncher): """Launch a set of engines as regular external processes.""" delay = Float( 0.1, config=True, help="""delay (in seconds) between starting each engine after the first. This can help force the engines to get their ids in order, or limit process flood when starting many engines.""", ) # launcher class launcher_class = LocalEngineLauncher launchers = Dict() stop_data = Dict() outputs = Dict() output_file = "" # no output file for me def __init__(self, work_dir=u'.', config=None, **kwargs): super(LocalEngineSetLauncher, self).__init__( work_dir=work_dir, config=config, **kwargs ) def to_dict(self): d = super().to_dict() d['engines'] = {i: launcher.to_dict() for i, launcher in self.launchers.items()} return d @classmethod def from_dict(cls, d, **kwargs): self = super().from_dict(d, **kwargs) n = 0 for i, engine_dict in d['engines'].items(): try: self.launchers[i] = el = self.launcher_class.from_dict( engine_dict, identifier=i, parent=self ) except NotRunning as e: self.log.error(f"Engine {i} not running: {e}") else: n += 1 el.on_stop(self._notice_engine_stopped) if n == 0: raise NotRunning("No engines left") else: self.n = n return self def start(self, n): """Start n engines by profile or profile_dir.""" self.n = n dlist = [] for i in range(n): identifier = str(i) if i > 0: time.sleep(self.delay) el = self.launchers[identifier] = self.launcher_class( work_dir=self.work_dir, parent=self, log=self.log, profile_dir=self.profile_dir, cluster_id=self.cluster_id, identifier=identifier, output_file=os.path.join( self.profile_dir, "log", f"ipengine-{self.cluster_id}-{self.engine_set_id}-{i}.log", ), ) # Copy the engine args over to each engine launcher. el.engine_cmd = copy.deepcopy(self.engine_cmd) el.engine_args = copy.deepcopy(self.engine_args) el.on_stop(self._notice_engine_stopped) d = el.start() dlist.append(d) self.notify_start(dlist) return dlist def find_args(self): return ['engine set'] def signal(self, sig): for el in list(self.launchers.values()): el.signal(sig) async def stop(self): futures = [] for el in list(self.launchers.values()): f = el.stop() if inspect.isawaitable(f): futures.append(asyncio.ensure_future(f)) if futures: await asyncio.gather(*futures) def _notice_engine_stopped(self, data): identifier = data['identifier'] launcher = self.launchers.pop(identifier) engines = self.stop_data.setdefault("engines", {}) if launcher is not None: self.outputs[identifier] = launcher.get_output() engines[identifier] = data if not self.launchers: # get exit code from engine exit codes # set error code if any engine has an error self.stop_data["exit_code"] = None for engine in engines.values(): if 'exit_code' in engine: if self.stop_data['exit_code'] is None: self.stop_data['exit_code'] = engine['exit_code'] if engine['exit_code']: # save the first nonzero exit code self.stop_data['exit_code'] = engine['exit_code'] break self.notify_stop(self.stop_data) def get_output(self, remove=False): """Get the output of all my child Launchers""" for identifier, launcher in self.launchers.items(): # remaining launchers self.outputs[identifier] = launcher.get_output(remove=remove) joined_output = [] for identifier, engine_output in self.outputs.items(): if engine_output: joined_output.append(f"Output for engine {identifier}") if self.output_limit: engine_output = "".join( engine_output.splitlines(True)[-self.output_limit :] ) joined_output.append(indent(engine_output, ' ')) return '\n'.join(joined_output) # ----------------------------------------------------------------------------- # MPI launchers # ----------------------------------------------------------------------------- class MPILauncher(LocalProcessLauncher): """Launch an external process using mpiexec.""" mpi_cmd = List( ['mpiexec'], config=True, help="The mpiexec command to use in starting the process.", ) mpi_args = List( [], config=True, help="The command line arguments to pass to mpiexec." ) program = List(['date'], help="The program to start via mpiexec.") program_args = List([], help="The command line argument to the program.") def __init__(self, *args, **kwargs): # deprecation for old MPIExec names: config = kwargs.get('config') or {} for oldname in ( 'MPIExecLauncher', 'MPIExecControllerLauncher', 'MPIExecEngineSetLauncher', ): deprecated = config.get(oldname) if deprecated: newname = oldname.replace('MPIExec', 'MPI') config[newname].update(deprecated) self.log.warning( "WARNING: %s name has been deprecated, use %s", oldname, newname ) super(MPILauncher, self).__init__(*args, **kwargs) def find_args(self): """Build self.args using all the fields.""" return ( self.mpi_cmd + ['-n', str(self.n)] + self.mpi_args + self.program + self.program_args ) def start(self, n): """Start n instances of the program using mpiexec.""" self.n = n return super(MPILauncher, self).start() def _log_output(self, stop_data): """Try to log mpiexec error output, if any, at warning level""" super()._log_output() if self.log.getEffectiveLevel() <= logging.DEBUG: return output = self.get_output(remove=False) mpiexec_lines = [] in_mpi = False after_mpi = False mpi_tail = 0 for line in output.splitlines(True): if line.startswith("======="): # mpich output looks like one block, # with a few lines trailing after # ========= # = message # = # ========= # YOUR APPLICATION TERMINATED WITH... if in_mpi: after_mpi = True mpi_tail = 2 in_mpi = False else: in_mpi = True elif not in_mpi and line.startswith("-----"): # openmpi has less clear boundaries; # potentially several blocks that start and end with `----` # and error messages can show up after one or more blocks # once we see one of these lines, capture everything after it # toggle on each such line if not in_mpi: in_mpi = True # this would let us only capture messages inside blocks # but doing so would exclude most useful error output # else: # # show the trailing delimiter line # mpiexec_lines.append(line) # in_mpi = False # continue if in_mpi: mpiexec_lines.append(line) elif after_mpi: if mpi_tail <= 0: break else: mpi_tail -= 1 mpiexec_lines.append(line) if mpiexec_lines: self.log.warning("mpiexec error output:\n" + "".join(mpiexec_lines)) class MPIControllerLauncher(MPILauncher, ControllerLauncher): """Launch a controller using mpiexec.""" # alias back to *non-configurable* program[_args] for use in find_args() # this way all Controller/EngineSetLaunchers have the same form, rather # than *some* having `program_args` and others `controller_args` @property def program(self): return self.controller_cmd @property def program_args(self): return self.cluster_args + self.controller_args class MPIEngineSetLauncher(MPILauncher, EngineLauncher): """Launch engines using mpiexec""" # alias back to *non-configurable* program[_args] for use in find_args() # this way all Controller/EngineSetLaunchers have the same form, rather # than *some* having `program_args` and others `controller_args` @property def program(self): return self.engine_cmd + ['--mpi'] @property def program_args(self): return self.cluster_args + self.engine_args def start(self, n): """Start n engines by profile or profile_dir.""" self.n = n return super(MPIEngineSetLauncher, self).start(n) # deprecated MPIExec names class DeprecatedMPILauncher: def warn(self): oldname = self.__class__.__name__ newname = oldname.replace('MPIExec', 'MPI') self.log.warning("WARNING: %s name is deprecated, use %s", oldname, newname) class MPIExecLauncher(MPILauncher, DeprecatedMPILauncher): """Deprecated, use MPILauncher""" def __init__(self, *args, **kwargs): super(MPIExecLauncher, self).__init__(*args, **kwargs) self.warn() class MPIExecControllerLauncher(MPIControllerLauncher, DeprecatedMPILauncher): """Deprecated, use MPIControllerLauncher""" def __init__(self, *args, **kwargs): super(MPIExecControllerLauncher, self).__init__(*args, **kwargs) self.warn() class MPIExecEngineSetLauncher(MPIEngineSetLauncher, DeprecatedMPILauncher): """Deprecated, use MPIEngineSetLauncher""" def __init__(self, *args, **kwargs): super(MPIExecEngineSetLauncher, self).__init__(*args, **kwargs) self.warn() # ----------------------------------------------------------------------------- # SSH launchers # ----------------------------------------------------------------------------- ssh_output_pattern = re.compile(r"__([a-z][a-z0-9_]+)=([a-z0-9\-\.]+)__", re.IGNORECASE) def _ssh_outputs(out): """Extract ssh output variables from process output""" return dict(ssh_output_pattern.findall(out)) def sshx(ssh_cmd, cmd, remote_output_file, log=None): """Launch a remote process, returning its remote pid Uses nohup and pipes to put it in the background """ remote_cmd = shlex_join(cmd) full_remote_cmd = [ f"nohup {remote_cmd} > {remote_output_file} 2>&1 0: self._start_waiting() def poll(self): """Override poll""" if self.state == 'running': return None else: return 0 def get_output(self, remove=False): """Retrieve engine output from the remote file""" if self._output is None: with TemporaryDirectory() as td: output_file = os.path.join( td, os.path.basename(self.remote_output_file) ) try: self._fetch_file(self.remote_output_file, output_file) except Exception as e: self.log.error( f"Failed to get output file {self.remote_output_file}: {e}" ) self._output = '' else: if remove: # remove the file after we retrieve it self.log.info( f"Removing {self.location}:{self.remote_output_file}" ) check_output( self.ssh_cmd + self.ssh_args + [ self.location, "--", shlex_join(["rm", "-f", self.remote_output_file]), ], input=None, ) with open(output_file) as f: self._output = f.read() return self._output def _send_file(self, local, remote, wait=True): """send a single file""" full_remote = "%s:%s" % (self.location, remote) for i in range(10 if wait else 0): if not os.path.exists(local): self.log.debug("waiting for %s" % local) time.sleep(1) else: break remote_dir = os.path.dirname(remote) self.log.info("ensuring remote %s:%s/ exists", self.location, remote_dir) check_output( self.ssh_cmd + self.ssh_args + [self.location, '--', 'mkdir', '-p', remote_dir], input=None, ) self.log.info("sending %s to %s", local, full_remote) check_output(self.scp_cmd + self.scp_args + [local, full_remote], input=None) def send_files(self): """send our files (called before start)""" if not self.to_send: return for local_file, remote_file in self.to_send: self._send_file(local_file, remote_file) def _fetch_file(self, remote, local, wait=True): """fetch a single file""" full_remote = "%s:%s" % (self.location, remote) self.log.info("fetching %s from %s", local, full_remote) for i in range(10 if wait else 0): # wait up to 10s for remote file to exist check = check_output( self.ssh_cmd + self.ssh_args + [self.location, 'test -e', remote, "&& echo 'yes' || echo 'no'"], input=None, ) check = check.decode("utf8", 'replace').strip() if check == u'no': time.sleep(1) elif check == u'yes': break local_dir = os.path.dirname(local) ensure_dir_exists(local_dir, 700) check_output(self.scp_cmd + self.scp_args + [full_remote, local]) def fetch_files(self): """fetch remote files (called after start)""" if not self.to_fetch: return for remote_file, local_file in self.to_fetch: self._fetch_file(remote_file, local_file) def start(self, hostname=None, user=None, port=None): if hostname is not None: self.hostname = hostname if user is not None: self.user = user if port is not None: if '-p' not in self.ssh_args: self.ssh_args.append('-p') self.ssh_args.append(str(port)) if '-P' not in self.scp_args: self.scp_args.append('-P') self.scp_args.append(str(port)) # create remote profile dir check_output( self.ssh_cmd + self.ssh_args + [ self.location, shlex_join( [ self.remote_python, "-m", "IPython", "profile", "create", "--profile-dir", self.remote_profile_dir, ] ), ], input=None, ) self.send_files() self.pid = sshx( self.ssh_cmd + self.ssh_args + [self.location], self.program + self.program_args, self.remote_output_file, log=self.log, ) self.notify_start({'host': self.location, 'pid': self.pid}) self._start_waiting() self.fetch_files() def _wait(self): """Background thread waiting for a process to exit""" exit_code = None while not self._stop_waiting.is_set() and self.state == 'running': try: # use a timeout so we can check the _stop_waiting event exit_code = self.wait_one(timeout=self.poll_seconds) except TimeoutError: continue else: break stop_data = dict(exit_code=exit_code, pid=self.pid, identifier=self.identifier) self.loop.add_callback(lambda: self.notify_stop(stop_data)) def _start_waiting(self): """Start background thread waiting on the process to exit""" # ensure self.loop is accessed on the main thread before waiting self.loop self._stop_waiting = threading.Event() self._wait_thread = threading.Thread( target=self._wait, daemon=True, name=f"wait(host={self.location}, pid={self.pid})", ) self._wait_thread.start() def wait_one(self, timeout): python_code = f"from ipyparallel.cluster.launcher import ssh_waitpid; ssh_waitpid({self.pid}, timeout={timeout})" full_cmd = ( self.ssh_cmd + self.ssh_args # double-quote for ssh + [self.location, "--", self.remote_python, "-c", f"'{python_code}'"] ) out = check_output(full_cmd, input=None, start_new_session=True).decode( "utf8", "replace" ) values = _ssh_outputs(out) if 'process_running' not in values: raise RuntimeError(out) running = int(values.get("process_running", 0)) if running: raise TimeoutError("still running") return int(values.get("exit_code", -1)) async def join(self, timeout=None): with ThreadPoolExecutor(1) as pool: wait = partial(self.wait_one, timeout=timeout) try: future = pool.submit(wait) except RuntimeError: # e.g. called during process shutdown, # which raises # RuntimeError: cannot schedule new futures after interpreter shutdown # Instead, do the blocking call wait() else: await asyncio.wrap_future(future) if getattr(self, '_stop_waiting', None) and self._wait_thread: self._stop_waiting.set() # got here, should be done # wait for wait_thread to cleanup self._wait_thread.join() def signal(self, sig): if self.state == 'running': check_output( self.ssh_cmd + self.ssh_args + [ self.location, '--', 'kill', f'-{sig}', str(self.pid), ], input=None, ) @property def remote_connection_files(self): """Return remote paths for connection files""" return { key: self.remote_profile_dir + local_path[len(self.profile_dir) :] for key, local_path in self.connection_files.items() } class SSHControllerLauncher(SSHLauncher, ControllerLauncher): # alias back to *non-configurable* program[_args] for use in find_args() # this way all Controller/EngineSetLaunchers have the same form, rather # than *some* having `program_args` and others `controller_args` def _controller_cmd_default(self): return [self.remote_python, "-m", 'ipyparallel.controller'] @property def program(self): return self.controller_cmd @property def program_args(self): return self.cluster_args + self.controller_args @default("to_fetch") def _to_fetch_default(self): to_fetch = [] return [ (self.remote_connection_files[key], local_path) for key, local_path in self.connection_files.items() ] class SSHEngineLauncher(SSHLauncher, EngineLauncher): # alias back to *non-configurable* program[_args] for use in find_args() # this way all Controller/EngineSetLaunchers have the same form, rather # than *some* having `program_args` and others `controller_args` def _engine_cmd_default(self): return [self.remote_python, "-m", "ipyparallel.engine"] @property def program(self): return self.engine_cmd @property def program_args(self): return self.cluster_args + self.engine_args @default("to_send") def _to_send_default(self): return [ (local_path, self.remote_connection_files[key]) for key, local_path in self.connection_files.items() ] class SSHEngineSetLauncher(LocalEngineSetLauncher, SSHLauncher): launcher_class = SSHEngineLauncher engines = Dict( config=True, help="""dict of engines to launch. This is a dict by hostname of ints, corresponding to the number of engines to start on that host.""", ).tag(to_dict=True) def _engine_cmd_default(self): return [self.remote_python, "-m", "ipyparallel.engine"] # unset some traits we inherit but don't use remote_output_file = "" def get_output(self, remove=True): # no-op in EngineSet, EngineLaunchers take care of this return '' def start(self, n): """Start engines by profile or profile_dir. `n` is an *upper limit* of engines. The `engines` config property is used to assign slots to hosts. """ dlist = [] # traits to inherit: # + all common config traits # - traits set per-engine via engines dict # + some non-configurable traits such as cluster_id engine_traits = self.launcher_class.class_traits(config=True) my_traits = self.traits(config=True) shared_traits = set(my_traits).intersection(engine_traits) # in addition to shared traits, pass some derived traits # and exclude some composite traits inherited_traits = shared_traits.difference( {"location", "user", "hostname", "to_send", "to_fetch"} ).union({"profile_dir", "cluster_id"}) requested_n = n started_n = 0 for host, n_or_config in self.engines.items(): if isinstance(n_or_config, dict): overrides = n_or_config n = overrides.pop("n", 1) else: overrides = {} n = n_or_config full_host = host if '@' in host: user, host = host.split('@', 1) else: user = None if ':' in host: host, port = host.split(':', 1) else: port = None for i in range(min(n, requested_n - started_n)): if i > 0: time.sleep(self.delay) # pass all common traits to the launcher kwargs = {attr: getattr(self, attr) for attr in inherited_traits} # overrides from engine config kwargs.update(overrides) # explicit per-engine values kwargs['parent'] = self kwargs['identifier'] = key = f"{full_host}/{i}" el = self.launchers[key] = self.launcher_class(**kwargs) if i > 0: # only send files for the first engine on each host el.to_send = [] el.on_stop(self._notice_engine_stopped) d = el.start(user=user, hostname=host, port=port) dlist.append(key) started_n += 1 if started_n >= requested_n: break self.notify_start(dlist) self.n = started_n return dlist class SSHProxyEngineSetLauncher(SSHLauncher, EngineLauncher): """Launcher for calling `ipcluster engines` on a remote machine. Requires that remote profile is already configured. """ n = Integer().tag(to_dict=True) ipcluster_cmd = List(Unicode(), config=True) @default("ipcluster_cmd") def _default_ipcluster_cmd(self): return [self.remote_python, "-m", "ipyparallel.cluster"] ipcluster_args = List( Unicode(), config=True, help="""Extra CLI arguments to pass to ipcluster engines""", ) @property def program(self): return self.ipcluster_cmd + ['engines'] @property def program_args(self): return [ '-n', str(self.n), '--profile-dir', self.remote_profile_dir, '--cluster-id', self.cluster_id, ] + self.ipcluster_args @default("to_send") def _to_send_default(self): return [ (local_path, self.remote_connection_files[key]) for key, local_path in self.connection_files.items() ] def start(self, n): self.n = n super(SSHProxyEngineSetLauncher, self).start() # ----------------------------------------------------------------------------- # Windows HPC Server 2008 scheduler launchers # ----------------------------------------------------------------------------- class WindowsHPCLauncher(BaseLauncher): job_id_regexp = CRegExp( r'\d+', config=True, help="""A regular expression used to get the job id from the output of the submit_command. """, ) job_file_name = Unicode( u'ipython_job.xml', config=True, help="The filename of the instantiated job script.", ) # The full path to the instantiated job script. This gets made dynamically # by combining the work_dir with the job_file_name. job_file = Unicode(u'') scheduler = Unicode( '', config=True, help="The hostname of the scheduler to submit the job to." ) job_cmd = Unicode(config=True, help="The command for submitting jobs.") @default("job_cmd") def _default_job(self): return shutil.which("job") or "job" @property def job_file(self): return os.path.join(self.work_dir, self.job_file_name) def write_job_file(self, n): raise NotImplementedError("Implement write_job_file in a subclass.") def find_args(self): return [u'job.exe'] def parse_job_id(self, output): """Take the output of the submit command and return the job id.""" m = self.job_id_regexp.search(output) if m is not None: job_id = m.group() else: raise LauncherError("Job id couldn't be determined: %s" % output) self.job_id = job_id self.log.info('Job started with id: %r', job_id) return job_id def start(self, n): """Start n copies of the process using the Win HPC job scheduler.""" self.write_job_file(n) args = [ 'submit', '/jobfile:%s' % self.job_file, '/scheduler:%s' % self.scheduler, ] self.log.debug( "Starting Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),) ) output = check_output( [self.job_cmd] + args, env=os.environ, cwd=self.work_dir, stderr=STDOUT ) output = output.decode("utf8", 'replace') job_id = self.parse_job_id(output) self.notify_start(job_id) return job_id def stop(self): args = ['cancel', self.job_id, '/scheduler:%s' % self.scheduler] self.log.info( "Stopping Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),) ) try: output = check_output( [self.job_cmd] + args, env=os.environ, cwd=self.work_dir, stderr=STDOUT ) output = output.decode("utf8", 'replace') except: output = u'The job already appears to be stopped: %r' % self.job_id self.notify_stop( dict(job_id=self.job_id, output=output) ) # Pass the output of the kill cmd return output class WindowsHPCControllerLauncher(WindowsHPCLauncher): job_file_name = Unicode( u'ipcontroller_job.xml', config=True, help="WinHPC xml job file." ) controller_args = List([], config=False, help="extra args to pass to ipcontroller") def write_job_file(self, n): job = IPControllerJob(parent=self) t = IPControllerTask(parent=self) # The tasks work directory is *not* the actual work directory of # the controller. It is used as the base path for the stdout/stderr # files that the scheduler redirects to. t.work_directory = self.profile_dir # Add the profile_dir and from self.start(). t.controller_args.extend(self.cluster_args) t.controller_args.extend(self.controller_args) job.add_task(t) self.log.debug("Writing job description file: %s", self.job_file) job.write(self.job_file) class WindowsHPCEngineSetLauncher(WindowsHPCLauncher): job_file_name = Unicode( u'ipengineset_job.xml', config=True, help="jobfile for ipengines job" ) engine_args = List([], config=False, help="extra args to pas to ipengine") def write_job_file(self, n): job = IPEngineSetJob(parent=self) for i in range(n): t = IPEngineTask(parent=self) # The tasks work directory is *not* the actual work directory of # the engine. It is used as the base path for the stdout/stderr # files that the scheduler redirects to. t.work_directory = self.profile_dir # Add the profile_dir and from self.start(). t.engine_args.extend(self.cluster_args) t.engine_args.extend(self.engine_args) job.add_task(t) self.log.debug("Writing job description file: %s", self.job_file) job.write(self.job_file) def start(self, n): """Start the controller by profile_dir.""" return super(WindowsHPCEngineSetLauncher, self).start(n) # ----------------------------------------------------------------------------- # Batch (PBS) system launchers # ----------------------------------------------------------------------------- class BatchSystemLauncher(BaseLauncher): """Launch an external process using a batch system. This class is designed to work with UNIX batch systems like PBS, LSF, GridEngine, etc. The overall model is that there are different commands like qsub, qdel, etc. that handle the starting and stopping of the process. This class also has the notion of a batch script. The ``batch_template`` attribute can be set to a string that is a template for the batch script. This template is instantiated using string formatting. Thus the template can use {n} for the number of instances. Subclasses can add additional variables to the template dict. """ # load cluster args into context instead of cli @observe('profile_dir') def _profile_dir_changed(self, change): self._update_context(change) @observe('cluster_id') def _cluster_id_changed(self, change): self._update_context(change) def _profile_dir_default(self): self.context['profile_dir'] = '' return '' def _cluster_id_default(self): self.context['cluster_id'] = '' return '' # Subclasses must fill these in. See PBSEngineSet submit_command = List( [''], config=True, help="The name of the command line program used to submit jobs.", ) delete_command = List( [''], config=True, help="The name of the command line program used to delete jobs.", ) signal_command = List( [''], config=True, help="The name of the command line program used to send signals to jobs.", ) job_id = Unicode().tag(to_dict=True) job_id_regexp = CRegExp( '', config=True, help="""A regular expression used to get the job id from the output of the submit_command.""", ) job_id_regexp_group = Integer( 0, config=True, help="""The group we wish to match in job_id_regexp (0 to match all)""", ) batch_template = Unicode( '', config=True, help="The string that is the batch script template itself." ).tag(to_dict=True) batch_template_file = Unicode( u'', config=True, help="The file that contains the batch template." ) batch_file_name = Unicode( u'batch_script', config=True, help="The filename of the instantiated batch script.", ).tag(to_dict=True) queue = Unicode(u'', config=True, help="The batch queue.").tag(to_dict=True) @observe('queue') def _queue_changed(self, change): self._update_context(change) n = Integer(1).tag(to_dict=True) @observe('n') def _n_changed(self, change): self._update_context(change) # not configurable, override in subclasses # Job Array regex job_array_regexp = CRegExp('') job_array_template = Unicode('') # Queue regex queue_regexp = CRegExp('') queue_template = Unicode('') # The default batch template, override in subclasses default_template = Unicode('') # The full path to the instantiated batch script. batch_file = Unicode(u'') # the format dict used with batch_template: context = Dict() namespace = Dict( config=True, help="""Extra variables to pass to the template. This lets you parameterize additional options, such as wall_time with a custom template. """, ).tag(to_dict=True) @default("context") def _context_default(self): """load the default context with the default values for the basic keys because the _trait_changed methods only load the context if they are set to something other than the default value. """ return dict(n=1, queue=u'', profile_dir=u'', cluster_id=u'') program = List(Unicode()) program_args = List(Unicode()) @observe("program", "program_args") def _program_changed(self, change=None): self.context['program'] = shlex_join(self.program) self.context['program_args'] = shlex_join(self.program_args) self.context['program_and_args'] = shlex_join(self.program + self.program_args) @observe("n", "queue") def _update_context(self, change): self.context[change['name']] = change['new'] # the Formatter instance for rendering the templates: formatter = Instance(EvalFormatter, (), {}) def find_args(self): return self.submit_command + [self.batch_file] def __init__(self, work_dir=u'.', config=None, **kwargs): super(BatchSystemLauncher, self).__init__( work_dir=work_dir, config=config, **kwargs ) self.batch_file = os.path.join(self.work_dir, self.batch_file_name) # trigger program_changed to populate default context arguments self._program_changed() def parse_job_id(self, output): """Take the output of the submit command and return the job id.""" m = self.job_id_regexp.search(output) if m is not None: job_id = m.group(self.job_id_regexp_group) else: raise LauncherError("Job id couldn't be determined: %s" % output) self.job_id = job_id self.log.info('Job submitted with job id: %r', job_id) return job_id def write_batch_script(self, n=1): """Instantiate and write the batch script to the work_dir.""" self.n = n # first priority is batch_template if set if self.batch_template_file and not self.batch_template: # second priority is batch_template_file with open(self.batch_template_file) as f: self.batch_template = f.read() if not self.batch_template: # third (last) priority is default_template self.batch_template = self.default_template # add jobarray or queue lines to user-specified template # note that this is *only* when user did not specify a template. self._insert_options_in_script() self._insert_job_array_in_script() ns = {} # internally generated ns.update(self.context) # from user config ns.update(self.namespace) script_as_string = self.formatter.format(self.batch_template, **ns) self.log.debug(f'Writing batch script: {self.batch_file}\n{script_as_string}') with open(self.batch_file, 'w') as f: f.write(script_as_string) os.chmod(self.batch_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) def _insert_options_in_script(self): """Inserts a queue if required into the batch script.""" if self.queue and not self.queue_regexp.search(self.batch_template): self.log.debug("adding queue settings to batch script") firstline, rest = self.batch_template.split('\n', 1) self.batch_template = u'\n'.join([firstline, self.queue_template, rest]) def _insert_job_array_in_script(self): """Inserts a job array if required into the batch script.""" if not self.job_array_regexp.search(self.batch_template): self.log.debug("adding job array settings to batch script") firstline, rest = self.batch_template.split('\n', 1) self.batch_template = u'\n'.join([firstline, self.job_array_template, rest]) def start(self, n=1): """Start n copies of the process using a batch system.""" self.log.debug("Starting %s: %r", self.__class__.__name__, self.args) # Here we save profile_dir in the context so they # can be used in the batch script template as {profile_dir} self.write_batch_script(n) output = check_output(self.args, env=os.environ) output = output.decode("utf8", 'replace') self.log.debug(f"Submitted {shlex_join(self.args)}. Output: {output}") job_id = self.parse_job_id(output) self.notify_start(job_id) return job_id def stop(self): try: output = check_output( self.delete_command + [self.job_id], stdin=None, ).decode("utf8", 'replace') except Exception: self.log.exception( "Problem stopping cluster with command: %s" % (self.delete_command + [self.job_id]) ) output = "" self.notify_stop( dict(job_id=self.job_id, output=output) ) # Pass the output of the kill cmd return output def signal(self, sig): cmd = self.signal_command + [str(sig), self.job_id] try: output = check_output( cmd, stdin=None, ).decode("utf8", 'replace') except Exception: self.log.exception("Problem sending signal with: {shlex_join(cmd)}") output = "" class BatchControllerLauncher(BatchSystemLauncher, ControllerLauncher): @default("program") def _default_program(self): return self.controller_cmd @observe("controller_cmd") def _controller_cmd_changed(self, change): self.program = self._default_program() @default("program_args") def _default_program_args(self): return self.cluster_args + self.controller_args @observe("controller_args") def _controller_args_changed(self, change): self.program_args = self._default_program_args() def start(self): return super().start(n=1) class BatchEngineSetLauncher(BatchSystemLauncher, EngineLauncher): @default("program") def _default_program(self): return self.engine_cmd @observe("engine_cmd") def _engine_cmd_changed(self, change): self.program = self._default_program() @default("program_args") def _default_program_args(self): return self.cluster_args + self.engine_args @observe("engine_args") def _engine_args_changed(self, change): self.program_args = self._default_program_args() class PBSLauncher(BatchSystemLauncher): """A BatchSystemLauncher subclass for PBS.""" submit_command = List(['qsub'], config=True, help="The PBS submit command ['qsub']") delete_command = List(['qdel'], config=True, help="The PBS delete command ['qdel']") signal_command = List( ['qsig', '-s'], config=True, help="The PBS signal command ['qsig']" ) job_id_regexp = CRegExp( r'\d+', config=True, help=r"Regular expresion for identifying the job ID [r'\d+']", ) batch_file = Unicode(u'') job_array_regexp = CRegExp(r'#PBS\W+-t\W+[\w\d\-\$]+') job_array_template = Unicode('#PBS -t 1-{n}') queue_regexp = CRegExp(r'#PBS\W+-q\W+\$?\w+') queue_template = Unicode('#PBS -q {queue}') class PBSControllerLauncher(PBSLauncher, BatchControllerLauncher): """Launch a controller using PBS.""" batch_file_name = Unicode( u'pbs_controller', config=True, help="batch file name for the controller job." ) default_template = Unicode( """#!/bin/sh #PBS -V #PBS -N ipcontroller {program_and_args} """ ) class PBSEngineSetLauncher(PBSLauncher, BatchEngineSetLauncher): """Launch Engines using PBS""" batch_file_name = Unicode( u'pbs_engines', config=True, help="batch file name for the engine(s) job." ) default_template = Unicode( u"""#!/bin/sh #PBS -V #PBS -N ipengine {program_and_args} """ ) # Slurm is very similar to PBS class SlurmLauncher(BatchSystemLauncher): """A BatchSystemLauncher subclass for slurm.""" submit_command = List( ['sbatch'], config=True, help="The slurm submit command ['sbatch']" ) delete_command = List( ['scancel'], config=True, help="The slurm delete command ['scancel']" ) signal_command = List( ['scancel', '-s'], config=True, help="The slurm signal command ['scancel', '-s']", ) job_id_regexp = CRegExp( r'\d+', config=True, help=r"Regular expresion for identifying the job ID [r'\d+']", ) account = Unicode(u"", config=True, help="Slurm account to be used") qos = Unicode(u"", config=True, help="Slurm QoS to be used") # Note: from the man page: #'Acceptable time formats include "minutes", "minutes:seconds", # "hours:minutes:seconds", "days-hours", "days-hours:minutes" # and "days-hours:minutes:seconds". timelimit = Any(u"", config=True, help="Slurm timelimit to be used") options = Unicode(u"", config=True, help="Extra Slurm options") @observe('account') def _account_changed(self, change): self._update_context(change) @observe('qos') def _qos_changed(self, change): self._update_context(change) @observe('timelimit') def _timelimit_changed(self, change): self._update_context(change) @observe('options') def _options_changed(self, change): self._update_context(change) batch_file = Unicode(u'') job_array_regexp = CRegExp(r'#SBATCH\W+(?:--ntasks|-n)[\w\d\-\$]+') job_array_template = Unicode('''#SBATCH --ntasks={n}''') queue_regexp = CRegExp(r'#SBATCH\W+(?:--partition|-p)\W+\$?\w+') queue_template = Unicode('#SBATCH --partition={queue}') account_regexp = CRegExp(r'#SBATCH\W+(?:--account|-A)\W+\$?\w+') account_template = Unicode('#SBATCH --account={account}') qos_regexp = CRegExp(r'#SBATCH\W+--qos\W+\$?\w+') qos_template = Unicode('#SBATCH --qos={qos}') timelimit_regexp = CRegExp(r'#SBATCH\W+(?:--time|-t)\W+\$?\w+') timelimit_template = Unicode('#SBATCH --time={timelimit}') def _insert_options_in_script(self): """Insert 'partition' (slurm name for queue), 'account', 'time' and other options if necessary""" if self.queue and not self.queue_regexp.search(self.batch_template): self.log.debug("adding slurm queue settings to batch script") firstline, rest = self.batch_template.split('\n', 1) self.batch_template = u'\n'.join([firstline, self.queue_template, rest]) if self.account and not self.account_regexp.search(self.batch_template): self.log.debug("adding slurm account settings to batch script") firstline, rest = self.batch_template.split('\n', 1) self.batch_template = u'\n'.join([firstline, self.account_template, rest]) if self.qos and not self.qos_regexp.search(self.batch_template): self.log.debug("adding Slurm qos settings to batch script") firstline, rest = self.batch_template.split('\n', 1) self.batch_template = u'\n'.join([firstline, self.qos_template, rest]) if self.timelimit and not self.timelimit_regexp.search(self.batch_template): self.log.debug("adding slurm time limit settings to batch script") firstline, rest = self.batch_template.split('\n', 1) self.batch_template = u'\n'.join([firstline, self.timelimit_template, rest]) class SlurmControllerLauncher(SlurmLauncher, BatchControllerLauncher): """Launch a controller using Slurm.""" batch_file_name = Unicode( u'slurm_controller.sbatch', config=True, help="batch file name for the controller job.", ) default_template = Unicode( """#!/bin/sh #SBATCH --job-name=ipy-controller-{cluster_id} #SBATCH --ntasks=1 {program_and_args} """ ) class SlurmEngineSetLauncher(SlurmLauncher, BatchEngineSetLauncher): """Launch Engines using Slurm""" batch_file_name = Unicode( u'slurm_engine.sbatch', config=True, help="batch file name for the engine(s) job.", ) default_template = Unicode( """#!/bin/sh #SBATCH --job-name=ipy-engine-{cluster_id} srun {program_and_args} """ ) # SGE is very similar to PBS class SGELauncher(PBSLauncher): """Sun GridEngine is a PBS clone with slightly different syntax""" job_array_regexp = CRegExp(r'#\$\W+\-t') job_array_template = Unicode('#$ -t 1-{n}') queue_regexp = CRegExp(r'#\$\W+-q\W+\$?\w+') queue_template = Unicode('#$ -q {queue}') class SGEControllerLauncher(SGELauncher, BatchControllerLauncher): """Launch a controller using SGE.""" batch_file_name = Unicode( u'sge_controller', config=True, help="batch file name for the ipontroller job." ) default_template = Unicode( """#$ -V #$ -S /bin/sh #$ -N ipcontroller {program_and_args} """ ) class SGEEngineSetLauncher(SGELauncher, BatchEngineSetLauncher): """Launch Engines with SGE""" batch_file_name = Unicode( u'sge_engines', config=True, help="batch file name for the engine(s) job." ) default_template = Unicode( """#$ -V #$ -S /bin/sh #$ -N ipengine {program_and_args} """ ) # LSF launchers class LSFLauncher(BatchSystemLauncher): """A BatchSystemLauncher subclass for LSF.""" submit_command = List(['bsub'], config=True, help="The LSF submit command ['bsub']") delete_command = List( ['bkill'], config=True, help="The LSF delete command ['bkill']" ) signal_command = List( ['bkill', '-s'], config=True, help="The LSF signal command ['bkill', '-s']" ) job_id_regexp = CRegExp( r'\d+', config=True, help=r"Regular expresion for identifying the job ID [r'\d+']", ) batch_file = Unicode(u'') job_array_regexp = CRegExp(r'#BSUB[ \t]-J+\w+\[\d+-\d+\]') job_array_template = Unicode('#BSUB -J ipengine[1-{n}]') queue_regexp = CRegExp(r'#BSUB[ \t]+-q[ \t]+\w+') queue_template = Unicode('#BSUB -q {queue}') def start(self, n=1): """Start n copies of the process using LSF batch system. This cant inherit from the base class because bsub expects to be piped a shell script in order to honor the #BSUB directives : bsub < script """ # Here we save profile_dir in the context so they # can be used in the batch script template as {profile_dir} self.write_batch_script(n) piped_cmd = self.args[0] + '<\"' + self.args[1] + '\"' self.log.debug("Starting %s: %s", self.__class__.__name__, piped_cmd) p = Popen(piped_cmd, shell=True, env=os.environ, stdout=PIPE) output, err = p.communicate() output = output.decode("utf8", 'replace') job_id = self.parse_job_id(output) self.notify_start(job_id) return job_id class LSFControllerLauncher(LSFLauncher, BatchControllerLauncher): """Launch a controller using LSF.""" batch_file_name = Unicode( u'lsf_controller', config=True, help="batch file name for the controller job." ) default_template = Unicode( """#!/bin/sh #BSUB -J ipcontroller #BSUB -oo ipcontroller.o.%%J #BSUB -eo ipcontroller.e.%%J {program_and_args} """ ) class LSFEngineSetLauncher(LSFLauncher, BatchEngineSetLauncher): """Launch Engines using LSF""" batch_file_name = Unicode( u'lsf_engines', config=True, help="batch file name for the engine(s) job." ) default_template = Unicode( """#!/bin/sh #BSUB -oo ipengine.o.%%J #BSUB -eo ipengine.e.%%J {program_and_args} """ ) class HTCondorLauncher(BatchSystemLauncher): """A BatchSystemLauncher subclass for HTCondor. HTCondor requires that we launch the ipengine/ipcontroller scripts rather that the python instance but otherwise is very similar to PBS. This is because HTCondor destroys sys.executable when launching remote processes - a launched python process depends on sys.executable to effectively evaluate its module search paths. Without it, regardless of which python interpreter you launch you will get the to built in module search paths. We use the ip{cluster, engine, controller} scripts as our executable to circumvent this - the mechanism of shebanged scripts means that the python binary will be launched with argv[0] set to the *location of the ip{cluster, engine, controller} scripts on the remote node*. This means you need to take care that: a. Your remote nodes have their paths configured correctly, with the ipengine and ipcontroller of the python environment you wish to execute code in having top precedence. b. This functionality is untested on Windows. If you need different behavior, consider making you own template. """ submit_command = List( ['condor_submit'], config=True, help="The HTCondor submit command ['condor_submit']", ) delete_command = List( ['condor_rm'], config=True, help="The HTCondor delete command ['condor_rm']" ) job_id_regexp = CRegExp( r'(\d+)\.$', config=True, help=r"Regular expression for identifying the job ID [r'(\d+)\.$']", ) job_id_regexp_group = Integer( 1, config=True, help="""The group we wish to match in job_id_regexp [1]""" ) job_array_regexp = CRegExp(r'queue\W+\$') job_array_template = Unicode('queue {n}') def _insert_job_array_in_script(self): """Inserts a job array if required into the batch script.""" if not self.job_array_regexp.search(self.batch_template): self.log.debug("adding job array settings to batch script") # HTCondor requires that the job array goes at the bottom of the script self.batch_template = '\n'.join( [self.batch_template, self.job_array_template] ) def _insert_options_in_script(self): """AFAIK, HTCondor doesn't have a concept of multiple queues that can be specified in the script. """ pass class HTCondorControllerLauncher(HTCondorLauncher, BatchControllerLauncher): """Launch a controller using HTCondor.""" batch_file_name = Unicode( u'htcondor_controller', config=True, help="batch file name for the controller job.", ) default_template = Unicode( r""" universe = vanilla executable = ipcontroller # by default we expect a shared file system transfer_executable = False arguments = {program_args} """ ) class HTCondorEngineSetLauncher(HTCondorLauncher, BatchEngineSetLauncher): """Launch Engines using HTCondor""" batch_file_name = Unicode( u'htcondor_engines', config=True, help="batch file name for the engine(s) job." ) default_template = Unicode( """ universe = vanilla executable = ipengine # by default we expect a shared file system transfer_executable = False arguments = "{program_args}" """ ) # ----------------------------------------------------------------------------- # Collections of launchers # ----------------------------------------------------------------------------- local_launchers = [ LocalControllerLauncher, LocalEngineLauncher, LocalEngineSetLauncher, ] mpi_launchers = [ MPILauncher, MPIControllerLauncher, MPIEngineSetLauncher, ] ssh_launchers = [ SSHLauncher, SSHControllerLauncher, SSHEngineLauncher, SSHEngineSetLauncher, SSHProxyEngineSetLauncher, ] winhpc_launchers = [ WindowsHPCLauncher, WindowsHPCControllerLauncher, WindowsHPCEngineSetLauncher, ] pbs_launchers = [ PBSLauncher, PBSControllerLauncher, PBSEngineSetLauncher, ] slurm_launchers = [ SlurmLauncher, SlurmControllerLauncher, SlurmEngineSetLauncher, ] sge_launchers = [ SGELauncher, SGEControllerLauncher, SGEEngineSetLauncher, ] lsf_launchers = [ LSFLauncher, LSFControllerLauncher, LSFEngineSetLauncher, ] htcondor_launchers = [ HTCondorLauncher, HTCondorControllerLauncher, HTCondorEngineSetLauncher, ] all_launchers = ( local_launchers + mpi_launchers + ssh_launchers + winhpc_launchers + pbs_launchers + slurm_launchers + sge_launchers + lsf_launchers + htcondor_launchers ) def find_launcher_class(name, kind): """Return a launcher class for a given name and kind. Parameters ---------- name : str The full name of the launcher class, either with or without the module path, or an abbreviation (MPI, SSH, SGE, PBS, LSF, HTCondor Slurm, WindowsHPC). kind : str Either 'EngineSet' or 'Controller'. """ if kind == 'engine': group_name = 'ipyparallel.engine_launchers' elif kind == 'controller': group_name = 'ipyparallel.controller_launchers' else: raise ValueError(f"kind must be 'engine' or 'controller', not {kind!r}") group = entrypoints.get_group_named(group_name) # make it case-insensitive registry = {key.lower(): value for key, value in group.items()} return registry[name.lower()].load() @lru_cache() def abbreviate_launcher_class(cls): """Abbreviate a launcher class back to its entrypoint name""" cls_key = f"{cls.__module__}.{cls.__name__}" # allow entrypoint_name attribute in case the definition module # is not the same as the 'import' module if getattr(cls, 'entrypoint_name', None): return getattr(cls, 'entrypoint_name') for kind in ('controller', 'engine'): group_name = f'ipyparallel.{kind}_launchers' group = entrypoints.get_group_named(group_name) for key, value in group.items(): if f"{value.module_name}.{value.object_name}" == cls_key: return key.lower() return cls_key ipyparallel-7.1.0/ipyparallel/controller/000077500000000000000000000000001412533144600205415ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/controller/__init__.py000066400000000000000000000000001412533144600226400ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/controller/__main__.py000066400000000000000000000001011412533144600226230ustar00rootroot00000000000000if __name__ == '__main__': from .app import main main() ipyparallel-7.1.0/ipyparallel/controller/app.py000077500000000000000000001272641412533144600217120ustar00rootroot00000000000000#!/usr/bin/env python # encoding: utf-8 """ The IPython controller application. """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import with_statement import json import os import socket import stat import sys import time from multiprocessing import Process from signal import SIGABRT from signal import SIGINT from signal import signal from signal import SIGTERM import zmq from IPython.core.profiledir import ProfileDir from jupyter_client.localinterfaces import localhost from jupyter_client.session import Session from jupyter_client.session import session_aliases from jupyter_client.session import session_flags from traitlets import Bool from traitlets import Bytes from traitlets import default from traitlets import Dict from traitlets import import_item from traitlets import Instance from traitlets import Integer from traitlets import List from traitlets import observe from traitlets import Tuple from traitlets import Type from traitlets import Unicode from traitlets import Union from traitlets import validate from traitlets.config import Config from zmq.devices import ProcessMonitoredQueue from zmq.eventloop.zmqstream import ZMQStream from zmq.log.handlers import PUBHandler from .broadcast_scheduler import BroadcastScheduler from ipyparallel import util from ipyparallel.apps.baseapp import base_aliases from ipyparallel.apps.baseapp import base_flags from ipyparallel.apps.baseapp import BaseParallelApplication from ipyparallel.apps.baseapp import catch_config_error from ipyparallel.controller.broadcast_scheduler import launch_broadcast_scheduler from ipyparallel.controller.dictdb import DictDB from ipyparallel.controller.heartmonitor import HeartMonitor from ipyparallel.controller.heartmonitor import start_heartmonitor from ipyparallel.controller.hub import Hub from ipyparallel.controller.scheduler import launch_scheduler from ipyparallel.controller.task_scheduler import TaskScheduler from ipyparallel.traitlets import PortList from ipyparallel.util import disambiguate_url # conditional import of SQLiteDB / MongoDB backend class real_dbs = [] try: from ipyparallel.controller.sqlitedb import SQLiteDB except ImportError: pass else: real_dbs.append(SQLiteDB) try: from ipyparallel.controller.mongodb import MongoDB except ImportError: pass else: real_dbs.append(MongoDB) # ----------------------------------------------------------------------------- # Module level variables # ----------------------------------------------------------------------------- _description = """Start the IPython controller for parallel computing. The IPython controller provides a gateway between the IPython engines and clients. The controller needs to be started before the engines and can be configured using command line options or using a cluster directory. Cluster directories contain config, log and security files and are usually located in your ipython directory and named as "profile_name". See the `profile` and `profile-dir` options for details. """ _examples = """ ipcontroller --ip=192.168.0.1 --port=1000 # listen on ip, port for engines ipcontroller --scheme=pure # use the pure zeromq scheduler """ # ----------------------------------------------------------------------------- # The main application # ----------------------------------------------------------------------------- flags = {} flags.update(base_flags) flags.update( { 'usethreads': ( {'IPController': {'use_threads': True}}, 'Use threads instead of processes for the schedulers', ), 'sqlitedb': ( {'IPController': {'db_class': 'ipyparallel.controller.sqlitedb.SQLiteDB'}}, 'use the SQLiteDB backend', ), 'mongodb': ( {'IPController': {'db_class': 'ipyparallel.controller.mongodb.MongoDB'}}, 'use the MongoDB backend', ), 'dictdb': ( {'IPController': {'db_class': 'ipyparallel.controller.dictdb.DictDB'}}, 'use the in-memory DictDB backend', ), 'nodb': ( {'IPController': {'db_class': 'ipyparallel.controller.dictdb.NoDB'}}, """use dummy DB backend, which doesn't store any information. This is the default as of IPython 0.13. To enable delayed or repeated retrieval of results from the Hub, select one of the true db backends. """, ), 'reuse': ( {'IPController': {'reuse_files': True}}, 'reuse existing json connection files', ), 'restore': ( {'IPController': {'restore_engines': True, 'reuse_files': True}}, 'Attempt to restore engines from a JSON file. ' 'For use when resuming a crashed controller', ), } ) flags.update(session_flags) aliases = dict( ssh='IPController.ssh_server', enginessh='IPController.engine_ssh_server', location='IPController.location', ip='IPController.ip', transport='IPController.transport', port='IPController.regport', ports='IPController.ports', ping='HeartMonitor.period', scheme='TaskScheduler.scheme_name', hwm='TaskScheduler.hwm', ) aliases.update(base_aliases) aliases.update(session_aliases) _db_shortcuts = { 'sqlitedb': 'ipyparallel.controller.sqlitedb.SQLiteDB', 'mongodb': 'ipyparallel.controller.mongodb.MongoDB', 'dictdb': 'ipyparallel.controller.dictdb.DictDB', 'nodb': 'ipyparallel.controller.dictdb.NoDB', } class IPController(BaseParallelApplication): name = u'ipcontroller' description = _description examples = _examples classes = [ ProfileDir, Session, Hub, TaskScheduler, HeartMonitor, DictDB, ] + real_dbs _deprecated_classes = ("HubFactory", "IPControllerApp") # change default to True auto_create = Bool( True, config=True, help="""Whether to create profile dir if it doesn't exist.""" ) reuse_files = Bool( False, config=True, help="""Whether to reuse existing json connection files. If False, connection files will be removed on a clean exit. """, ) restore_engines = Bool( False, config=True, help="""Reload engine state from JSON file """, ) ssh_server = Unicode( u'', config=True, help="""ssh url for clients to use when connecting to the Controller processes. It should be of the form: [user@]server[:port]. The Controller's listening addresses must be accessible from the ssh server""", ) engine_ssh_server = Unicode( u'', config=True, help="""ssh url for engines to use when connecting to the Controller processes. It should be of the form: [user@]server[:port]. The Controller's listening addresses must be accessible from the ssh server""", ) location = Unicode( socket.gethostname(), config=True, help="""The external IP or domain name of the Controller, used for disambiguating engine and client connections.""", ) use_threads = Bool( False, config=True, help='Use threads instead of processes for the schedulers' ) engine_json_file = Unicode( 'ipcontroller-engine.json', config=True, help="JSON filename where engine connection info will be stored.", ) client_json_file = Unicode( 'ipcontroller-client.json', config=True, help="JSON filename where client connection info will be stored.", ) @observe('cluster_id') def _cluster_id_changed(self, change): base = 'ipcontroller' if change.new: base = f"{base}-{change.new}" self.engine_json_file = f"{base}-engine.json" self.client_json_file = f"{base}-client.json" enable_curve = Bool( False, config=True, help="""Enable CurveZMQ encryption and authentication Caution: known to have issues on platforms with getrandom """, ) @default("enable_curve") def _default_enable_curve(self): enabled = os.environ.get("IPP_ENABLE_CURVE", "") == "1" if enabled: self._ensure_curve_keypair() # disable redundant digest-key, CurveZMQ protects against replays if 'key' not in self.config.Session: self.config.Session.key = b'' return enabled @observe("enable_curve") def _enable_curve_changed(self, change): if change.new: self._ensure_curve_keypair() # disable redundant digest-key, CurveZMQ protects against replays if 'key' not in self.config.Session: self.config.Session.key = b'' def _ensure_curve_keypair(self): if not self.curve_secretkey or not self.curve_publickey: self.log.info("Generating new CURVE credentials") self.curve_publickey, self.curve_secretkey = zmq.curve_keypair() curve_secretkey = Bytes( config=True, help="The CurveZMQ secret key for the controller", ) curve_publickey = Bytes( config=True, help="""The CurveZMQ public key for the controller. Engines and clients use this for the server key. """, ) @default("curve_secretkey") def _default_curve_secretkey(self): return os.environ.get("IPP_CURVE_SECRETKEY", "").encode("ascii") @default("curve_publickey") def _default_curve_publickey(self): return os.environ.get("IPP_CURVE_PUBLICKEY", "").encode("ascii") @validate("curve_publickey", "curve_secretkey") def _cast_bytes(self, proposal): if isinstance(proposal.value, str): return proposal.value.encode("ascii") return proposal.value # internal children = List() mq_class = Unicode('zmq.devices.ProcessMonitoredQueue') @observe('use_threads') def _use_threads_changed(self, change): self.mq_class = 'zmq.devices.{}MonitoredQueue'.format( 'Thread' if change['new'] else 'Process' ) write_connection_files = Bool( True, help="""Whether to write connection files to disk. True in all cases other than runs with `reuse_files=True` *after the first* """, ) aliases = Dict(aliases) flags = Dict(flags) broadcast_scheduler_depth = Integer( 1, config=True, help="Depth of spanning tree schedulers", ) number_of_leaf_schedulers = Integer() number_of_broadcast_schedulers = Integer() number_of_non_leaf_schedulers = Integer() @default('number_of_leaf_schedulers') def get_number_of_leaf_schedulers(self): return 2 ** self.broadcast_scheduler_depth @default('number_of_broadcast_schedulers') def get_number_of_broadcast_schedulers(self): return 2 * self.number_of_leaf_schedulers - 1 @default('number_of_non_leaf_schedulers') def get_number_of_non_leaf_schedulers(self): return self.number_of_broadcast_schedulers - self.number_of_leaf_schedulers ports = PortList( Integer(min_value=1, max_value=65536), config=True, help=""" Pool of ports to use for the controller. For example: ipcontroller --ports 10101-10120 This list will be consumed to populate the ports to be used when binding controller sockets for engines, clients, or internal connections. The number of sockets needed depends on scheduler depth, but is at least 14 (16 by default) If more ports than are defined here are needed, random ports will be selected. Can be specified as a list or string expressing a range See also engine_ports and client_ports """, ) engine_ports = PortList( config=True, help=""" Pool of ports to use for engine connections This list will be consumed to populate the ports to be used when binding controller sockets used for engine connections Can be specified as a list or string expressing a range If this list is exhausted, the common `ports` pool will be consumed. See also ports and client_ports """, ) client_ports = PortList( config=True, help=""" Pool of ports to use for client connections This list will be consumed to populate the ports to be used when binding controller sockets used for client connections Can be specified as a list or string expressing a range If this list is empty or exhausted, the common `ports` pool will be consumed. See also ports and engine_ports """, ) # observe consumption of pools port_index = engine_port_index = client_port_index = 0 # port-pairs for schedulers: hb = Tuple( Integer(), Integer(), config=True, help="""DEPRECATED: use ports""", ) mux = Tuple( Integer(), Integer(), config=True, help="""DEPRECATED: use ports""", ) task = Tuple( Integer(), Integer(), config=True, help="""DEPRECATED: use ports""", ) control = Tuple( Integer(), Integer(), config=True, help="""DEPRECATED: use ports""", ) iopub = Tuple( Integer(), Integer(), config=True, help="""DEPRECATED: use ports""", ) @observe("iopub", "control", "task", "mux") def _scheduler_ports_assigned(self, change): self.log.warning( f"Setting {self.__class__.__name__}.{change.name} = {change.new!r} is deprecated in IPython Parallel 7." " Use IPController.ports config instead." ) self.client_ports.append(change.new[0]) self.engine_ports.append(change.new[0]) @observe("hb") def _hb_ports_assigned(self, change): self.log.warning( f"Setting {self.__class__.__name__}.{change.name} = {change.new!r} is deprecated in IPython Parallel 7." " Use IPController.engine_ports config instead." ) self.engine_ports.extend(change.new) # single ports: mon_port = Integer(config=True, help="""DEPRECATED: use ports""") notifier_port = Integer(config=True, help="""DEPRECATED: use ports""").tag( port_pool="client" ) regport = Integer(config=True, help="DEPRECATED: use ports").tag(port_pool="engine") @observe("regport", "notifier_port", "mon_port") def _port_assigned(self, change): self.log.warning( f"Setting {self.__class__.__name__}.{change.name} = {change.new!r} is deprecated in IPython Parallel 7." " Use IPController.ports config instead." ) trait = self.traits()[change.name] pool_name = trait.metadata.get("port_pool") if pool_name == 'engine': self.engine_ports.append(change.new) elif pool_name == 'client': self.client_ports.append(change.new) else: self.ports.append(change.new) engine_ip = Unicode( config=True, help="IP on which to listen for engine connections. [default: loopback]", ) def _engine_ip_default(self): return localhost() engine_transport = Unicode( 'tcp', config=True, help="0MQ transport for engine connections. [default: tcp]" ) client_ip = Unicode( config=True, help="IP on which to listen for client connections. [default: loopback]", ) client_transport = Unicode( 'tcp', config=True, help="0MQ transport for client connections. [default : tcp]" ) monitor_ip = Unicode( config=True, help="IP on which to listen for monitor messages. [default: loopback]", ) monitor_transport = Unicode( 'tcp', config=True, help="0MQ transport for monitor messages. [default : tcp]" ) _client_ip_default = _monitor_ip_default = _engine_ip_default monitor_url = Unicode('') db_class = Union( [Unicode(), Type()], default_value=DictDB, config=True, help="""The class to use for the DB backend Options include: SQLiteDB: SQLite MongoDB : use MongoDB DictDB : in-memory storage (fastest, but be mindful of memory growth of the Hub) NoDB : disable database altogether (default) """, ) @validate("db_class") def _validate_db_class(self, proposal): value = proposal.value if isinstance(value, str): # if it's a string, import it value = _db_shortcuts.get(value.lower(), value) return import_item(value) return value registration_timeout = Integer( 0, config=True, help="Engine registration timeout in seconds [default: max(30," "10*heartmonitor.period)]", ) @default("registration_timeout") def _registration_timeout_default(self): # heartmonitor period is in milliseconds, so 10x in seconds is .01 return max(30, int(0.01 * HeartMonitor(parent=self).period)) # not configurable db = Instance('ipyparallel.controller.dictdb.BaseDB', allow_none=True) heartmonitor = Instance( 'ipyparallel.controller.heartmonitor.HeartMonitor', allow_none=True ) ip = Unicode( "127.0.0.1", config=True, help="""Set the controller ip for all connections.""" ) transport = Unicode( "tcp", config=True, help="""Set the zmq transport for all connections.""" ) @observe('ip') def _ip_changed(self, change): new = change['new'] self.engine_ip = new self.client_ip = new self.monitor_ip = new @observe('transport') def _transport_changed(self, change): new = change['new'] self.engine_transport = new self.client_transport = new self.monitor_transport = new context = Instance(zmq.Context) @default("context") def _defaut_context(self): return zmq.Context.instance() # connection file contents engine_info = Dict() client_info = Dict() _logged_exhaustion = Dict() _random_port_count = Integer(0) def next_port(self, pool_name='common'): """Consume a port from our port pools""" if pool_name == 'client': if len(self.client_ports) > self.client_port_index: port = self.client_ports[self.client_port_index] self.client_port_index += 1 return port elif self.client_ports and not self._logged_exhaustion.get("client"): self._logged_exhaustion['client'] = True # only log once self.log.warning(f"Exhausted {len(self.client_ports)} client ports") elif pool_name == 'engine': if len(self.engine_ports) > self.engine_port_index: port = self.engine_ports[self.engine_port_index] self.engine_port_index += 1 return port elif self.engine_ports and not self._logged_exhaustion.get("engine"): self._logged_exhaustion['engine'] = True self.log.warning(f"Exhausted {len(self.engine_ports)} engine ports") # drawing from common pool if len(self.ports) > self.port_index: port = self.ports[self.port_index] self.port_index += 1 return port elif self.ports and not self._logged_exhaustion.get("common"): self._logged_exhaustion['common'] = True self.log.warning(f"Exhausted {len(self.ports)} common ports") self._random_port_count += 1 port = util.select_random_ports(1)[0] return port def construct_url(self, kind: str, channel: str, index=None): if kind == 'engine': info = self.engine_info elif kind == 'client': info = self.client_info elif kind == 'internal': info = self.internal_info else: raise ValueError( "kind must be 'engine', 'client', or 'internal', not {kind!r}" ) interface = info['interface'] sep = '-' if interface.partition("://")[0] == 'ipc' else ':' port = info[channel] if index is not None: port = port[index] return f"{interface}{sep}{port}" def internal_url(self, channel, index=None): """return full zmq url for a named internal channel""" return self.construct_url('internal', channel, index) def bind(self, socket, url): """Bind a socket""" return util.bind( socket, url, curve_secretkey=self.curve_secretkey if self.enable_curve else None, curve_publickey=self.curve_publickey if self.enable_curve else None, ) def connect(self, socket, url): return util.connect( socket, url, curve_serverkey=self.curve_publickey if self.enable_curve else None, curve_publickey=self.curve_publickey if self.enable_curve else None, curve_secretkey=self.curve_secretkey if self.enable_curve else None, ) def client_url(self, channel, index=None): """return full zmq url for a named client channel""" return self.construct_url('client', channel, index) def engine_url(self, channel, index=None): """return full zmq url for a named engine channel""" return self.construct_url('engine', channel, index) def save_connection_dict(self, fname, cdict): """save a connection dict to json file.""" fname = os.path.join(self.profile_dir.security_dir, fname) self.log.info("writing connection info to %s", fname) with open(fname, 'w') as f: f.write(json.dumps(cdict, indent=2)) os.chmod(fname, stat.S_IRUSR | stat.S_IWUSR) def load_config_from_json(self): """load config from existing json connector files.""" c = self.config self.log.debug("loading config from JSON") # load engine config fname = os.path.join(self.profile_dir.security_dir, self.engine_json_file) self.log.info("loading connection info from %s", fname) with open(fname) as f: ecfg = json.loads(f.read()) # json gives unicode, Session.key wants bytes c.Session.key = ecfg['key'].encode('ascii') xport, ip = ecfg['interface'].split('://') c.IPController.engine_ip = ip c.IPController.engine_transport = xport self.location = ecfg['location'] if not self.engine_ssh_server: self.engine_ssh_server = ecfg['ssh'] # load client config fname = os.path.join(self.profile_dir.security_dir, self.client_json_file) self.log.info("loading connection info from %s", fname) with open(fname) as f: ccfg = json.loads(f.read()) for key in ('key', 'registration', 'pack', 'unpack', 'signature_scheme'): assert ccfg[key] == ecfg[key], ( "mismatch between engine and client info: %r" % key ) xport, ip = ccfg['interface'].split('://') c.IPController.client_transport = xport c.IPController.client_ip = ip if not self.ssh_server: self.ssh_server = ccfg['ssh'] self.engine_info = ecfg self.client_info = ccfg def cleanup_connection_files(self): if self.reuse_files: self.log.debug("leaving JSON connection files for reuse") return self.log.debug("cleaning up JSON connection files") for f in (self.client_json_file, self.engine_json_file): f = os.path.join(self.profile_dir.security_dir, f) try: os.remove(f) except Exception as e: self.log.error("Failed to cleanup connection file: %s", e) else: self.log.debug(u"removed %s", f) def load_secondary_config(self): """secondary config, loading from JSON and setting defaults""" if self.reuse_files: try: self.load_config_from_json() except (AssertionError, IOError) as e: self.log.error("Could not load config from JSON: %s" % e) else: # successfully loaded config from JSON, and reuse=True # no need to write back the same file self.write_connection_files = False def init_hub(self): if self.enable_curve: self.log.info( "Using CURVE security. Ignore warnings about disabled message signing." ) c = self.config ctx = self.context loop = self.loop if 'TaskScheduler.scheme_name' in self.config: scheme = self.config.TaskScheduler.scheme_name else: from .task_scheduler import TaskScheduler scheme = TaskScheduler.scheme_name.default_value if self.engine_info: registration_port = self.engine_info['registration'] else: registration_port = self.next_port('engine') # build connection dicts if not self.engine_info: self.engine_info = { 'interface': "%s://%s" % (self.engine_transport, self.engine_ip), 'registration': registration_port, 'control': self.next_port('engine'), 'mux': self.next_port('engine'), 'task': self.next_port('engine'), 'iopub': self.next_port('engine'), 'hb_ping': self.next_port('engine'), 'hb_pong': self.next_port('engine'), BroadcastScheduler.port_name: [ self.next_port('engine') for i in range(self.number_of_leaf_schedulers) ], } if not self.client_info: self.client_info = { 'interface': "%s://%s" % (self.client_transport, self.client_ip), 'registration': registration_port, 'control': self.next_port('client'), 'mux': self.next_port('client'), 'task': self.next_port('client'), 'task_scheme': scheme, 'iopub': self.next_port('client'), 'notification': self.next_port('client'), BroadcastScheduler.port_name: self.next_port('client'), } if self.engine_transport == 'tcp': internal_interface = "tcp://127.0.0.1" else: internal_interface = self.engine_info['interface'] broadcast_ids = [] # '0', '00', '01', '001', etc. # always a leading 0 for the root node for d in range(1, self.broadcast_scheduler_depth + 1): for i in range(2 ** d): broadcast_ids.append(format(i, f"0{d + 1}b")) self.internal_info = { 'interface': internal_interface, BroadcastScheduler.port_name: { broadcast_id: self.next_port() for broadcast_id in broadcast_ids }, } mon_port = self.next_port() self.monitor_url = f"{self.monitor_transport}://{self.monitor_ip}:{mon_port}" # debug port pool consumption if self.engine_ports: self.log.debug( f"Used {self.engine_port_index} / {len(self.engine_ports)} engine ports" ) if self.client_ports: self.log.debug( f"Used {self.client_port_index} / {len(self.client_ports)} client ports" ) if self.ports: self.log.debug(f"Used {self.port_index} / {len(self.ports)} common ports") if self._random_port_count: self.log.debug(f"Used {self._random_port_count} random ports") self.log.debug("Hub engine addrs: %s", self.engine_info) self.log.debug("Hub client addrs: %s", self.client_info) self.log.debug("Hub internal addrs: %s", self.internal_info) # Registrar socket query = ZMQStream(ctx.socket(zmq.ROUTER), loop) util.set_hwm(query, 0) self.bind(query, self.client_url('registration')) self.log.info( "Hub listening on %s for registration.", self.client_url('registration') ) if self.client_ip != self.engine_ip: self.bind(query, self.engine_url('registration')) self.log.info( "Hub listening on %s for registration.", self.engine_url('registration') ) ### Engine connections ### # heartbeat hm_config = Config() for key in ("Session", "HeartMonitor"): if key in self.config: hm_config[key] = self.config[key] hm_config.Session.key = self.session.key self.heartmonitor_process = Process( target=start_heartmonitor, kwargs=dict( ping_url=self.engine_url('hb_ping'), pong_url=self.engine_url('hb_pong'), monitor_url=disambiguate_url(self.monitor_url), config=hm_config, log_level=self.log.getEffectiveLevel(), curve_publickey=self.curve_publickey, curve_secretkey=self.curve_secretkey, ), daemon=True, ) ### Client connections ### # Notifier socket notifier = ZMQStream(ctx.socket(zmq.PUB), loop) notifier.socket.SNDHWM = 0 self.bind(notifier, self.client_url('notification')) ### build and launch the queues ### # monitor socket sub = ctx.socket(zmq.SUB) sub.RCVHWM = 0 sub.setsockopt(zmq.SUBSCRIBE, b"") self.bind(sub, self.monitor_url) # self.bind(sub, 'inproc://monitor') sub = ZMQStream(sub, loop) # connect the db db_class = self.db_class self.log.info(f'Hub using DB backend: {self.db_class.__name__}') self.db = self.db_class(session=self.session.session, parent=self, log=self.log) time.sleep(0.25) # resubmit stream resubmit = ZMQStream(ctx.socket(zmq.DEALER), loop) url = util.disambiguate_url(self.client_url('task')) self.connect(resubmit, url) self.hub = Hub( loop=loop, session=self.session, monitor=sub, query=query, notifier=notifier, resubmit=resubmit, db=self.db, heartmonitor_period=HeartMonitor(parent=self).period, engine_info=self.engine_info, client_info=self.client_info, log=self.log, registration_timeout=self.registration_timeout, parent=self, ) if self.write_connection_files: # save to new json config files base = { 'key': self.session.key.decode('ascii'), 'curve_serverkey': self.curve_publickey.decode("ascii") if self.enable_curve else None, 'location': self.location, 'pack': self.session.packer, 'unpack': self.session.unpacker, 'signature_scheme': self.session.signature_scheme, } cdict = {'ssh': self.ssh_server} cdict.update(self.client_info) cdict.update(base) self.save_connection_dict(self.client_json_file, cdict) edict = {'ssh': self.engine_ssh_server} edict.update(self.engine_info) edict.update(base) self.save_connection_dict(self.engine_json_file, edict) fname = "engines%s.json" % self.cluster_id self.hub.engine_state_file = os.path.join(self.profile_dir.log_dir, fname) if self.restore_engines: self.hub._load_engine_state() def launch_python_scheduler(self, name, scheduler_args, children): if 'Process' in self.mq_class: # run the Python scheduler in a Process q = Process( target=launch_scheduler, kwargs=scheduler_args, name=name, daemon=True, ) children.append(q) else: # single-threaded Controller scheduler_args['in_thread'] = True launch_scheduler(**scheduler_args) def get_python_scheduler_args( self, scheduler_name, scheduler_class, monitor_url, identity=None, in_addr=None, out_addr=None, ): if identity is not None: logname = f"{scheduler_name}-{identity}" else: logname = scheduler_name return { 'scheduler_class': scheduler_class, 'in_addr': in_addr or self.client_url(scheduler_name), 'out_addr': out_addr or self.engine_url(scheduler_name), 'mon_addr': monitor_url, 'not_addr': disambiguate_url(self.client_url('notification')), 'reg_addr': disambiguate_url(self.client_url('registration')), 'identity': identity if identity is not None else bytes(scheduler_name, 'utf8'), 'logname': logname, 'loglevel': self.log_level, 'log_url': self.log_url, 'config': dict(self.config), 'curve_secretkey': self.curve_secretkey if self.enable_curve else None, 'curve_publickey': self.curve_publickey if self.enable_curve else None, } def launch_broadcast_schedulers(self, monitor_url, children): def launch_in_thread_or_process(scheduler_args, depth, identity): if 'Process' in self.mq_class: # run the Python scheduler in a Process q = Process( target=launch_broadcast_scheduler, kwargs=scheduler_args, name=f"BroadcastScheduler(depth={depth}, id={identity})", daemon=True, ) children.append(q) else: # single-threaded Controller scheduler_args['in_thread'] = True launch_broadcast_scheduler(**scheduler_args) def recursively_start_schedulers(identity, depth): outgoing_id1 = identity + '0' outgoing_id2 = identity + '1' is_leaf = depth == self.broadcast_scheduler_depth is_root = depth == 0 # FIXME: use localhost, not client ip for internal communication # this will still be localhost anyway for the most common cases # of localhost or */0.0.0.0 if is_root: in_addr = self.client_url(BroadcastScheduler.port_name) else: # not root, use internal address in_addr = self.internal_url( BroadcastScheduler.port_name, index=identity, ) scheduler_args = self.get_python_scheduler_args( BroadcastScheduler.port_name, BroadcastScheduler, monitor_url, identity, in_addr=in_addr, out_addr='ignored', ) scheduler_args.pop('out_addr') # add broadcast args scheduler_args.update( outgoing_ids=[outgoing_id1, outgoing_id2], depth=depth, max_depth=self.broadcast_scheduler_depth, is_leaf=is_leaf, ) if is_leaf: scheduler_args.update( out_addrs=[ self.engine_url( BroadcastScheduler.port_name, index=int(identity, 2), ) ], ) else: scheduler_args.update( out_addrs=[ self.internal_url( BroadcastScheduler.port_name, index=outgoing_id1 ), self.internal_url( BroadcastScheduler.port_name, index=outgoing_id2 ), ] ) launch_in_thread_or_process(scheduler_args, depth=depth, identity=identity) if not is_leaf: recursively_start_schedulers(outgoing_id1, depth + 1) recursively_start_schedulers(outgoing_id2, depth + 1) recursively_start_schedulers(identity='0', depth=0) def init_schedulers(self): children = self.children mq = import_item(str(self.mq_class)) # ensure session key is shared across sessions self.config.Session.key = self.session.key ident = self.session.bsession def add_auth(q): """Add CURVE auth to a monitored queue""" if not self.enable_curve: return False q.setsockopt_in(zmq.CURVE_SERVER, 1) q.setsockopt_in(zmq.CURVE_SECRETKEY, self.curve_secretkey) q.setsockopt_out(zmq.CURVE_SERVER, 1) q.setsockopt_out(zmq.CURVE_SECRETKEY, self.curve_secretkey) # monitor is a client pub, secret = zmq.curve_keypair() q.setsockopt_mon(zmq.CURVE_SERVERKEY, self.curve_publickey) q.setsockopt_mon(zmq.CURVE_SECRETKEY, secret) q.setsockopt_mon(zmq.CURVE_PUBLICKEY, pub) # disambiguate url, in case of * monitor_url = disambiguate_url(self.monitor_url) # maybe_inproc = 'inproc://monitor' if self.use_threads else monitor_url # IOPub relay (in a Process) q = mq(zmq.SUB, zmq.PUB, zmq.PUB, b'iopub', b'N/A') add_auth(q) q.name = "IOPubScheduler" q.bind_in(self.engine_url('iopub')) q.setsockopt_in(zmq.SUBSCRIBE, b'') q.bind_out(self.client_url('iopub')) q.setsockopt_out(zmq.IDENTITY, ident + b"_iopub") q.connect_mon(monitor_url) q.daemon = True children.append(q) # Multiplexer Queue (in a Process) q = mq(zmq.ROUTER, zmq.ROUTER, zmq.PUB, b'in', b'out') add_auth(q) q.name = "DirectScheduler" q.bind_in(self.client_url('mux')) q.setsockopt_in(zmq.IDENTITY, b'mux_in') q.bind_out(self.engine_url('mux')) q.setsockopt_out(zmq.IDENTITY, b'mux_out') q.connect_mon(monitor_url) q.daemon = True children.append(q) # Control Queue (in a Process) q = mq(zmq.ROUTER, zmq.ROUTER, zmq.PUB, b'incontrol', b'outcontrol') add_auth(q) q.name = "ControlScheduler" q.bind_in(self.client_url('control')) q.setsockopt_in(zmq.IDENTITY, b'control_in') q.bind_out(self.engine_url('control')) q.setsockopt_out(zmq.IDENTITY, b'control_out') q.connect_mon(monitor_url) q.daemon = True children.append(q) if 'TaskScheduler.scheme_name' in self.config: scheme = self.config.TaskScheduler.scheme_name else: scheme = TaskScheduler.scheme_name.default_value # Task Queue (in a Process) if scheme == 'pure': self.log.warning("task::using pure DEALER Task scheduler") q = mq(zmq.ROUTER, zmq.DEALER, zmq.PUB, b'intask', b'outtask') add_auth(q) q.name = "TaskScheduler(pure)" # q.setsockopt_out(zmq.HWM, hub.hwm) q.bind_in(self.client_url('task')) q.setsockopt_in(zmq.IDENTITY, b'task_in') q.bind_out(self.engine_url('task')) q.setsockopt_out(zmq.IDENTITY, b'task_out') q.connect_mon(monitor_url) q.daemon = True children.append(q) elif scheme == 'none': self.log.warning("task::using no Task scheduler") else: self.log.info("task::using Python %s Task scheduler" % scheme) self.launch_python_scheduler( 'TaskScheduler', self.get_python_scheduler_args('task', TaskScheduler, monitor_url), children, ) self.launch_broadcast_schedulers(monitor_url, children) # set unlimited HWM for all relay devices if hasattr(zmq, 'SNDHWM'): q = children[0] q.setsockopt_in(zmq.RCVHWM, 0) q.setsockopt_out(zmq.SNDHWM, 0) for q in children[1:]: if not hasattr(q, 'setsockopt_in'): continue q.setsockopt_in(zmq.SNDHWM, 0) q.setsockopt_in(zmq.RCVHWM, 0) q.setsockopt_out(zmq.SNDHWM, 0) q.setsockopt_out(zmq.RCVHWM, 0) q.setsockopt_mon(zmq.SNDHWM, 0) def terminate_children(self): child_procs = [] for child in self.children + [self.heartmonitor_process]: if isinstance(child, ProcessMonitoredQueue): child_procs.append(child.launcher) elif isinstance(child, Process): child_procs.append(child) if child_procs: self.log.critical("terminating children...") for child in child_procs: try: child.terminate() except OSError: # already dead pass def handle_signal(self, sig, frame): self.log.critical("Received signal %i, shutting down", sig) self.terminate_children() self.loop.add_callback_from_signal(self.loop.stop) def init_signal(self): for sig in (SIGINT, SIGABRT, SIGTERM): signal(sig, self.handle_signal) def forward_logging(self): if self.log_url: self.log.info("Forwarding logging to %s" % self.log_url) context = zmq.Context.instance() lsock = context.socket(zmq.PUB) lsock.connect(self.log_url) handler = PUBHandler(lsock) handler.root_topic = 'controller' handler.setLevel(self.log_level) self.log.addHandler(handler) @catch_config_error def initialize(self, argv=None): super(IPController, self).initialize(argv) self.forward_logging() self.load_secondary_config() self.init_hub() self.init_schedulers() def start(self): # Start the subprocesses: # children must be started before signals are setup, # otherwise signal-handling will fire multiple times for child in self.children: child.start() if hasattr(child, 'launcher'): # apply name to actual process/thread for logging setattr(child.launcher, 'name', child.name) if not self.use_threads: process = getattr(child, 'launcher', child) self.log.debug(f"Started process {child.name}: {process.pid}") else: self.log.debug(f"Started thread {child.name}") self.heartmonitor_process.start() self.log.info(f"Heartmonitor beating every {self.hub.heartmonitor_period}ms") self.init_signal() try: self.loop.start() except KeyboardInterrupt: self.log.critical("Interrupted, Exiting...\n") finally: self.loop.close(all_fds=True) self.cleanup_connection_files() def main(*args, **kwargs): """Create and run the IPython controller""" if sys.platform == 'win32': # make sure we don't get called from a multiprocessing subprocess # this can result in infinite Controllers being started on Windows # which doesn't have a proper fork, so multiprocessing is wonky # this only comes up when IPython has been installed using vanilla # setuptools, and *not* distribute. import multiprocessing p = multiprocessing.current_process() # the main process has name 'MainProcess' # subprocesses will have names like 'Process-1' if p.name != 'MainProcess': # we are a subprocess, don't start another Controller! return return IPController.launch_instance(*args, **kwargs) if __name__ == '__main__': main() ipyparallel-7.1.0/ipyparallel/controller/broadcast_scheduler.py000066400000000000000000000214211412533144600251130ustar00rootroot00000000000000import logging import zmq from traitlets import Bool from traitlets import Bytes from traitlets import Integer from traitlets import List from traitlets import Unicode from ipyparallel import util from ipyparallel.controller.scheduler import get_common_scheduler_streams from ipyparallel.controller.scheduler import Scheduler from ipyparallel.controller.scheduler import ZMQStream class BroadcastScheduler(Scheduler): port_name = 'broadcast' accumulated_replies = {} accumulated_targets = {} is_leaf = Bool(False) connected_sub_scheduler_ids = List(Bytes()) outgoing_streams = List() depth = Integer() max_depth = Integer() name = Unicode() def start(self): self.client_stream.on_recv(self.dispatch_submission, copy=False) if self.is_leaf: super().start() else: for outgoing_stream in self.outgoing_streams: outgoing_stream.on_recv(self.dispatch_result, copy=False) self.log.info(f"BroadcastScheduler {self.name} started") def send_to_targets(self, msg, original_msg_id, targets, idents, is_coalescing): if is_coalescing: self.accumulated_replies[original_msg_id] = { target.encode('utf8'): None for target in targets } self.accumulated_targets[original_msg_id] = targets for target in targets: new_msg = self.append_new_msg_id_to_msg( self.get_new_msg_id(original_msg_id, target), target, idents, msg ) self.engine_stream.send_multipart(new_msg, copy=False) def send_to_sub_schedulers( self, msg, original_msg_id, targets, idents, is_coalescing ): trunc = 2 ** self.max_depth fmt = f"0{self.max_depth + 1}b" # assign targets to sub-schedulers based on binary path # compute binary '010110' representation of the engine id targets_by_scheduler = [ [] for i in range(len(self.connected_sub_scheduler_ids)) ] for target_tuple in targets: path = format(target_tuple[1] % trunc, fmt) next_idx = int(path[self.depth + 1]) # 0 or 1 targets_by_scheduler[next_idx].append(target_tuple) if is_coalescing: self.accumulated_replies[original_msg_id] = { scheduler_id: None for scheduler_id in self.connected_sub_scheduler_ids } self.accumulated_targets[original_msg_id] = {} for i, scheduler_id in enumerate(self.connected_sub_scheduler_ids): targets_for_scheduler = targets_by_scheduler[i] if is_coalescing: if targets_for_scheduler: self.accumulated_targets[original_msg_id][ scheduler_id ] = targets_for_scheduler else: del self.accumulated_replies[original_msg_id][scheduler_id] msg['metadata']['targets'] = targets_for_scheduler new_msg = self.append_new_msg_id_to_msg( self.get_new_msg_id(original_msg_id, scheduler_id), scheduler_id, idents, msg, ) self.outgoing_streams[i].send_multipart(new_msg, copy=False) def coalescing_reply(self, raw_msg, msg, original_msg_id, outgoing_id, idents): # accumulate buffers self.accumulated_replies[original_msg_id][outgoing_id] = msg['buffers'] if all( msg_buffers is not None for msg_buffers in self.accumulated_replies[original_msg_id].values() ): replies = self.accumulated_replies.pop(original_msg_id) self.log.debug(f"Coalescing {len(replies)} reply to {original_msg_id}") targets = self.accumulated_targets.pop(original_msg_id) new_msg = msg.copy() # begin rebuilding message # metadata['targets'] if self.is_leaf: new_msg['metadata']['broadcast_targets'] = targets else: new_msg['metadata']['broadcast_targets'] = [] # avoid duplicated msg buffers buffers = [] for sub_target, msg_buffers in replies.items(): buffers.extend(msg_buffers) if not self.is_leaf: new_msg['metadata']['broadcast_targets'].extend(targets[sub_target]) new_raw_msg = self.session.serialize(new_msg) self.client_stream.send_multipart( idents + new_raw_msg + buffers, copy=False ) @util.log_errors def dispatch_submission(self, raw_msg): try: idents, msg_list = self.session.feed_identities(raw_msg, copy=False) msg = self.session.deserialize(msg_list, content=False, copy=False) except: self.log.error( f'broadcast::Invalid broadcast msg: {raw_msg}', exc_info=True ) return metadata = msg['metadata'] msg_id = msg['header']['msg_id'] targets = metadata['targets'] is_coalescing = metadata['is_coalescing'] if 'original_msg_id' not in metadata: metadata['original_msg_id'] = msg_id original_msg_id = metadata['original_msg_id'] if self.is_leaf: target_idents = [t[0] for t in targets] self.send_to_targets( msg, original_msg_id, target_idents, idents, is_coalescing ) else: self.send_to_sub_schedulers( msg, original_msg_id, targets, idents, is_coalescing ) @util.log_errors def dispatch_result(self, raw_msg): try: idents, msg = self.session.feed_identities(raw_msg, copy=False) msg = self.session.deserialize(msg, content=False, copy=False) outgoing_id = idents[0] except Exception: self.log.error( f'broadcast::Invalid broadcast msg: {raw_msg}', exc_info=True ) return original_msg_id = msg['metadata']['original_msg_id'] is_coalescing = msg['metadata']['is_coalescing'] if is_coalescing: self.coalescing_reply( raw_msg, msg, original_msg_id, outgoing_id, idents[1:] ) else: self.client_stream.send_multipart(raw_msg[1:], copy=False) def get_id_with_prefix(identity): return bytes(f'sub_scheduler_{identity}', 'utf8') def launch_broadcast_scheduler( in_addr, out_addrs, mon_addr, not_addr, reg_addr, identity, config=None, loglevel=logging.DEBUG, log_url=None, is_leaf=False, in_thread=False, outgoing_ids=None, curve_publickey=None, curve_secretkey=None, depth=0, max_depth=0, scheduler_class=BroadcastScheduler, logname='broadcast', ): config, ctx, loop, mons, nots, querys, log = get_common_scheduler_streams( mon_addr, not_addr, reg_addr, config, logname, log_url, loglevel, in_thread, curve_serverkey=curve_publickey, curve_publickey=curve_publickey, curve_secretkey=curve_secretkey, ) is_root = depth == 0 sub_scheduler_id = get_id_with_prefix(identity) incoming_stream = ZMQStream(ctx.socket(zmq.ROUTER), loop) util.set_hwm(incoming_stream, 0) incoming_stream.setsockopt(zmq.IDENTITY, sub_scheduler_id) if is_root: util.bind(incoming_stream, in_addr, curve_secretkey=curve_secretkey) else: util.connect( incoming_stream, in_addr, curve_serverkey=curve_publickey, curve_publickey=curve_publickey, curve_secretkey=curve_secretkey, ) outgoing_streams = [] for out_addr in out_addrs: out = ZMQStream(ctx.socket(zmq.ROUTER), loop) util.set_hwm(out, 0) out.setsockopt(zmq.IDENTITY, sub_scheduler_id) util.bind(out, out_addr, curve_secretkey=curve_secretkey) outgoing_streams.append(out) scheduler_args = dict( client_stream=incoming_stream, mon_stream=mons, notifier_stream=nots, query_stream=querys, loop=loop, log=log, config=config, depth=depth, max_depth=max_depth, name=identity, ) if is_leaf: scheduler_args.update(engine_stream=outgoing_streams[0], is_leaf=True) else: scheduler_args.update( connected_sub_scheduler_ids=[ get_id_with_prefix(identity) for identity in outgoing_ids ], outgoing_streams=outgoing_streams, ) scheduler = scheduler_class(**scheduler_args) scheduler.start() if not in_thread: try: loop.start() except KeyboardInterrupt: scheduler.log.critical("Interrupted, exiting...") ipyparallel-7.1.0/ipyparallel/controller/dependency.py000066400000000000000000000147101412533144600232340ustar00rootroot00000000000000"""Dependency utilities """ from types import ModuleType from ipyparallel.client.asyncresult import AsyncResult from ipyparallel.error import UnmetDependency from ipyparallel.serialize import can from ipyparallel.util import interactive class depend: """Dependency decorator, for use with tasks. `@depend` lets you define a function for engine dependencies just like you use `apply` for tasks. Examples -------- :: @depend(df, a,b, c=5) def f(m,n,p) view.apply(f, 1,2,3) will call df(a,b,c=5) on the engine, and if it returns False or raises an UnmetDependency error, then the task will not be run and another engine will be tried. """ def __init__(self, _wrapped_f, *args, **kwargs): self.f = _wrapped_f self.args = args self.kwargs = kwargs def __call__(self, f): return dependent(f, self.f, *self.args, **self.kwargs) class dependent: """A function that depends on another function. This is an object to prevent the closure used in traditional decorators, which are not picklable. """ def __init__(self, _wrapped_f, _wrapped_df, *dargs, **dkwargs): self.f = _wrapped_f name = getattr(_wrapped_f, '__name__', 'f') self.__name__ = name self.df = _wrapped_df self.dargs = dargs self.dkwargs = dkwargs def check_dependency(self): if self.df(*self.dargs, **self.dkwargs) is False: raise UnmetDependency() def __call__(self, *args, **kwargs): return self.f(*args, **kwargs) @interactive def _require(*modules, **mapping): """Helper for @require decorator.""" from ipyparallel.error import UnmetDependency from ipyparallel.serialize import uncan user_ns = globals() for name in modules: try: exec('import %s' % name, user_ns) except ImportError: raise UnmetDependency(name) for name, cobj in mapping.items(): user_ns[name] = uncan(cobj, user_ns) return True def require(*objects, **mapping): """Simple decorator for requiring local objects and modules to be available when the decorated function is called on the engine. Modules specified by name or passed directly will be imported prior to calling the decorated function. Objects other than modules will be pushed as a part of the task. Functions can be passed positionally, and will be pushed to the engine with their __name__. Other objects can be passed by keyword arg. Examples -------- :: In [1]: @ipp.require('numpy') ...: def norm(a): ...: return numpy.linalg.norm(a,2) :: In [2]: foo = lambda x: x*x In [3]: @ipp.require(foo) ...: def bar(a): ...: return foo(1-a) """ names = [] for obj in objects: if isinstance(obj, ModuleType): obj = obj.__name__ if isinstance(obj, str): names.append(obj) elif hasattr(obj, '__name__'): mapping[obj.__name__] = obj else: raise TypeError( "Objects other than modules and functions " "must be passed by kwarg, but got: %s" % type(obj) ) for name, obj in mapping.items(): mapping[name] = can(obj) return depend(_require, *names, **mapping) class Dependency(set): """An object for representing a set of msg_id dependencies. Subclassed from set(). Parameters ---------- dependencies: list/set of msg_ids or AsyncResult objects or output of Dependency.as_dict() The msg_ids to depend on all : bool [default True] Whether the dependency should be considered met when *all* depending tasks have completed or only when *any* have been completed. success : bool [default True] Whether to consider successes as fulfilling dependencies. failure : bool [default False] Whether to consider failures as fulfilling dependencies. If `all=success=True` and `failure=False`, then the task will fail with an ImpossibleDependency as soon as the first depended-upon task fails. """ all = True success = True failure = True def __init__(self, dependencies=[], all=True, success=True, failure=False): if isinstance(dependencies, dict): # load from dict all = dependencies.get('all', True) success = dependencies.get('success', success) failure = dependencies.get('failure', failure) dependencies = dependencies.get('dependencies', []) ids = [] # extract ids from various sources: if isinstance(dependencies, (str, AsyncResult)): dependencies = [dependencies] for d in dependencies: if isinstance(d, str): ids.append(d) elif isinstance(d, AsyncResult): ids.extend(d.msg_ids) else: raise TypeError("invalid dependency type: %r" % type(d)) set.__init__(self, ids) self.all = all if not (success or failure): raise ValueError("Must depend on at least one of successes or failures!") self.success = success self.failure = failure def check(self, completed, failed=None): """check whether our dependencies have been met.""" if len(self) == 0: return True against = set() if self.success: against = completed if failed is not None and self.failure: against = against.union(failed) if self.all: return self.issubset(against) else: return not self.isdisjoint(against) def unreachable(self, completed, failed=None): """return whether this dependency has become impossible.""" if len(self) == 0: return False against = set() if not self.success: against = completed if failed is not None and not self.failure: against = against.union(failed) if self.all: return not self.isdisjoint(against) else: return self.issubset(against) def as_dict(self): """Represent this dependency as a dict. For json compatibility.""" return dict( dependencies=list(self), all=self.all, success=self.success, failure=self.failure, ) __all__ = ['depend', 'require', 'dependent', 'Dependency'] ipyparallel-7.1.0/ipyparallel/controller/dictdb.py000066400000000000000000000252121412533144600223460ustar00rootroot00000000000000"""A Task logger that presents our DB interface, but exists entirely in memory and implemented with dicts. TaskRecords are dicts of the form:: { 'msg_id' : str(uuid), 'client_uuid' : str(uuid), 'engine_uuid' : str(uuid) or None, 'header' : dict(header), 'content': dict(content), 'buffers': list(buffers), 'submitted': datetime or None, 'started': datetime or None, 'completed': datetime or None, 'received': datetime or None, 'resubmitted': str(uuid) or None, 'result_header' : dict(header) or None, 'result_content' : dict(content) or None, 'result_buffers' : list(buffers) or None, } With this info, many of the special categories of tasks can be defined by query, e.g.: * pending: completed is None * client's outstanding: client_uuid = uuid && completed is None * MIA: arrived is None (and completed is None) DictDB supports a subset of mongodb operators:: $lt,$gt,$lte,$gte,$ne,$in,$nin,$all,$mod,$exists """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import copy from copy import deepcopy from datetime import datetime from traitlets import Dict from traitlets import Float from traitlets import Integer from traitlets import Unicode from traitlets.config.configurable import LoggingConfigurable from ..util import ensure_timezone # Python can't copy memoryviews, but creating another memoryview works for us copy._deepcopy_dispatch[memoryview] = lambda x, memo: memoryview(x) filters = { '$lt': lambda a, b: a < b, '$gt': lambda a, b: b > a, '$eq': lambda a, b: a == b, '$ne': lambda a, b: a != b, '$lte': lambda a, b: a <= b, '$gte': lambda a, b: a >= b, '$in': lambda a, b: a in b, '$nin': lambda a, b: a not in b, '$all': lambda a, b: all([a in bb for bb in b]), '$mod': lambda a, b: a % b[0] == b[1], '$exists': lambda a, b: (b and a is not None) or (a is None and not b), } def _add_tz(obj): if isinstance(obj, datetime): obj = ensure_timezone(obj) return obj class CompositeFilter: """Composite filter for matching multiple properties.""" def __init__(self, dikt): self.tests = [] self.values = [] for key, value in dikt.items(): self.tests.append(_add_tz(filters[key])) self.values.append(_add_tz(value)) def __call__(self, value): for test, check in zip(self.tests, self.values): if not test(value, check): return False return True class BaseDB(LoggingConfigurable): """Empty Parent class so traitlets work on DB.""" # base configurable traits: session = Unicode("") def close(self): pass class DictDB(BaseDB): """Basic in-memory dict-based object for saving Task Records. This is the first object to present the DB interface for logging tasks out of memory. The interface is based on MongoDB, so adding a MongoDB backend should be straightforward. """ _records = Dict() _culled_ids = set() # set of ids which have been culled _buffer_bytes = Integer(0) # running total of the bytes in the DB size_limit = Integer( 1024 ** 3, config=True, help="""The maximum total size (in bytes) of the buffers stored in the db When the db exceeds this size, the oldest records will be culled until the total size is under size_limit * (1-cull_fraction). default: 1 GB """, ) record_limit = Integer( 1024, config=True, help="""The maximum number of records in the db When the history exceeds this size, the first record_limit * cull_fraction records will be culled. """, ) cull_fraction = Float( 0.1, config=True, help="""The fraction by which the db should culled when one of the limits is exceeded In general, the db size will spend most of its time with a size in the range: [limit * (1-cull_fraction), limit] for each of size_limit and record_limit. """, ) def _match_one(self, rec, tests): """Check if a specific record matches tests.""" for key, test in tests.items(): if not test(rec.get(key, None)): return False return True def _match(self, check): """Find all the matches for a check dict.""" matches = [] tests = {} for k, v in check.items(): if isinstance(v, dict): tests[k] = CompositeFilter(v) else: tests[k] = lambda o: _add_tz(o) == _add_tz(v) for rec in self._records.values(): if self._match_one(rec, tests): matches.append(deepcopy(rec)) return matches def _extract_subdict(self, rec, keys): """extract subdict of keys""" d = {} d['msg_id'] = rec['msg_id'] for key in keys: d[key] = rec[key] return deepcopy(d) # methods for monitoring size / culling history def _add_bytes(self, rec): for key in ('buffers', 'result_buffers'): for buf in rec.get(key) or []: self._buffer_bytes += len(buf) self._maybe_cull() def _drop_bytes(self, rec): for key in ('buffers', 'result_buffers'): for buf in rec.get(key) or []: self._buffer_bytes -= len(buf) def _cull_oldest(self, n=1): """cull the oldest N records""" for msg_id in self.get_history()[:n]: self.log.debug("Culling record: %r", msg_id) self._culled_ids.add(msg_id) self.drop_record(msg_id) def _maybe_cull(self): # cull by count: if len(self._records) > self.record_limit: to_cull = int(self.cull_fraction * self.record_limit) self.log.info( "%i records exceeds limit of %i, culling oldest %i", len(self._records), self.record_limit, to_cull, ) self._cull_oldest(to_cull) # cull by size: if self._buffer_bytes > self.size_limit: limit = self.size_limit * (1 - self.cull_fraction) before = self._buffer_bytes before_count = len(self._records) culled = 0 while self._buffer_bytes > limit: self._cull_oldest(1) culled += 1 self.log.info( "%i records with total buffer size %i exceeds limit: %i. Culled oldest %i records.", before_count, before, self.size_limit, culled, ) def _check_dates(self, rec): for key in ('submitted', 'started', 'completed', 'received'): value = rec.get(key, None) if value is not None and not isinstance(value, datetime): raise ValueError("%s must be None or datetime, not %r" % (key, value)) if isinstance(value, datetime) and value.tzinfo is None: self.log.warning( "Timestamps should always have timezones: %s=%s", key, value ) rec[key] = ensure_timezone(value) # public API methods: def add_record(self, msg_id, rec): """Add a new Task Record, by msg_id.""" if msg_id in self._records: raise KeyError("Already have msg_id %r" % (msg_id)) self._check_dates(rec) self._records[msg_id] = rec self._add_bytes(rec) self._maybe_cull() def get_record(self, msg_id): """Get a specific Task Record, by msg_id.""" if msg_id in self._culled_ids: raise KeyError("Record %r has been culled for size" % msg_id) if not msg_id in self._records: raise KeyError("No such msg_id %r" % (msg_id)) return deepcopy(self._records[msg_id]) def update_record(self, msg_id, rec): """Update the data in an existing record.""" if msg_id in self._culled_ids: raise KeyError("Record %r has been culled for size" % msg_id) self._check_dates(rec) _rec = self._records[msg_id] self._drop_bytes(_rec) _rec.update(rec) self._add_bytes(_rec) def drop_matching_records(self, check): """Remove a record from the DB.""" matches = self._match(check) for rec in matches: self._drop_bytes(rec) del self._records[rec['msg_id']] def drop_record(self, msg_id): """Remove a record from the DB.""" rec = self._records[msg_id] self._drop_bytes(rec) del self._records[msg_id] def find_records(self, check, keys=None): """Find records matching a query dict, optionally extracting subset of keys. Returns dict keyed by msg_id of matching records. Parameters ---------- check : dict mongodb-style query argument keys : list of strs [optional] if specified, the subset of keys to extract. msg_id will *always* be included. """ matches = self._match(check) if keys: return [self._extract_subdict(rec, keys) for rec in matches] else: return matches def get_history(self): """get all msg_ids, ordered by time submitted.""" msg_ids = self._records.keys() # Remove any that do not have a submitted timestamp. # This is extremely unlikely to happen, # but it seems to come up in some tests on VMs. msg_ids = [m for m in msg_ids if self._records[m]['submitted'] is not None] return sorted(msg_ids, key=lambda m: self._records[m]['submitted']) class NoData(KeyError): """Special KeyError to raise when requesting data from NoDB""" def __str__(self): return "NoDB backend doesn't store any data. " "Start the Controller with a DB backend to enable resubmission / result persistence." class NoDB(BaseDB): """A blackhole db backend that actually stores no information. Provides the full DB interface, but raises KeyErrors on any method that tries to access the records. This can be used to minimize the memory footprint of the Hub when its record-keeping functionality is not required. """ def add_record(self, msg_id, record): pass def get_record(self, msg_id): raise NoData() def update_record(self, msg_id, record): pass def drop_matching_records(self, check): pass def drop_record(self, msg_id): pass def find_records(self, check, keys=None): raise NoData() def get_history(self): raise NoData() ipyparallel-7.1.0/ipyparallel/controller/heartmonitor.py000077500000000000000000000240641412533144600236370ustar00rootroot00000000000000#!/usr/bin/env python """ A multi-heart Heartbeat system using PUB and ROUTER sockets. pings are sent out on the PUB, and hearts are tracked based on their DEALER identities. """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import print_function import logging import time import uuid import zmq from jupyter_client.session import Session from tornado import ioloop from traitlets import Bool from traitlets import default from traitlets import Dict from traitlets import Float from traitlets import Instance from traitlets import Integer from traitlets import Set from traitlets.config.configurable import LoggingConfigurable from zmq.devices import ThreadDevice from zmq.devices import ThreadMonitoredQueue from zmq.eventloop.zmqstream import ZMQStream from ipyparallel.util import bind from ipyparallel.util import connect from ipyparallel.util import log_errors from ipyparallel.util import set_hwm class Heart: """A basic heart object for responding to a HeartMonitor. This is a simple wrapper with defaults for the most common Device model for responding to heartbeats. Builds a threadsafe zmq.FORWARDER Device, defaulting to using SUB/DEALER for in/out. You can specify the DEALER's IDENTITY via the optional heart_id argument.""" device = None id = None def __init__( self, in_addr, out_addr, mon_addr=None, in_type=zmq.SUB, out_type=zmq.DEALER, mon_type=zmq.PUB, heart_id=None, curve_serverkey=None, curve_secretkey=None, curve_publickey=None, ): if mon_addr is None: self.device = ThreadDevice(zmq.FORWARDER, in_type, out_type) else: self.device = ThreadMonitoredQueue( in_type, out_type, mon_type, in_prefix=b"", out_prefix=b"" ) # do not allow the device to share global Context.instance, # which is the default behavior in pyzmq > 2.1.10 self.device.context_factory = zmq.Context self.device.daemon = True self.device.connect_in(in_addr) self.device.connect_out(out_addr) if curve_serverkey: self.device.setsockopt_in(zmq.CURVE_SERVERKEY, curve_serverkey) self.device.setsockopt_in(zmq.CURVE_PUBLICKEY, curve_publickey) self.device.setsockopt_in(zmq.CURVE_SECRETKEY, curve_secretkey) self.device.setsockopt_out(zmq.CURVE_SERVERKEY, curve_serverkey) self.device.setsockopt_out(zmq.CURVE_PUBLICKEY, curve_publickey) self.device.setsockopt_out(zmq.CURVE_SECRETKEY, curve_secretkey) if mon_addr is not None: self.device.setsockopt_mon(zmq.CURVE_SERVERKEY, curve_publickey) self.device.setsockopt_mon(zmq.CURVE_PUBLICKEY, curve_publickey) self.device.setsockopt_mon(zmq.CURVE_SECRETKEY, curve_secretkey) if mon_addr is not None: self.device.connect_mon(mon_addr) if in_type == zmq.SUB: self.device.setsockopt_in(zmq.SUBSCRIBE, b"") if heart_id is None: heart_id = uuid.uuid4().bytes self.device.setsockopt_out(zmq.IDENTITY, heart_id) self.id = heart_id def start(self): return self.device.start() class HeartMonitor(LoggingConfigurable): """A basic HeartMonitor class ping_stream: a PUB stream pong_stream: an ROUTER stream period: the period of the heartbeat in milliseconds """ debug = Bool( False, config=True, help="""Whether to include every heartbeat in debugging output. Has to be set explicitly, because there will be *a lot* of output. """, ) period = Integer( 3000, config=True, help='The frequency at which the Hub pings the engines for heartbeats ' '(in ms)', ) max_heartmonitor_misses = Integer( 10, config=True, help='Allowed consecutive missed pings from controller Hub to engine before unregistering.', ) ping_stream = Instance(ZMQStream) pong_stream = Instance(ZMQStream) monitor_stream = Instance(ZMQStream) session = Instance(Session) @default("session") def _default_session(self): return Session(parent=self) loop = Instance(ioloop.IOLoop) @default("loop") def _loop_default(self): return ioloop.IOLoop.current() # not settable: hearts = Set() responses = Set() on_probation = Dict() last_ping = Float(0) lifetime = Float(0) tic = Float(0) def start(self): self.log.debug("heartbeat::waiting for subscription") msg = self.monitor_stream.socket.recv_multipart() self.log.debug("heartbeat::subscription started") self.pong_stream.on_recv(self.handle_pong) self.tic = time.monotonic() self.caller = ioloop.PeriodicCallback(self.beat, self.period) self.caller.start() def beat(self): self.pong_stream.flush() self.last_ping = self.lifetime toc = time.monotonic() self.lifetime += toc - self.tic self.tic = toc if self.debug: self.log.debug("heartbeat::sending %s", self.lifetime) good_hearts = self.hearts.intersection(self.responses) missed_beats = self.hearts.difference(good_hearts) new_hearts = self.responses.difference(good_hearts) if new_hearts: self.handle_new_hearts(new_hearts) heart_failures, on_probation = self._check_missed( missed_beats, self.on_probation, self.hearts ) if heart_failures: self.handle_heart_failure(heart_failures) self.on_probation = on_probation self.responses = set() # print self.on_probation, self.hearts # self.log.debug("heartbeat::beat %.3f, %i beating hearts", self.lifetime, len(self.hearts)) self.ping_stream.send(str(self.lifetime).encode('ascii')) # flush stream to force immediate socket send self.ping_stream.flush() def _check_missed(self, missed_beats, on_probation, hearts): """Update heartbeats on probation, identifying any that have too many misses.""" failures = [] new_probation = {} for cur_heart in (b for b in missed_beats if b in hearts): miss_count = on_probation.get(cur_heart, 0) + 1 self.log.info("heartbeat::missed %s : %s" % (cur_heart, miss_count)) if miss_count > self.max_heartmonitor_misses: failures.append(cur_heart) else: new_probation[cur_heart] = miss_count return failures, new_probation def handle_new_hearts(self, hearts): for heart in hearts: self.hearts.add(heart) self.log.debug(f"Notifying hub of {len(hearts)} new hearts") self.session.send( self.monitor_stream, "new_heart", content={ "hearts": [h.decode("utf8", "replace") for h in hearts], }, ident=[b"heartmonitor", b""], ) def handle_heart_failure(self, hearts): self.log.debug(f"Notifying hub of {len(hearts)} stopped hearts") self.session.send( self.monitor_stream, "stopped_heart", content={ "hearts": [h.decode("utf8", "replace") for h in hearts], }, ident=[b"heartmonitor", b""], ) for heart in hearts: try: self.hearts.remove(heart) except KeyError: self.log.info("heartbeat:: %s has already been removed.", heart) @log_errors def handle_pong(self, msg): """a heart just beat""" current = str(self.lifetime).encode('ascii') last = str(self.last_ping).encode('ascii') if msg[1] == current: delta = time.monotonic() - self.tic if self.debug: self.log.debug( "heartbeat::heart %r took %.2f ms to respond", msg[0], 1000 * delta ) self.responses.add(msg[0]) elif msg[1] == last: delta = time.monotonic() - self.tic + (self.lifetime - self.last_ping) self.log.warning( "heartbeat::heart %r missed a beat, and took %.2f ms to respond", msg[0], 1000 * delta, ) self.responses.add(msg[0]) else: self.log.warning( "heartbeat::got bad heartbeat (possibly old?): %s (current=%.3f)", msg[1], self.lifetime, ) def start_heartmonitor( ping_url, pong_url, monitor_url, log_level=logging.INFO, curve_publickey=None, curve_secretkey=None, **kwargs, ): """Start a heart monitor. For use in a background process, via Process(target=start_heartmonitor) """ loop = ioloop.IOLoop() loop.make_current() ctx = zmq.Context() ping_socket = ctx.socket(zmq.PUB) bind( ping_socket, ping_url, curve_publickey=curve_publickey, curve_secretkey=curve_secretkey, ) ping_stream = ZMQStream(ping_socket) pong_socket = ctx.socket(zmq.ROUTER) set_hwm(pong_socket, 0) bind( pong_socket, pong_url, curve_publickey=curve_publickey, curve_secretkey=curve_secretkey, ) pong_stream = ZMQStream(pong_socket) monitor_socket = ctx.socket(zmq.XPUB) connect( monitor_socket, monitor_url, curve_publickey=curve_publickey, curve_secretkey=curve_secretkey, curve_serverkey=curve_publickey, ) monitor_stream = ZMQStream(monitor_socket) # reinitialize logging after fork from .app import IPController app = IPController(log_level=log_level) kwargs['log'] = app.log heart_monitor = HeartMonitor( ping_stream=ping_stream, pong_stream=pong_stream, monitor_stream=monitor_stream, **kwargs, ) heart_monitor.start() try: loop.start() finally: loop.close(all_fds=True) ctx.destroy() ipyparallel-7.1.0/ipyparallel/controller/hub.py000066400000000000000000001541271412533144600217030ustar00rootroot00000000000000"""The IPython Controller Hub with 0MQ This is the master object that handles connections from engines and clients, and monitors traffic through the various queues. """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import print_function import inspect import json import os import sys import time from collections import deque from datetime import datetime import zmq from jupyter_client.jsonutil import parse_date from jupyter_client.session import Session from tornado import ioloop from traitlets import Any from traitlets import Bytes from traitlets import default from traitlets import Dict from traitlets import Float from traitlets import HasTraits from traitlets import Instance from traitlets import Integer from traitlets import Set from traitlets import Unicode from traitlets.config import LoggingConfigurable from zmq.eventloop.zmqstream import ZMQStream from ..util import extract_dates from .heartmonitor import HeartMonitor from ipyparallel import error from ipyparallel import util # internal: def _passer(*args, **kwargs): return def _printer(*args, **kwargs): print(args) print(kwargs) def empty_record(): """Return an empty dict with all record keys.""" return { 'msg_id': None, 'header': None, 'metadata': None, 'content': None, 'buffers': None, 'submitted': None, 'client_uuid': None, 'engine_uuid': None, 'started': None, 'completed': None, 'resubmitted': None, 'received': None, 'result_header': None, 'result_metadata': None, 'result_content': None, 'result_buffers': None, 'queue': None, 'execute_input': None, 'execute_result': None, 'error': None, 'stdout': '', 'stderr': '', } def ensure_date_is_parsed(header): if not isinstance(header['date'], datetime): header['date'] = parse_date(header['date']) def init_record(msg): """Initialize a TaskRecord based on a request.""" header = msg['header'] ensure_date_is_parsed(header) return { 'msg_id': header['msg_id'], 'header': header, 'content': msg['content'], 'metadata': msg['metadata'], 'buffers': msg['buffers'], 'submitted': util.ensure_timezone(header['date']), 'client_uuid': None, 'engine_uuid': None, 'started': None, 'completed': None, 'resubmitted': None, 'received': None, 'result_header': None, 'result_metadata': None, 'result_content': None, 'result_buffers': None, 'queue': None, 'execute_input': None, 'execute_result': None, 'error': None, 'stdout': '', 'stderr': '', } class EngineConnector(HasTraits): """A simple object for accessing the various zmq connections of an object. Attributes are: id (int): engine ID uuid (unicode): engine UUID pending: set of msg_ids stallback: tornado timeout for stalled registration registration_started (float): time when registration began """ id = Integer() uuid = Unicode() ident = Bytes() pending = Set() stallback = Any() registration_started = Float() def __init__(self, **kwargs): super().__init__(**kwargs) if not self.registration_started: self.registration_started = time.monotonic() class Hub(LoggingConfigurable): """The IPython Controller Hub with 0MQ connections Parameters ========== loop: zmq IOLoop instance session: Session object context: zmq context for creating new connections (?) queue: ZMQStream for monitoring the command queue (SUB) query: ZMQStream for engine registration and client queries requests (ROUTER) heartbeat: HeartMonitor object checking the pulse of the engines notifier: ZMQStream for broadcasting engine registration changes (PUB) db: connection to db for out of memory logging of commands NotImplemented engine_info: dict of zmq connection information for engines to connect to the queues. client_info: dict of zmq connection information for engines to connect to the queues. """ engine_state_file = Unicode() # internal data structures: ids = Set() # engine IDs by_ident = Dict() # map bytes identities : int engine id engines = Dict() # map int engine id : EngineConnector hearts = Dict() # map bytes identities : int engine id, only for active heartbeats heartmonitor_period = Integer() pending = Set() queues = Dict() # pending msg_ids keyed by engine_id tasks = Dict() # pending msg_ids submitted as tasks, keyed by client_id completed = Dict() # completed msg_ids keyed by engine_id all_completed = Set() # completed msg_ids keyed by engine_id unassigned = Set() # set of task msg_ids not yet assigned a destination incoming_registrations = Dict() registration_timeout = Integer() _idcounter = Integer(0) distributed_scheduler = Any() expect_stopped_hearts = Instance(deque) @default("expect_stopped_hearts") def _default_expect_stopped_hearts(self): # remember the last this-many hearts # silences warnings about ignoring stopped hearts for unregistered engines # harmless, but noisy if they happen on every unregistration return deque(maxlen=1024) loop = Instance(ioloop.IOLoop) @default("loop") def _default_loop(self): return ioloop.IOLoop.current() # objects from constructor: session = Instance(Session) query = Instance(ZMQStream, allow_none=True) monitor = Instance(ZMQStream, allow_none=True) notifier = Instance(ZMQStream, allow_none=True) resubmit = Instance(ZMQStream, allow_none=True) heartmonitor = Instance(HeartMonitor, allow_none=True) db = Instance(object, allow_none=True) client_info = Dict() engine_info = Dict() def __init__(self, **kwargs): """ # universal: loop: IOLoop for creating future connections session: streamsession for sending serialized data # engine: queue: ZMQStream for monitoring queue messages query: ZMQStream for engine+client registration and client requests heartbeat: HeartMonitor object for tracking engines # extra: db: ZMQStream for db connection (NotImplemented) engine_info: zmq address/protocol dict for engine connections client_info: zmq address/protocol dict for client connections """ super().__init__(**kwargs) # register our callbacks self.query.on_recv(self.dispatch_query) self.monitor.on_recv(self.dispatch_monitor_traffic) self.monitor_handlers = { b'in': self.save_queue_request, b'out': self.save_queue_result, b'intask': self.save_task_request, b'outtask': self.save_task_result, b'inbcast': self.save_broadcast_request, b'outbcast': self.save_broadcast_result, b'tracktask': self.save_task_destination, b'incontrol': _passer, b'outcontrol': _passer, b'iopub': self.monitor_iopub_message, b'heartmonitor': self.heartmonitor_message, } self.query_handlers = { 'queue_request': self.queue_status, 'result_request': self.get_results, 'history_request': self.get_history, 'db_request': self.db_query, 'purge_request': self.purge_results, 'load_request': self.check_load, 'resubmit_request': self.resubmit_task, 'shutdown_request': self.shutdown_request, 'registration_request': self.register_engine, 'unregistration_request': self.unregister_engine, 'connection_request': self.connection_request, 'become_dask_request': self.become_dask, 'stop_distributed_request': self.stop_distributed, } # ignore resubmit replies self.resubmit.on_recv(lambda msg: None, copy=False) self.log.info("hub::created hub") def new_engine_id(self, requested_id=None): """generate a new engine integer id. No longer reuse old ids, just count from 0. If an id is requested and available, use that. Otherwise, use the counter. """ if requested_id is not None: if requested_id in self.engines: self.log.warning( "Engine id %s in use by engine with uuid=%s", requested_id, self.engines[requested_id].uuid, ) elif requested_id in {ec.id for ec in self.incoming_registrations.values()}: self.log.warning( "Engine id %s registration already pending", requested_id ) else: self._idcounter = max(requested_id + 1, self._idcounter) return requested_id newid = self._idcounter self._idcounter += 1 return newid # ----------------------------------------------------------------------------- # message validation # ----------------------------------------------------------------------------- def _validate_targets(self, targets): """turn any valid targets argument into a list of integer ids""" if targets is None: # default to all return self.ids if isinstance(targets, (int, str)): # only one target specified targets = [targets] _targets = [] for t in targets: # map raw identities to ids if isinstance(t, str): t = self.by_ident.get(t.encode("utf8", "replace"), t) _targets.append(t) targets = _targets bad_targets = [t for t in targets if t not in self.ids] if bad_targets: raise IndexError("No Such Engine: %r" % bad_targets) if not targets: raise IndexError("No Engines Registered") return targets # ----------------------------------------------------------------------------- # dispatch methods (1 per stream) # ----------------------------------------------------------------------------- @util.log_errors def dispatch_monitor_traffic(self, msg): """all ME and Task queue messages come through here, as well as IOPub traffic.""" self.log.debug("monitor traffic: %r", msg[0]) switch = msg[0] try: idents, msg = self.session.feed_identities(msg[1:]) except ValueError: idents = [] if not idents: self.log.error("Monitor message without topic: %r", msg) return handler = self.monitor_handlers.get(switch, None) if handler is not None: handler(idents, msg) else: self.log.error("Unrecognized monitor topic: %r", switch) @util.log_errors async def dispatch_query(self, msg): """Route registration requests and queries from clients.""" try: idents, msg = self.session.feed_identities(msg) except ValueError: idents = [] if not idents: self.log.error("Bad Query Message: %r", msg) return client_id = idents[0] try: msg = self.session.deserialize(msg, content=True) except Exception: content = error.wrap_exception() self.log.error("Bad Query Message: %r", msg, exc_info=True) self.session.send( self.query, "hub_error", ident=client_id, content=content, parent=msg ) return # print client_id, header, parent, content # switch on message type: msg_type = msg['header']['msg_type'] self.log.info("client::client %r requested %r", client_id, msg_type) handler = self.query_handlers.get(msg_type, None) try: if handler is None: raise KeyError("Bad Message Type: %r" % msg_type) except: content = error.wrap_exception() self.log.error("Bad Message Type: %r", msg_type, exc_info=True) self.session.send( self.query, "hub_error", ident=client_id, content=content, parent=msg ) return try: f = handler(idents, msg) if f and inspect.isawaitable(f): await f except Exception: content = error.wrap_exception() self.log.error("Error handling request: %r", msg_type, exc_info=True) self.session.send( self.query, "hub_error", ident=client_id, content=content, parent=msg ) # --------------------------------------------------------------------------- # handler methods (1 per event) # --------------------------------------------------------------------------- # ----------------------- Heartbeat -------------------------------------- @util.log_errors def heartmonitor_message(self, topics, msg): """Handle a message from the heart monitor""" try: msg = self.session.deserialize(msg) except Exception: self.log.error( "heartmonitor::invalid message %r", msg, exc_info=True, ) return msg_type = msg['header']['msg_type'] if msg_type == 'new_heart': hearts = msg['content']['hearts'] self.log.info(f"Registering {len(hearts)} new hearts") for heart in hearts: self.handle_new_heart(heart.encode("utf8")) elif msg_type == 'stopped_heart': hearts = msg['content']['hearts'] self.log.warning(f"{len(hearts)} hearts stopped") for heart in hearts: self.handle_stopped_heart(heart.encode("utf8")) def handle_new_heart(self, heart): """Handle a new heart that just started beating""" self.log.debug("heartbeat::handle_new_heart(%r)", heart) if heart not in self.incoming_registrations: self.log.info("heartbeat::ignoring new heart: %r", heart) else: self.finish_registration(heart) def handle_stopped_heart(self, heart): """Handle notification that heart has stopped""" self.log.debug("heartbeat::handle_stopped_heart(%r)", heart) eid = self.hearts.get(heart, None) if eid is None: if heart in self.expect_stopped_hearts: log = self.log.debug else: log = self.log.info log( "heartbeat::ignoring heart failure %r (probably unregistered already)", heart, ) else: uuid = self.engines[eid].uuid self.unregister_engine(heart, dict(content=dict(id=eid, queue=uuid))) # ----------------------- MUX Queue Traffic ------------------------------ def save_queue_request(self, idents, msg): if len(idents) < 2: self.log.error("invalid identity prefix: %r", idents) return queue_id, client_id = idents[:2] try: msg = self.session.deserialize(msg) except Exception: self.log.error( "queue::client %r sent invalid message to %r: %r", client_id, queue_id, msg, exc_info=True, ) return eid = self.by_ident.get(queue_id, None) if eid is None: self.log.error("queue::target %r not registered", queue_id) self.log.debug("queue:: valid are: %r", self.by_ident.keys()) return record = init_record(msg) msg_id = record['msg_id'] self.log.info( "queue::client %r submitted request %r to %s", client_id, msg_id, eid ) # Unicode in records record['engine_uuid'] = queue_id.decode('ascii') record['client_uuid'] = msg['header']['session'] record['queue'] = 'mux' try: # it's posible iopub arrived first: existing = self.db.get_record(msg_id) for key, evalue in existing.items(): rvalue = record.get(key, None) if evalue and rvalue and evalue != rvalue: self.log.warning( "conflicting initial state for record: %r:%r <%r> %r", msg_id, rvalue, key, evalue, ) elif evalue and not rvalue: record[key] = evalue try: self.db.update_record(msg_id, record) except Exception: self.log.error("DB Error updating record %r", msg_id, exc_info=True) except KeyError: try: self.db.add_record(msg_id, record) except Exception: self.log.error("DB Error adding record %r", msg_id, exc_info=True) self.pending.add(msg_id) self.queues[eid].append(msg_id) def save_queue_result(self, idents, msg): if len(idents) < 2: self.log.error("invalid identity prefix: %r", idents) return client_id, queue_id = idents[:2] try: msg = self.session.deserialize(msg) except Exception: self.log.error( "queue::engine %r sent invalid message to %r: %r", queue_id, client_id, msg, exc_info=True, ) return eid = self.by_ident.get(queue_id, None) if eid is None: self.log.error("queue::unknown engine %r is sending a reply: ", queue_id) return parent = msg['parent_header'] if not parent: return msg_id = parent['msg_id'] if msg_id in self.pending: self.pending.remove(msg_id) self.all_completed.add(msg_id) self.queues[eid].remove(msg_id) self.completed[eid].append(msg_id) self.log.info("queue::request %r completed on %s", msg_id, eid) elif msg_id not in self.all_completed: # it could be a result from a dead engine that died before delivering the # result self.log.warning("queue:: unknown msg finished %r", msg_id) return # update record anyway, because the unregistration could have been premature rheader = msg['header'] md = msg['metadata'] ensure_date_is_parsed(rheader) completed = util.ensure_timezone(rheader['date']) started = extract_dates(md.get('started', None)) result = { 'result_header': rheader, 'result_metadata': md, 'result_content': msg['content'], 'received': util.utcnow(), 'started': started, 'completed': completed, } result['result_buffers'] = msg['buffers'] try: self.db.update_record(msg_id, result) except Exception: self.log.error("DB Error updating record %r", msg_id, exc_info=True) # --------------------- Broadcast traffic ------------------------------ def save_broadcast_request(self, idents, msg): client_id = idents[0] try: msg = self.session.deserialize(msg) except Exception as e: self.log.error( f'broadcast:: client {client_id} sent invalid broadcast message:' f' {msg}', exc_info=True, ) return record = init_record(msg) record['client_uuid'] = msg['header']['session'] header = msg['header'] msg_id = header['msg_id'] self.pending.add(msg_id) try: self.db.add_record(msg_id, record) except Exception as e: self.log.error(f'DB Error adding record {msg_id}', exc_info=True) def save_broadcast_result(self, idents, msg): client_id = idents[0] try: msg = self.session.deserialize(msg) except Exception as e: self.log.error( f'broadcast::invalid broadcast result message send to {client_id}:' f'' ) # save the result of a completed broadcast parent = msg['parent_header'] if not parent: self.log.warning(f'Broadcast message {msg} had no parent') return msg_id = parent['msg_id'] header = msg['header'] md = msg['metadata'] engine_uuid = md.get('engine', u'') eid = self.by_ident.get(engine_uuid.encode("utf8"), None) status = md.get('status', None) if msg_id in self.pending: self.log.info(f'broadcast:: broadcast {msg_id} finished on {eid}') self.pending.remove(msg_id) self.all_completed.add(msg_id) if eid is not None and status != 'aborted': self.completed[eid].append(msg_id) ensure_date_is_parsed(header) completed = util.ensure_timezone(header['date']) started = extract_dates(md.get('started', None)) result = { 'result_header': header, 'result_metadata': msg['metadata'], 'result_content': msg['content'], 'started': started, 'completed': completed, 'received': util.utcnow(), 'engine_uuid': engine_uuid, 'result_buffers': msg['buffers'], } try: self.db.update_record(msg_id, result) except Exception as e: self.log.error( f'DB Error saving broadcast result {msg_id}', msg_id, exc_info=True ) else: self.log.debug(f'broadcast::unknown broadcast {msg_id} finished') # --------------------- Task Queue Traffic ------------------------------ def save_task_request(self, idents, msg): """Save the submission of a task.""" client_id = idents[0] try: msg = self.session.deserialize(msg) except Exception: self.log.error( "task::client %r sent invalid task message: %r", client_id, msg, exc_info=True, ) return record = init_record(msg) record['client_uuid'] = msg['header']['session'] record['queue'] = 'task' header = msg['header'] msg_id = header['msg_id'] self.pending.add(msg_id) self.unassigned.add(msg_id) try: # it's posible iopub arrived first: existing = self.db.get_record(msg_id) if existing['resubmitted']: for key in ('submitted', 'client_uuid', 'buffers'): # don't clobber these keys on resubmit # submitted and client_uuid should be different # and buffers might be big, and shouldn't have changed record.pop(key) # still check content,header which should not change # but are not expensive to compare as buffers for key, evalue in existing.items(): if key.endswith('buffers'): # don't compare buffers continue rvalue = record.get(key, None) if evalue and rvalue and evalue != rvalue: self.log.warning( "conflicting initial state for record: %r:%r <%r> %r", msg_id, rvalue, key, evalue, ) elif evalue and not rvalue: record[key] = evalue try: self.db.update_record(msg_id, record) except Exception: self.log.error("DB Error updating record %r", msg_id, exc_info=True) except KeyError: try: self.db.add_record(msg_id, record) except Exception: self.log.error("DB Error adding record %r", msg_id, exc_info=True) except Exception: self.log.error("DB Error saving task request %r", msg_id, exc_info=True) def save_task_result(self, idents, msg): """save the result of a completed task.""" client_id = idents[0] try: msg = self.session.deserialize(msg) except Exception: self.log.error( "task::invalid task result message send to %r: %r", client_id, msg, exc_info=True, ) return parent = msg['parent_header'] if not parent: # print msg self.log.warning("Task %r had no parent!", msg) return msg_id = parent['msg_id'] if msg_id in self.unassigned: self.unassigned.remove(msg_id) header = msg['header'] md = msg['metadata'] engine_uuid = md.get('engine', u'') eid = self.by_ident.get(engine_uuid.encode("utf8"), None) status = md.get('status', None) if msg_id in self.pending: self.log.info("task::task %r finished on %s", msg_id, eid) self.pending.remove(msg_id) self.all_completed.add(msg_id) if eid is not None: if status != 'aborted': self.completed[eid].append(msg_id) if msg_id in self.tasks[eid]: self.tasks[eid].remove(msg_id) ensure_date_is_parsed(header) completed = util.ensure_timezone(header['date']) started = extract_dates(md.get('started', None)) result = { 'result_header': header, 'result_metadata': msg['metadata'], 'result_content': msg['content'], 'started': started, 'completed': completed, 'received': util.utcnow(), 'engine_uuid': engine_uuid, } result['result_buffers'] = msg['buffers'] try: self.db.update_record(msg_id, result) except Exception: self.log.error("DB Error saving task request %r", msg_id, exc_info=True) else: self.log.debug("task::unknown task %r finished", msg_id) def save_task_destination(self, idents, msg): try: msg = self.session.deserialize(msg, content=True) except Exception: self.log.error("task::invalid task tracking message", exc_info=True) return content = msg['content'] # print (content) msg_id = content['msg_id'] engine_uuid = content['engine_id'] eid = self.by_ident[engine_uuid.encode("utf8")] self.log.info("task::task %r arrived on %r", msg_id, eid) if msg_id in self.unassigned: self.unassigned.remove(msg_id) # else: # self.log.debug("task::task %r not listed as MIA?!"%(msg_id)) self.tasks[eid].append(msg_id) try: self.db.update_record(msg_id, dict(engine_uuid=engine_uuid)) except Exception: self.log.error("DB Error saving task destination %r", msg_id, exc_info=True) # --------------------- IOPub Traffic ------------------------------ def monitor_iopub_message(self, topics, msg): '''intercept iopub traffic so events can be acted upon''' try: msg = self.session.deserialize(msg, content=True) except Exception: self.log.error("iopub::invalid IOPub message", exc_info=True) return msg_type = msg['header']['msg_type'] if msg_type == 'shutdown_reply': session = msg['header']['session'] uuid_bytes = session.encode("utf8", "replace") eid = self.by_ident.get(uuid_bytes, None) if eid is None: self.log.error(f"Found no engine for {session}") return uuid = self.engines[eid].uuid self.unregister_engine( ident='shutdown_reply', msg=dict(content=dict(id=eid, queue=uuid)) ) if msg_type not in ( 'status', 'shutdown_reply', ): self.save_iopub_message(topics, msg) def save_iopub_message(self, topics, msg): """save an iopub message into the db""" parent = msg['parent_header'] if not parent: self.log.debug("iopub::IOPub message lacks parent: %r", msg) return msg_id = parent['msg_id'] msg_type = msg['header']['msg_type'] content = msg['content'] # ensure msg_id is in db try: rec = self.db.get_record(msg_id) except KeyError: rec = None # stream d = {} if msg_type == 'stream': name = content['name'] s = '' if rec is None else rec[name] d[name] = s + content['text'] elif msg_type == 'error': d['error'] = content elif msg_type == 'execute_input': d['execute_input'] = content['code'] elif msg_type in ('display_data', 'execute_result'): d[msg_type] = content elif msg_type == 'data_pub': self.log.info("ignored data_pub message for %s" % msg_id) else: self.log.warning("unhandled iopub msg_type: %r", msg_type) if not d: return if rec is None: # new record rec = empty_record() rec['msg_id'] = msg_id rec.update(d) d = rec update_record = self.db.add_record else: update_record = self.db.update_record try: update_record(msg_id, d) except Exception: self.log.error("DB Error saving iopub message %r", msg_id, exc_info=True) # ------------------------------------------------------------------------- # Registration requests # ------------------------------------------------------------------------- def connection_request(self, client_id, msg): """Reply with connection addresses for clients.""" self.log.info("client::client %r connected", client_id) content = dict(status='ok') jsonable = {} for eid, ec in self.engines.items(): jsonable[str(eid)] = ec.uuid content['engines'] = jsonable self.session.send( self.query, 'connection_reply', content, parent=msg, ident=client_id ) def register_engine(self, reg, msg): """Register a new engine.""" content = msg['content'] try: uuid = content['uuid'] except KeyError: self.log.error("registration::queue not specified", exc_info=True) return eid = self.new_engine_id(content.get('id')) self.log.debug(f"registration::requesting registration {eid}:{uuid}") content = dict( id=eid, status='ok', hb_period=self.heartmonitor_period, connection_info=self.engine_info, ) # check if requesting available IDs: if uuid.encode("utf8") in self.by_ident: try: raise KeyError("uuid %r in use" % uuid) except: content = error.wrap_exception() self.log.error("uuid %r in use", uuid, exc_info=True) else: for h, ec in self.incoming_registrations.items(): if uuid == h: try: raise KeyError("heart_id %r in use" % uuid) except: self.log.error("heart_id %r in use", uuid, exc_info=True) content = error.wrap_exception() break elif uuid == ec.uuid: try: raise KeyError("uuid %r in use" % uuid) except: self.log.error("uuid %r in use", uuid, exc_info=True) content = error.wrap_exception() break msg = self.session.send( self.query, "registration_reply", content=content, ident=reg ) # actually send replies to avoid async starvation during a registration flood # it's important that this message has actually been sent # before we start the stalled registration countdown self.query.flush(zmq.POLLOUT) heart = uuid.encode("utf8") if content['status'] == 'ok': self.log.info(f"registration::accepting registration {eid}:{uuid}") t = self.loop.add_timeout( self.loop.time() + self.registration_timeout, lambda: self._purge_stalled_registration(heart), ) self.incoming_registrations[heart] = EngineConnector( id=eid, uuid=uuid, ident=heart, stallback=t ) else: self.log.error( "registration::registration %i failed: %r", eid, content['evalue'] ) return eid def unregister_engine(self, ident, msg): """Unregister an engine that explicitly requested to leave.""" try: eid = msg['content']['id'] except: self.log.error( f"registration::bad request for engine for unregistration: {msg['content']}", ) return if eid not in self.engines: self.log.info( f"registration::unregister_engine({eid}) already unregistered" ) return self.log.info(f"registration::unregister_engine({eid})") ec = self.engines[eid] content = dict(id=eid, uuid=ec.uuid) # stop the heartbeats self.hearts.pop(ec.ident, None) self.expect_stopped_hearts.append(ec.ident) self.loop.add_timeout( self.loop.time() + self.registration_timeout, lambda: self._handle_stranded_msgs(eid, ec.uuid), ) # cleanup mappings self.by_ident.pop(ec.ident, None) self.engines.pop(eid, None) self._save_engine_state() if self.notifier: self.session.send( self.notifier, "unregistration_notification", content=content ) def _handle_stranded_msgs(self, eid, uuid): """Handle messages known to be on an engine when the engine unregisters. It is possible that this will fire prematurely - that is, an engine will go down after completing a result, and the client will be notified that the result failed and later receive the actual result. """ outstanding = self.queues[eid] for msg_id in outstanding: self.pending.remove(msg_id) self.all_completed.add(msg_id) try: raise error.EngineError( "Engine %r died while running task %r" % (eid, msg_id) ) except: content = error.wrap_exception() # build a fake header: header = {} header['engine'] = uuid header['date'] = util.utcnow() rec = dict(result_content=content, result_header=header, result_buffers=[]) rec['completed'] = util.ensure_timezone(header['date']) rec['engine_uuid'] = uuid try: self.db.update_record(msg_id, rec) except Exception: self.log.error( "DB Error handling stranded msg %r", msg_id, exc_info=True ) def finish_registration(self, heart): """Second half of engine registration, called after our HeartMonitor has received a beat from the Engine's Heart.""" try: ec = self.incoming_registrations.pop(heart) except KeyError: self.log.error( f"registration::tried to finish nonexistant registration for {heart}", exc_info=True, ) return duration_ms = int(1000 * (time.monotonic() - ec.registration_started)) self.log.info( f"registration::finished registering engine {ec.id}:{ec.uuid} in {duration_ms}ms" ) if ec.stallback is not None: self.loop.remove_timeout(ec.stallback) eid = ec.id self.ids.add(eid) self.engines[eid] = ec self.by_ident[ec.ident] = ec.id self.queues[eid] = list() self.tasks[eid] = list() self.completed[eid] = list() self.hearts[heart] = eid content = dict(id=eid, uuid=ec.uuid) if self.notifier: self.session.send( self.notifier, "registration_notification", content=content ) self.log.info("engine::Engine Connected: %i", eid) self._save_engine_state() def _purge_stalled_registration(self, heart): # flush monitor messages before purging # first heartbeat might be waiting to be handled self.mon_stream.flush() if heart in self.incoming_registrations: ec = self.incoming_registrations.pop(heart) self.log.warning( f"registration::purging stalled registration {ec.id}:{ec.uuid}" ) # ------------------------------------------------------------------------- # Engine State # ------------------------------------------------------------------------- def _cleanup_engine_state_file(self): """cleanup engine state mapping""" if os.path.exists(self.engine_state_file): self.log.debug("cleaning up engine state: %s", self.engine_state_file) try: os.remove(self.engine_state_file) except IOError: self.log.error( "Couldn't cleanup file: %s", self.engine_state_file, exc_info=True ) def _save_engine_state(self): """save engine mapping to JSON file""" if not self.engine_state_file: return self.log.debug("save engine state to %s" % self.engine_state_file) state = {} engines = {} for eid, ec in self.engines.items(): engines[eid] = ec.uuid state['engines'] = engines state['next_id'] = self._idcounter with open(self.engine_state_file, 'w') as f: json.dump(state, f) def _load_engine_state(self): """load engine mapping from JSON file""" if not os.path.exists(self.engine_state_file): return self.log.info("loading engine state from %s" % self.engine_state_file) with open(self.engine_state_file) as f: state = json.load(f) save_notifier = self.notifier self.notifier = None for eid, uuid in state['engines'].items(): heart = uuid.encode('ascii') self.incoming_registrations[heart] = EngineConnector( id=int(eid), uuid=uuid, ident=heart ) self.finish_registration(heart) self.notifier = save_notifier self._idcounter = state['next_id'] # ------------------------------------------------------------------------- # Client Requests # ------------------------------------------------------------------------- def shutdown_request(self, client_id, msg): """handle shutdown request.""" self.session.send( self.query, 'shutdown_reply', content={'status': 'ok'}, ident=client_id, parent=msg, ) # also notify other clients of shutdown self.session.send( self.notifier, 'shutdown_notification', content={'status': 'ok'}, parent=msg ) self.loop.add_timeout(self.loop.time() + 1, self._shutdown) def _shutdown(self): self.log.info("hub::hub shutting down.") time.sleep(0.1) sys.exit(0) def check_load(self, client_id, msg): content = msg['content'] try: targets = content['targets'] targets = self._validate_targets(targets) except: content = error.wrap_exception() self.session.send( self.query, "hub_error", content=content, ident=client_id, parent=msg ) return content = dict(status='ok') # loads = {} for t in targets: content[bytes(t)] = len(self.queues[t]) + len(self.tasks[t]) self.session.send( self.query, "load_reply", content=content, ident=client_id, parent=msg ) def queue_status(self, client_id, msg): """Return the Queue status of one or more targets. If verbose, return the msg_ids, else return len of each type. Keys: * queue (pending MUX jobs) * tasks (pending Task jobs) * completed (finished jobs from both queues) """ content = msg['content'] targets = content['targets'] try: targets = self._validate_targets(targets) except: content = error.wrap_exception() self.session.send( self.query, "hub_error", content=content, ident=client_id, parent=msg ) return verbose = content.get('verbose', False) content = dict(status='ok') for t in targets: queue = self.queues[t] completed = self.completed[t] tasks = self.tasks[t] if not verbose: queue = len(queue) completed = len(completed) tasks = len(tasks) content[str(t)] = {'queue': queue, 'completed': completed, 'tasks': tasks} content['unassigned'] = ( list(self.unassigned) if verbose else len(self.unassigned) ) # print (content) self.session.send( self.query, "queue_reply", content=content, ident=client_id, parent=msg ) def purge_results(self, client_id, msg): """Purge results from memory. This method is more valuable before we move to a DB based message storage mechanism.""" content = msg['content'] self.log.info("Dropping records with %s", content) msg_ids = content.get('msg_ids', []) reply = dict(status='ok') if msg_ids == 'all': try: self.db.drop_matching_records(dict(completed={'$ne': None})) except Exception: reply = error.wrap_exception() self.log.exception("Error dropping records") else: pending = [m for m in msg_ids if (m in self.pending)] if pending: try: raise IndexError("msg pending: %r" % pending[0]) except: reply = error.wrap_exception() self.log.exception("Error dropping records") else: try: self.db.drop_matching_records(dict(msg_id={'$in': msg_ids})) except Exception: reply = error.wrap_exception() self.log.exception("Error dropping records") if reply['status'] == 'ok': eids = content.get('engine_ids', []) for eid in eids: if eid not in self.engines: try: raise IndexError("No such engine: %i" % eid) except: reply = error.wrap_exception() self.log.exception("Error dropping records") break uid = self.engines[eid].uuid try: self.db.drop_matching_records( dict(engine_uuid=uid, completed={'$ne': None}) ) except Exception: reply = error.wrap_exception() self.log.exception("Error dropping records") break self.session.send( self.query, 'purge_reply', content=reply, ident=client_id, parent=msg ) def resubmit_task(self, client_id, msg): """Resubmit one or more tasks.""" parent = msg def finish(reply): self.session.send( self.query, 'resubmit_reply', content=reply, ident=client_id, parent=parent, ) content = msg['content'] msg_ids = content['msg_ids'] reply = dict(status='ok') try: records = self.db.find_records( {'msg_id': {'$in': msg_ids}}, keys=['header', 'content', 'buffers'] ) except Exception: self.log.error('db::db error finding tasks to resubmit', exc_info=True) return finish(error.wrap_exception()) # validate msg_ids found_ids = [rec['msg_id'] for rec in records] pending_ids = [msg_id for msg_id in found_ids if msg_id in self.pending] if len(records) > len(msg_ids): try: raise RuntimeError( "DB appears to be in an inconsistent state." "More matching records were found than should exist" ) except Exception: self.log.exception("Failed to resubmit task") return finish(error.wrap_exception()) elif len(records) < len(msg_ids): missing = [m for m in msg_ids if m not in found_ids] try: raise KeyError("No such msg(s): %r" % missing) except KeyError: self.log.exception("Failed to resubmit task") return finish(error.wrap_exception()) elif pending_ids: pass # no need to raise on resubmit of pending task, now that we # resubmit under new ID, but do we want to raise anyway? # msg_id = invalid_ids[0] # try: # raise ValueError("Task(s) %r appears to be inflight" % ) # except Exception: # return finish(error.wrap_exception()) # mapping of original IDs to resubmitted IDs resubmitted = {} # send the messages for rec in records: header = rec['header'] msg = self.session.msg(header['msg_type'], parent=header) msg_id = msg['msg_id'] msg['content'] = rec['content'] # use the old header, but update msg_id and timestamp fresh = msg['header'] header['msg_id'] = fresh['msg_id'] header['date'] = fresh['date'] msg['header'] = header self.session.send(self.resubmit, msg, buffers=rec['buffers']) resubmitted[rec['msg_id']] = msg_id self.pending.add(msg_id) msg['buffers'] = rec['buffers'] try: self.db.add_record(msg_id, init_record(msg)) except Exception: self.log.error( "db::DB Error updating record: %s", msg_id, exc_info=True ) return finish(error.wrap_exception()) finish(dict(status='ok', resubmitted=resubmitted)) # store the new IDs in the Task DB for msg_id, resubmit_id in resubmitted.items(): try: self.db.update_record(msg_id, {'resubmitted': resubmit_id}) except Exception: self.log.error( "db::DB Error updating record: %s", msg_id, exc_info=True ) def _extract_record(self, rec): """decompose a TaskRecord dict into subsection of reply for get_result""" io_dict = {} for key in ('execute_input', 'execute_result', 'error', 'stdout', 'stderr'): io_dict[key] = rec[key] content = { 'header': rec['header'], 'metadata': rec['metadata'], 'result_metadata': rec['result_metadata'], 'result_header': rec['result_header'], 'result_content': rec['result_content'], 'received': rec['received'], 'io': io_dict, } if rec['result_buffers']: buffers = list(rec['result_buffers']) else: buffers = [] return content, buffers def get_results(self, client_id, msg): """Get the result of 1 or more messages.""" content = msg['content'] msg_ids = sorted(set(content['msg_ids'])) statusonly = content.get('status_only', False) pending = [] completed = [] content = dict(status='ok') content['pending'] = pending content['completed'] = completed buffers = [] if not statusonly: try: matches = self.db.find_records(dict(msg_id={'$in': msg_ids})) # turn match list into dict, for faster lookup records = {} for rec in matches: records[rec['msg_id']] = rec except Exception: content = error.wrap_exception() self.log.exception("Failed to get results") self.session.send( self.query, "result_reply", content=content, parent=msg, ident=client_id, ) return else: records = {} for msg_id in msg_ids: if msg_id in self.pending: pending.append(msg_id) elif msg_id in self.all_completed: completed.append(msg_id) if not statusonly: c, bufs = self._extract_record(records[msg_id]) content[msg_id] = c buffers.extend(bufs) elif msg_id in records: if rec['completed']: completed.append(msg_id) c, bufs = self._extract_record(records[msg_id]) content[msg_id] = c buffers.extend(bufs) else: pending.append(msg_id) else: try: raise KeyError('No such message: ' + msg_id) except: content = error.wrap_exception() break self.session.send( self.query, "result_reply", content=content, parent=msg, ident=client_id, buffers=buffers, ) def get_history(self, client_id, msg): """Get a list of all msg_ids in our DB records""" try: msg_ids = self.db.get_history() except Exception as e: content = error.wrap_exception() self.log.exception("Failed to get history") else: content = dict(status='ok', history=msg_ids) self.session.send( self.query, "history_reply", content=content, parent=msg, ident=client_id ) def db_query(self, client_id, msg): """Perform a raw query on the task record database.""" content = msg['content'] query = extract_dates(content.get('query', {})) keys = content.get('keys', None) buffers = [] empty = list() try: records = self.db.find_records(query, keys) except Exception as e: content = error.wrap_exception() self.log.exception("DB query failed") else: # extract buffers from reply content: if keys is not None: buffer_lens = [] if 'buffers' in keys else None result_buffer_lens = [] if 'result_buffers' in keys else None else: buffer_lens = None result_buffer_lens = None for rec in records: # buffers may be None, so double check b = rec.pop('buffers', empty) or empty if buffer_lens is not None: buffer_lens.append(len(b)) buffers.extend(b) rb = rec.pop('result_buffers', empty) or empty if result_buffer_lens is not None: result_buffer_lens.append(len(rb)) buffers.extend(rb) content = dict( status='ok', records=records, buffer_lens=buffer_lens, result_buffer_lens=result_buffer_lens, ) # self.log.debug (content) self.session.send( self.query, "db_reply", content=content, parent=msg, ident=client_id, buffers=buffers, ) async def become_dask(self, client_id, msg): """Start a dask.distributed Scheduler.""" if self.distributed_scheduler is None: kwargs = msg['content'].get('scheduler_args', {}) self.log.info("Becoming dask.distributed scheduler: %s", kwargs) from distributed import Scheduler self.distributed_scheduler = scheduler = Scheduler(**kwargs) await scheduler.start() content = { 'status': 'ok', 'ip': self.distributed_scheduler.ip, 'address': self.distributed_scheduler.address, 'port': self.distributed_scheduler.port, } self.log.info( "dask.distributed scheduler running at {address}".format(**content) ) self.session.send( self.query, "become_dask_reply", content=content, parent=msg, ident=client_id, ) def stop_distributed(self, client_id, msg): """Start a dask.distributed Scheduler.""" if self.distributed_scheduler is not None: self.log.info("Stopping dask.distributed scheduler") self.distributed_scheduler.close() self.distributed_scheduler = None else: self.log.info("No dask.distributed scheduler running.") content = {'status': 'ok'} self.session.send( self.query, "stop_distributed_reply", content=content, parent=msg, ident=client_id, ) ipyparallel-7.1.0/ipyparallel/controller/mongodb.py000066400000000000000000000077421412533144600225520ustar00rootroot00000000000000"""A TaskRecord backend using mongodb """ try: from pymongo import MongoClient except ImportError: from pymongo import Connection as MongoClient # bson.Binary import moved try: from bson.binary import Binary except ImportError: from bson import Binary from traitlets import Dict, List, Unicode, Instance from .dictdb import BaseDB # ----------------------------------------------------------------------------- # MongoDB class # ----------------------------------------------------------------------------- class MongoDB(BaseDB): """MongoDB TaskRecord backend.""" connection_args = List( config=True, help="""Positional arguments to be passed to pymongo.MongoClient. Only necessary if the default mongodb configuration does not point to your mongod instance.""", ) connection_kwargs = Dict( config=True, help="""Keyword arguments to be passed to pymongo.MongoClient. Only necessary if the default mongodb configuration does not point to your mongod instance.""", ) database = Unicode( "ipython-tasks", config=True, help="""The MongoDB database name to use for storing tasks for this session. If unspecified, a new database will be created with the Hub's IDENT. Specifying the database will result in tasks from previous sessions being available via Clients' db_query and get_result methods.""", ) _connection = Instance(MongoClient, allow_none=True) # pymongo connection def __init__(self, **kwargs): super(MongoDB, self).__init__(**kwargs) if self._connection is None: self._connection = MongoClient( *self.connection_args, **self.connection_kwargs ) if not self.database: self.database = self.session self._db = self._connection[self.database] self._records = self._db['task_records'] self._records.ensure_index('msg_id', unique=True) self._records.ensure_index('submitted') # for sorting history # for rec in self._records.find def _binary_buffers(self, rec): for key in ('buffers', 'result_buffers'): if rec.get(key, None): rec[key] = list(map(Binary, rec[key])) return rec def add_record(self, msg_id, rec): """Add a new Task Record, by msg_id.""" # print rec rec = self._binary_buffers(rec) self._records.insert(rec) def get_record(self, msg_id): """Get a specific Task Record, by msg_id.""" r = self._records.find_one({'msg_id': msg_id}) if not r: # r will be '' if nothing is found raise KeyError(msg_id) return r def update_record(self, msg_id, rec): """Update the data in an existing record.""" rec = self._binary_buffers(rec) self._records.update({'msg_id': msg_id}, {'$set': rec}) def drop_matching_records(self, check): """Remove a record from the DB.""" self._records.remove(check) def drop_record(self, msg_id): """Remove a record from the DB.""" self._records.remove({'msg_id': msg_id}) def find_records(self, check, keys=None): """Find records matching a query dict, optionally extracting subset of keys. Returns list of matching records. Parameters ---------- check : dict mongodb-style query argument keys : list of strs [optional] if specified, the subset of keys to extract. msg_id will *always* be included. """ if keys and 'msg_id' not in keys: keys.append('msg_id') matches = list(self._records.find(check, keys)) for rec in matches: rec.pop('_id') return matches def get_history(self): """get all msg_ids, ordered by time submitted.""" cursor = self._records.find({}, {'msg_id': 1}).sort('submitted') return [rec['msg_id'] for rec in cursor] ipyparallel-7.1.0/ipyparallel/controller/scheduler.py000066400000000000000000000151561412533144600231010ustar00rootroot00000000000000"""The Python scheduler for rich scheduling. The Pure ZMQ scheduler does not allow routing schemes other than LRU, nor does it check msg_id DAG dependencies. For those, a slightly slower Python Scheduler exists. """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import logging import jupyter_client.session import traitlets.log import zmq from decorator import decorator from tornado import ioloop from traitlets import Bytes from traitlets import default from traitlets import Instance from traitlets import Set from traitlets.config import Config from traitlets.config import LoggingConfigurable from zmq.eventloop import zmqstream from ipyparallel import util from ipyparallel.util import connect_logger from ipyparallel.util import local_logger # local imports # performance optimization: skip unused date deserialization jupyter_client.session.extract_dates = lambda obj: obj @decorator def logged(f, self, *args, **kwargs): # print ("#--------------------") self.log.debug("scheduler::%s(*%s,**%s)", f.__name__, args, kwargs) # print ("#--") return f(self, *args, **kwargs) class Scheduler(LoggingConfigurable): loop = Instance(ioloop.IOLoop) @default("loop") def _default_loop(self): return ioloop.IOLoop.current() session = Instance(jupyter_client.session.Session) @default("session") def _default_session(self): return jupyter_client.session.Session(parent=self) client_stream = Instance( zmqstream.ZMQStream, allow_none=True ) # client-facing stream engine_stream = Instance( zmqstream.ZMQStream, allow_none=True ) # engine-facing stream notifier_stream = Instance( zmqstream.ZMQStream, allow_none=True ) # hub-facing sub stream mon_stream = Instance(zmqstream.ZMQStream, allow_none=True) # hub-facing pub stream query_stream = Instance( zmqstream.ZMQStream, allow_none=True ) # hub-facing DEALER stream all_completed = Set() # set of all completed tasks all_failed = Set() # set of all failed tasks all_done = Set() # set of all finished tasks=union(completed,failed) all_ids = Set() # set of all submitted task IDs ident = Bytes() # ZMQ identity. This should just be self.session.session as bytes # but ensure Bytes @default("ident") def _ident_default(self): return self.session.bsession def start(self): self.engine_stream.on_recv(self.dispatch_result, copy=False) self.client_stream.on_recv(self.dispatch_submission, copy=False) def resume_receiving(self): """Resume accepting jobs.""" self.client_stream.on_recv(self.dispatch_submission, copy=False) def stop_receiving(self): """Stop accepting jobs while there are no engines. Leave them in the ZMQ queue.""" self.client_stream.on_recv(None) def dispatch_result(self, raw_msg): raise NotImplementedError("Implement in subclasses") def dispatch_submission(self, raw_msg): raise NotImplementedError("Implement in subclasses") def append_new_msg_id_to_msg(self, new_id, target_id, idents, msg): if isinstance(target_id, str): target_id = target_id.encode("utf8") new_idents = [target_id] + idents msg['header']['msg_id'] = new_id new_msg_list = self.session.serialize(msg, ident=new_idents) new_msg_list.extend(msg['buffers']) return new_msg_list def get_new_msg_id(self, original_msg_id, outgoing_id): return f'{original_msg_id}_{outgoing_id if isinstance(outgoing_id, str) else outgoing_id.decode("utf8")}' ZMQStream = zmqstream.ZMQStream def get_common_scheduler_streams( mon_addr, not_addr, reg_addr, config, logname, log_url, loglevel, in_thread, curve_serverkey, curve_publickey, curve_secretkey, ): if config: # unwrap dict back into Config config = Config(config) if in_thread: # use instance() to get the same Context/Loop as our parent ctx = zmq.Context.instance() loop = ioloop.IOLoop.current() else: # in a process, don't use instance() # for safety with multiprocessing ctx = zmq.Context() loop = ioloop.IOLoop() loop.make_current() def connect(s, addr): return util.connect( s, addr, curve_serverkey=curve_serverkey, curve_secretkey=curve_secretkey, curve_publickey=curve_publickey, ) mons = zmqstream.ZMQStream(ctx.socket(zmq.PUB), loop) connect(mons, mon_addr) nots = zmqstream.ZMQStream(ctx.socket(zmq.SUB), loop) nots.setsockopt(zmq.SUBSCRIBE, b'') connect(nots, not_addr) querys = ZMQStream(ctx.socket(zmq.DEALER), loop) connect(querys, reg_addr) # setup logging. if in_thread: log = traitlets.log.get_logger() else: if log_url: log = connect_logger( logname, ctx, log_url, root="scheduler", loglevel=loglevel ) else: log = local_logger(logname, loglevel) return config, ctx, loop, mons, nots, querys, log def launch_scheduler( scheduler_class, in_addr, out_addr, mon_addr, not_addr, reg_addr, config=None, logname='root', log_url=None, loglevel=logging.DEBUG, identity=None, in_thread=False, curve_secretkey=None, curve_publickey=None, ): config, ctx, loop, mons, nots, querys, log = get_common_scheduler_streams( mon_addr, not_addr, reg_addr, config, logname, log_url, loglevel, in_thread, curve_serverkey=curve_publickey, curve_publickey=curve_publickey, curve_secretkey=curve_secretkey, ) util.set_hwm(mons, 0) ins = ZMQStream(ctx.socket(zmq.ROUTER), loop) util.set_hwm(ins, 0) if identity: ins.setsockopt(zmq.IDENTITY, identity + b'_in') util.bind(ins, in_addr, curve_secretkey=curve_secretkey) outs = ZMQStream(ctx.socket(zmq.ROUTER), loop) util.set_hwm(outs, 0) if identity: outs.setsockopt(zmq.IDENTITY, identity + b'_out') util.bind(outs, out_addr, curve_secretkey=curve_secretkey) scheduler = scheduler_class( client_stream=ins, engine_stream=outs, mon_stream=mons, notifier_stream=nots, query_stream=querys, loop=loop, log=log, config=config, ) scheduler.start() if not in_thread: try: loop.start() except KeyboardInterrupt: scheduler.log.critical("Interrupted, exiting...") ipyparallel-7.1.0/ipyparallel/controller/sqlitedb.py000066400000000000000000000347231412533144600227330ustar00rootroot00000000000000"""A TaskRecord backend using sqlite3""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import json import os try: import cPickle as pickle except ImportError: import pickle from datetime import datetime try: import sqlite3 except ImportError: sqlite3 = None from dateutil.parser import parse as dateutil_parse from tornado import ioloop from traitlets import Unicode, Instance, List, Dict from jupyter_client.jsonutil import date_default from .dictdb import BaseDB from ..util import ensure_timezone, extract_dates # ----------------------------------------------------------------------------- # SQLite operators, adapters, and converters # ----------------------------------------------------------------------------- operators = { '$lt': "<", '$gt': ">", # null is handled weird with ==,!= '$eq': "=", '$ne': "!=", '$lte': "<=", '$gte': ">=", '$in': ('=', ' OR '), '$nin': ('!=', ' AND '), # '$all': None, # '$mod': None, # '$exists' : None } null_operators = { '=': "IS NULL", '!=': "IS NOT NULL", } def _adapt_dict(d): return json.dumps(d, default=date_default) def _convert_dict(ds): if ds is None: return ds else: if isinstance(ds, bytes): # If I understand the sqlite doc correctly, this will always be utf8 ds = ds.decode('utf8') return extract_dates(json.loads(ds)) def _adapt_bufs(bufs): # this is *horrible* # copy buffers into single list and pickle it: if bufs and isinstance(bufs[0], (bytes, memoryview)): return sqlite3.Binary( pickle.dumps([memoryview(buf).tobytes() for buf in bufs], -1) ) elif bufs: return bufs else: return None def _convert_bufs(bs): if bs is None: return [] else: return pickle.loads(bytes(bs)) def _adapt_timestamp(dt): """Adapt datetime to text""" return ensure_timezone(dt).isoformat() def _convert_timestamp(s): """Adapt text timestamp to datetime""" return ensure_timezone(dateutil_parse(s)) # ----------------------------------------------------------------------------- # SQLiteDB class # ----------------------------------------------------------------------------- class SQLiteDB(BaseDB): """SQLite3 TaskRecord backend.""" filename = Unicode( 'tasks.db', config=True, help="""The filename of the sqlite task database. [default: 'tasks.db']""", ) location = Unicode( '', config=True, help="""The directory containing the sqlite task database. The default is to use the cluster_dir location.""", ) table = Unicode( "ipython-tasks", config=True, help="""The SQLite Table to use for storing tasks for this session. If unspecified, a new table will be created with the Hub's IDENT. Specifying the table will result in tasks from previous sessions being available via Clients' db_query and get_result methods.""", ) if sqlite3 is not None: _db = Instance('sqlite3.Connection', allow_none=True) else: _db = None # the ordered list of column names _keys = List( [ 'msg_id', 'header', 'metadata', 'content', 'buffers', 'submitted', 'client_uuid', 'engine_uuid', 'started', 'completed', 'resubmitted', 'received', 'result_header', 'result_metadata', 'result_content', 'result_buffers', 'queue', 'execute_input', 'execute_result', 'error', 'stdout', 'stderr', ] ) # sqlite datatypes for checking that db is current format _types = Dict( { 'msg_id': 'text', 'header': 'dict text', 'metadata': 'dict text', 'content': 'dict text', 'buffers': 'bufs blob', 'submitted': 'timestamp', 'client_uuid': 'text', 'engine_uuid': 'text', 'started': 'timestamp', 'completed': 'timestamp', 'resubmitted': 'text', 'received': 'timestamp', 'result_header': 'dict text', 'result_metadata': 'dict text', 'result_content': 'dict text', 'result_buffers': 'bufs blob', 'queue': 'text', 'execute_input': 'text', 'execute_result': 'text', 'error': 'text', 'stdout': 'text', 'stderr': 'text', } ) def __init__(self, **kwargs): super(SQLiteDB, self).__init__(**kwargs) if sqlite3 is None: raise ImportError("SQLiteDB requires sqlite3") if not self.table: # use session, and prefix _, since starting with # is illegal self.table = '_' + self.session.replace('-', '_') if not self.location: # get current profile from IPython.core.application import BaseIPythonApplication if BaseIPythonApplication.initialized(): app = BaseIPythonApplication.instance() if app.profile_dir is not None: self.location = app.profile_dir.location else: self.location = u'.' else: self.location = u'.' self._init_db() # register db commit as 2s periodic callback # to prevent clogging pipes # assumes we are being run in a zmq ioloop app self._commit_callback = pc = ioloop.PeriodicCallback(self._db.commit, 2000) pc.start() def close(self): self._commit_callback.stop() self._db.commit() self._db.close() def _defaults(self, keys=None): """create an empty record""" d = {} keys = self._keys if keys is None else keys for key in keys: d[key] = None return d def _check_table(self): """Ensure that an incorrect table doesn't exist If a bad (old) table does exist, return False """ cursor = self._db.execute("PRAGMA table_info('%s')" % self.table) lines = cursor.fetchall() if not lines: # table does not exist return True types = {} keys = [] for line in lines: keys.append(line[1]) types[line[1]] = line[2] if self._keys != keys: # key mismatch self.log.warning('keys mismatch') return False for key in self._keys: if types[key] != self._types[key]: self.log.warning( 'type mismatch: %s: %s != %s' % (key, types[key], self._types[key]) ) return False return True def _init_db(self): """Connect to the database and get new session number.""" # register adapters sqlite3.register_adapter(dict, _adapt_dict) sqlite3.register_converter('dict', _convert_dict) sqlite3.register_adapter(list, _adapt_bufs) sqlite3.register_converter('bufs', _convert_bufs) sqlite3.register_adapter(datetime, _adapt_timestamp) sqlite3.register_converter('timestamp', _convert_timestamp) # connect to the db dbfile = os.path.join(self.location, self.filename) self._db = sqlite3.connect( dbfile, detect_types=sqlite3.PARSE_DECLTYPES, # isolation_level = None)#, cached_statements=64, ) # print dir(self._db) first_table = previous_table = self.table i = 0 while not self._check_table(): i += 1 self.table = first_table + '_%i' % i self.log.warning( "Table %s exists and doesn't match db format, trying %s" % (previous_table, self.table) ) previous_table = self.table self._db.execute( """CREATE TABLE IF NOT EXISTS '%s' (msg_id text PRIMARY KEY, header dict text, metadata dict text, content dict text, buffers bufs blob, submitted timestamp, client_uuid text, engine_uuid text, started timestamp, completed timestamp, resubmitted text, received timestamp, result_header dict text, result_metadata dict text, result_content dict text, result_buffers bufs blob, queue text, execute_input text, execute_result text, error text, stdout text, stderr text) """ % self.table ) self._db.commit() def _dict_to_list(self, d): """turn a mongodb-style record dict into a list.""" return [d[key] for key in self._keys] def _list_to_dict(self, line, keys=None): """Inverse of dict_to_list""" keys = self._keys if keys is None else keys d = self._defaults(keys) for key, value in zip(keys, line): d[key] = value return d def _render_expression(self, check): """Turn a mongodb-style search dict into an SQL query.""" expressions = [] args = [] skeys = set(check.keys()) skeys.difference_update(set(self._keys)) skeys.difference_update(set(['buffers', 'result_buffers'])) if skeys: raise KeyError("Illegal testing key(s): %s" % skeys) for name, sub_check in check.items(): if isinstance(sub_check, dict): for test, value in sub_check.items(): try: op = operators[test] except KeyError: raise KeyError("Unsupported operator: %r" % test) if isinstance(op, tuple): op, join = op if value is None and op in null_operators: expr = "%s %s" % (name, null_operators[op]) else: expr = "%s %s ?" % (name, op) if isinstance(value, (tuple, list)): if op in null_operators and any([v is None for v in value]): # equality tests don't work with NULL raise ValueError( "Cannot use %r test with NULL values on SQLite backend" % test ) expr = '( %s )' % (join.join([expr] * len(value))) args.extend(value) else: args.append(value) expressions.append(expr) else: # it's an equality check if sub_check is None: expressions.append("%s IS NULL" % name) else: expressions.append("%s = ?" % name) args.append(sub_check) expr = " AND ".join(expressions) return expr, args def add_record(self, msg_id, rec): """Add a new Task Record, by msg_id.""" d = self._defaults() d.update(rec) d['msg_id'] = msg_id line = self._dict_to_list(d) tups = '(%s)' % (','.join(['?'] * len(line))) self._db.execute("INSERT INTO '%s' VALUES %s" % (self.table, tups), line) # self._db.commit() def get_record(self, msg_id): """Get a specific Task Record, by msg_id.""" cursor = self._db.execute( """SELECT * FROM '%s' WHERE msg_id==?""" % self.table, (msg_id,) ) line = cursor.fetchone() if line is None: raise KeyError("No such msg: %r" % msg_id) return self._list_to_dict(line) def update_record(self, msg_id, rec): """Update the data in an existing record.""" query = "UPDATE '%s' SET " % self.table sets = [] keys = sorted(rec.keys()) values = [] for key in keys: sets.append('%s = ?' % key) values.append(rec[key]) query += ', '.join(sets) query += ' WHERE msg_id == ?' values.append(msg_id) self._db.execute(query, values) # self._db.commit() def drop_record(self, msg_id): """Remove a record from the DB.""" self._db.execute("""DELETE FROM '%s' WHERE msg_id==?""" % self.table, (msg_id,)) # self._db.commit() def drop_matching_records(self, check): """Remove a record from the DB.""" expr, args = self._render_expression(check) query = "DELETE FROM '%s' WHERE %s" % (self.table, expr) self._db.execute(query, args) # self._db.commit() def find_records(self, check, keys=None): """Find records matching a query dict, optionally extracting subset of keys. Returns list of matching records. Parameters ---------- check : dict mongodb-style query argument keys : list of strs [optional] if specified, the subset of keys to extract. msg_id will *always* be included. """ if keys: bad_keys = [key for key in keys if key not in self._keys] if bad_keys: raise KeyError("Bad record key(s): %s" % bad_keys) if keys: # ensure msg_id is present and first: if 'msg_id' in keys: keys.remove('msg_id') keys.insert(0, 'msg_id') req = ', '.join(keys) else: req = '*' expr, args = self._render_expression(check) query = """SELECT %s FROM '%s' WHERE %s""" % (req, self.table, expr) cursor = self._db.execute(query, args) matches = cursor.fetchall() records = [] for line in matches: rec = self._list_to_dict(line, keys) records.append(rec) return records def get_history(self): """get all msg_ids, ordered by time submitted.""" query = """SELECT msg_id FROM '%s' ORDER by submitted ASC""" % self.table cursor = self._db.execute(query) # will be a list of length 1 tuples return [tup[0] for tup in cursor.fetchall()] __all__ = ['SQLiteDB'] ipyparallel-7.1.0/ipyparallel/controller/task_scheduler.py000066400000000000000000000653041412533144600241230ustar00rootroot00000000000000import time from collections import deque from random import randint from random import random from types import FunctionType import zmq from traitlets import Dict from traitlets import Enum from traitlets import Instance from traitlets import Integer from traitlets import List from traitlets import observe from ipyparallel import Dependency from ipyparallel import error from ipyparallel import util from ipyparallel.controller.scheduler import Scheduler try: import numpy except ImportError: numpy = None # ---------------------------------------------------------------------- # Chooser functions # ---------------------------------------------------------------------- def plainrandom(loads): """Plain random pick.""" n = len(loads) return randint(0, n - 1) def lru(loads): """Always pick the front of the line. The content of `loads` is ignored. Assumes LRU ordering of loads, with oldest first. """ return 0 def twobin(loads): """Pick two at random, use the LRU of the two. The content of loads is ignored. Assumes LRU ordering of loads, with oldest first. """ n = len(loads) a = randint(0, n - 1) b = randint(0, n - 1) return min(a, b) def weighted(loads): """Pick two at random using inverse load as weight. Return the less loaded of the two. """ # weight 0 a million times more than 1: weights = 1.0 / (1e-6 + numpy.array(loads)) sums = weights.cumsum() t = sums[-1] x = random() * t y = random() * t idx = 0 idy = 0 while sums[idx] < x: idx += 1 while sums[idy] < y: idy += 1 if weights[idy] > weights[idx]: return idy else: return idx def leastload(loads): """Always choose the lowest load. If the lowest load occurs more than once, the first occurance will be used. If loads has LRU ordering, this means the LRU of those with the lowest load is chosen. """ return loads.index(min(loads)) # --------------------------------------------------------------------- # Classes # --------------------------------------------------------------------- # store empty default dependency: MET = Dependency([]) class Job: """Simple container for a job""" def __init__( self, msg_id, raw_msg, idents, msg, header, metadata, targets, after, follow, timeout, ): self.msg_id = msg_id self.raw_msg = raw_msg self.idents = idents self.msg = msg self.header = header self.metadata = metadata self.targets = targets self.after = after self.follow = follow self.timeout = timeout self.removed = False # used for lazy-delete from sorted queue self.timestamp = time.time() self.timeout_id = 0 self.blacklist = set() def __lt__(self, other): return self.timestamp < other.timestamp @property def dependents(self): return self.follow.union(self.after) class TaskScheduler(Scheduler): """Python TaskScheduler object. This is the simplest object that supports msg_id based DAG dependencies. *Only* task msg_ids are checked, not msg_ids of jobs submitted via the MUX queue. """ hwm = Integer( 1, config=True, help="""specify the High Water Mark (HWM) for the downstream socket in the Task scheduler. This is the maximum number of allowed outstanding tasks on each engine. The default (1) means that only one task can be outstanding on each engine. Setting TaskScheduler.hwm=0 means there is no limit, and the engines continue to be assigned tasks while they are working, effectively hiding network latency behind computation, but can result in an imbalance of work when submitting many heterogenous tasks all at once. Any positive value greater than one is a compromise between the two. """, ) scheme_name = Enum( ('leastload', 'pure', 'lru', 'plainrandom', 'weighted', 'twobin'), 'leastload', config=True, help="""select the task scheduler scheme [default: Python LRU] Options are: 'pure', 'lru', 'plainrandom', 'weighted', 'twobin','leastload'""", ) # input arguments: scheme = Instance(FunctionType) # function for determining the destination @observe('scheme_name') def _scheme_name_changed(self, change): self.log.debug("Using scheme %r" % change['new']) self.scheme = globals()[change['new']] # input arguments: scheme = Instance(FunctionType) # function for determining the destination def _scheme_default(self): return leastload # internals: queue = Instance(deque) # sorted list of Jobs def _queue_default(self): return deque() queue_map = Dict() # dict by msg_id of Jobs (for O(1) access to the Queue) graph = Dict() # dict by msg_id of [ msg_ids that depend on key ] retries = Dict() # dict by msg_id of retries remaining (non-neg ints) # waiting = List() # list of msg_ids ready to run, but haven't due to HWM pending = Dict() # dict by engine_uuid of submitted tasks completed = Dict() # dict by engine_uuid of completed tasks failed = Dict() # dict by engine_uuid of failed tasks destinations = ( Dict() ) # dict by msg_id of engine_uuids where jobs ran (reverse of completed+failed) clients = Dict() # dict by msg_id for who submitted the task targets = List() # list of target IDENTs loads = List() # list of engine loads # full = Set() # set of IDENTs that have HWM outstanding tasks def start(self): super().start() self.query_stream.on_recv(self.dispatch_query_reply) self.session.send(self.query_stream, "connection_request", {}) self._notification_handlers = dict( registration_notification=self._register_engine, unregistration_notification=self._unregister_engine, ) self.log.info("Task scheduler started [%s]" % self.scheme_name) self.notifier_stream.on_recv(self.dispatch_notification) # ----------------------------------------------------------------------- # [Un]Registration Handling # ----------------------------------------------------------------------- def dispatch_query_reply(self, msg): """handle reply to our initial connection request""" try: idents, msg = self.session.feed_identities(msg) except ValueError: self.log.warning("task::Invalid Message: %r", msg) return try: msg = self.session.deserialize(msg) except ValueError: self.log.warning("task::Unauthorized message from: %r" % idents) return content = msg['content'] for uuid in content.get('engines', {}).values(): self._register_engine(uuid.encode("utf8")) @util.log_errors def dispatch_notification(self, msg): """dispatch register/unregister events.""" try: idents, msg = self.session.feed_identities(msg) except ValueError: self.log.warning("task::Invalid Message: %r", msg) return try: msg = self.session.deserialize(msg) except ValueError: self.log.warning("task::Unauthorized message from: %r" % idents) return msg_type = msg['header']['msg_type'] handler = self._notification_handlers.get(msg_type, None) if handler is None: self.log.error("Unhandled message type: %r" % msg_type) else: try: handler(msg['content']['uuid'].encode("utf8")) except Exception: self.log.error("task::Invalid notification msg: %r", msg, exc_info=True) def _register_engine(self, uid): """New engine with ident `uid` became available.""" # head of the line: self.targets.insert(0, uid) self.loads.insert(0, 0) # initialize sets self.completed[uid] = set() self.failed[uid] = set() self.pending[uid] = {} # rescan the graph: self.update_graph(None) def _unregister_engine(self, uid): """Existing engine with ident `uid` became unavailable.""" if len(self.targets) == 1: # this was our only engine pass # handle any potentially finished tasks: self.engine_stream.flush() # don't pop destinations, because they might be used later # map(self.destinations.pop, self.completed.pop(uid)) # map(self.destinations.pop, self.failed.pop(uid)) # prevent this engine from receiving work idx = self.targets.index(uid) self.targets.pop(idx) self.loads.pop(idx) # wait 5 seconds before cleaning up pending jobs, since the results might # still be incoming if self.pending[uid]: self.loop.add_timeout( self.loop.time() + 5, lambda: self.handle_stranded_tasks(uid) ) else: self.completed.pop(uid) self.failed.pop(uid) def handle_stranded_tasks(self, engine): """Deal with jobs resident in an engine that died.""" lost = self.pending[engine] for msg_id in list(lost.keys()): if msg_id not in lost: # prevent double-handling of messages continue raw_msg = lost[msg_id].raw_msg idents, msg = self.session.feed_identities(raw_msg, copy=False) parent = self.session.unpack(msg[1].bytes) idents = [engine, idents[0]] # build fake error reply try: raise error.EngineError( "Engine %r died while running task %r" % (engine, msg_id) ) except: content = error.wrap_exception() # build fake metadata md = dict( status=u'error', engine=engine.decode('ascii'), date=util.utcnow() ) msg = self.session.msg('apply_reply', content, parent=parent, metadata=md) raw_reply = list( map(zmq.Message, self.session.serialize(msg, ident=idents)) ) # and dispatch it self.dispatch_result(raw_reply) # finally scrub completed/failed lists self.completed.pop(engine) self.failed.pop(engine) # ----------------------------------------------------------------------- # Job Submission # ----------------------------------------------------------------------- @util.log_errors def dispatch_submission(self, raw_msg): """Dispatch job submission to appropriate handlers.""" # ensure targets up to date: self.notifier_stream.flush() try: idents, msg = self.session.feed_identities(raw_msg, copy=False) msg = self.session.deserialize(msg, content=False, copy=False) except Exception: self.log.error("task::Invaid task msg: %r" % raw_msg, exc_info=True) return # send to monitor self.mon_stream.send_multipart([b'intask'] + raw_msg, copy=False) header = msg['header'] md = msg['metadata'] msg_id = header['msg_id'] self.all_ids.add(msg_id) # get targets as a set of bytes objects # from a list of unicode objects targets = md.get('targets', []) targets = set(t.encode("utf8", "replace") for t in targets) retries = md.get('retries', 0) self.retries[msg_id] = retries # time dependencies after = md.get('after', None) if after: after = Dependency(after) if after.all: if after.success: after = Dependency( after.difference(self.all_completed), success=after.success, failure=after.failure, all=after.all, ) if after.failure: after = Dependency( after.difference(self.all_failed), success=after.success, failure=after.failure, all=after.all, ) if after.check(self.all_completed, self.all_failed): # recast as empty set, if `after` already met, # to prevent unnecessary set comparisons after = MET else: after = MET # location dependencies follow = Dependency(md.get('follow', [])) timeout = md.get('timeout', None) if timeout: timeout = float(timeout) job = Job( msg_id=msg_id, raw_msg=raw_msg, idents=idents, msg=msg, header=header, targets=targets, after=after, follow=follow, timeout=timeout, metadata=md, ) # validate and reduce dependencies: for dep in after, follow: if not dep: # empty dependency continue # check valid: if msg_id in dep or dep.difference(self.all_ids): self.queue_map[msg_id] = job return self.fail_unreachable(msg_id, error.InvalidDependency) # check if unreachable: if dep.unreachable(self.all_completed, self.all_failed): self.queue_map[msg_id] = job return self.fail_unreachable(msg_id) if after.check(self.all_completed, self.all_failed): # time deps already met, try to run if not self.maybe_run(job): # can't run yet if msg_id not in self.all_failed: # could have failed as unreachable self.save_unmet(job) else: self.save_unmet(job) def job_timeout(self, job, timeout_id): """callback for a job's timeout. The job may or may not have been run at this point. """ if job.timeout_id != timeout_id: # not the most recent call return now = time.time() if job.timeout >= (now + 1): self.log.warning( "task %s timeout fired prematurely: %s > %s", job.msg_id, job.timeout, now, ) if job.msg_id in self.queue_map: # still waiting, but ran out of time self.log.info("task %r timed out", job.msg_id) self.fail_unreachable(job.msg_id, error.TaskTimeout) def fail_unreachable(self, msg_id, why=error.ImpossibleDependency): """a task has become unreachable, send a reply with an ImpossibleDependency error.""" if msg_id not in self.queue_map: self.log.error("task %r already failed!", msg_id) return job = self.queue_map.pop(msg_id) # lazy-delete from the queue job.removed = True for mid in job.dependents: if mid in self.graph: self.graph[mid].remove(msg_id) try: raise why() except: content = error.wrap_exception() self.log.debug( "task %r failing as unreachable with: %s", msg_id, content['ename'] ) self.all_done.add(msg_id) self.all_failed.add(msg_id) msg = self.session.send( self.client_stream, 'apply_reply', content, parent=job.header, ident=job.idents, ) self.session.send(self.mon_stream, msg, ident=[b'outtask'] + job.idents) self.update_graph(msg_id, success=False) def available_engines(self): """return a list of available engine indices based on HWM""" if not self.hwm: return list(range(len(self.targets))) available = [] for idx in range(len(self.targets)): if self.loads[idx] < self.hwm: available.append(idx) return available def maybe_run(self, job): """check location dependencies, and run if they are met.""" msg_id = job.msg_id self.log.debug("Attempting to assign task %s", msg_id) available = self.available_engines() if not available: # no engines, definitely can't run return False if job.follow or job.targets or job.blacklist or self.hwm: # we need a can_run filter def can_run(idx): # check hwm if self.hwm and self.loads[idx] == self.hwm: return False target = self.targets[idx] # check blacklist if target in job.blacklist: return False # check targets if job.targets and target not in job.targets: return False # check follow return job.follow.check(self.completed[target], self.failed[target]) indices = list(filter(can_run, available)) if not indices: # couldn't run if job.follow.all: # check follow for impossibility dests = set() relevant = set() if job.follow.success: relevant = self.all_completed if job.follow.failure: relevant = relevant.union(self.all_failed) for m in job.follow.intersection(relevant): dests.add(self.destinations[m]) if len(dests) > 1: self.queue_map[msg_id] = job self.fail_unreachable(msg_id) return False if job.targets: # check blacklist+targets for impossibility job.targets.difference_update(job.blacklist) if not job.targets or not job.targets.intersection(self.targets): self.queue_map[msg_id] = job self.fail_unreachable(msg_id) return False return False else: indices = None self.submit_task(job, indices) return True def save_unmet(self, job): """Save a message for later submission when its dependencies are met.""" msg_id = job.msg_id self.log.debug("Adding task %s to the queue", msg_id) self.queue_map[msg_id] = job self.queue.append(job) # track the ids in follow or after, but not those already finished for dep_id in job.after.union(job.follow).difference(self.all_done): if dep_id not in self.graph: self.graph[dep_id] = set() self.graph[dep_id].add(msg_id) # schedule timeout callback if job.timeout: timeout_id = job.timeout_id = job.timeout_id + 1 self.loop.add_timeout( time.time() + job.timeout, lambda: self.job_timeout(job, timeout_id) ) def submit_task(self, job, indices=None): """Submit a task to any of a subset of our targets.""" if indices: loads = [self.loads[i] for i in indices] else: loads = self.loads idx = self.scheme(loads) if indices: idx = indices[idx] target = self.targets[idx] # print (target, map(str, msg[:3])) # send job to the engine self.engine_stream.send(target, flags=zmq.SNDMORE, copy=False) self.engine_stream.send_multipart(job.raw_msg, copy=False) # update load self.add_job(idx) self.pending[target][job.msg_id] = job # notify Hub content = dict(msg_id=job.msg_id, engine_id=target.decode('ascii')) self.session.send( self.mon_stream, 'task_destination', content=content, ident=[b'tracktask', self.ident], ) # ----------------------------------------------------------------------- # Result Handling # ----------------------------------------------------------------------- @util.log_errors def dispatch_result(self, raw_msg): # maybe_dispatch_reults ? """dispatch method for result replies""" try: idents, msg = self.session.feed_identities(raw_msg, copy=False) msg = self.session.deserialize(msg, content=False, copy=False) engine = idents[0] try: idx = self.targets.index(engine) except ValueError: pass # skip load-update for dead engines else: self.finish_job(idx) except Exception: self.log.error("task::Invalid result: %r", raw_msg, exc_info=True) return md = msg['metadata'] parent = msg['parent_header'] if md.get('dependencies_met', True): success = md['status'] == 'ok' msg_id = parent['msg_id'] retries = self.retries[msg_id] if not success and retries > 0: # failed self.retries[msg_id] = retries - 1 self.handle_unmet_dependency(idents, parent) else: del self.retries[msg_id] # relay to client and update graph self.handle_result(idents, parent, raw_msg, success) # send to Hub monitor self.mon_stream.send_multipart([b'outtask'] + raw_msg, copy=False) else: self.handle_unmet_dependency(idents, parent) def handle_result(self, idents, parent, raw_msg, success=True): """handle a real task result, either success or failure""" # first, relay result to client engine = idents[0] client = idents[1] # swap_ids for ROUTER-ROUTER mirror raw_msg[:2] = [client, engine] # print (map(str, raw_msg[:4])) self.client_stream.send_multipart(raw_msg, copy=False) # now, update our data structures msg_id = parent['msg_id'] self.pending[engine].pop(msg_id) if success: self.completed[engine].add(msg_id) self.all_completed.add(msg_id) else: self.failed[engine].add(msg_id) self.all_failed.add(msg_id) self.all_done.add(msg_id) self.destinations[msg_id] = engine self.update_graph(msg_id, success) def handle_unmet_dependency(self, idents, parent): """handle an unmet dependency""" engine = idents[0] msg_id = parent['msg_id'] job = self.pending[engine].pop(msg_id) job.blacklist.add(engine) if job.blacklist == job.targets: self.queue_map[msg_id] = job self.fail_unreachable(msg_id) elif not self.maybe_run(job): # resubmit failed if msg_id not in self.all_failed: # put it back in our dependency tree self.save_unmet(job) if self.hwm: try: idx = self.targets.index(engine) except ValueError: pass # skip load-update for dead engines else: if self.loads[idx] == self.hwm - 1: self.update_graph(None) def update_graph(self, dep_id=None, success=True): """dep_id just finished. Update our dependency graph and submit any jobs that just became runnable. Called with dep_id=None to update entire graph for hwm, but without finishing a task. """ # print ("\n\n***********") # pprint (dep_id) # pprint (self.graph) # pprint (self.queue_map) # pprint (self.all_completed) # pprint (self.all_failed) # print ("\n\n***********\n\n") # update any jobs that depended on the dependency msg_ids = self.graph.pop(dep_id, []) # recheck *all* jobs if # a) we have HWM and an engine just become no longer full # or b) dep_id was given as None if ( dep_id is None or self.hwm and any([load == self.hwm - 1 for load in self.loads]) ): jobs = self.queue using_queue = True else: using_queue = False jobs = deque(sorted(self.queue_map[msg_id] for msg_id in msg_ids)) to_restore = [] while jobs: job = jobs.popleft() if job.removed: continue msg_id = job.msg_id put_it_back = True if job.after.unreachable( self.all_completed, self.all_failed ) or job.follow.unreachable(self.all_completed, self.all_failed): self.fail_unreachable(msg_id) put_it_back = False elif job.after.check( self.all_completed, self.all_failed ): # time deps met, maybe run if self.maybe_run(job): put_it_back = False self.queue_map.pop(msg_id) for mid in job.dependents: if mid in self.graph: self.graph[mid].remove(msg_id) # abort the loop if we just filled up all of our engines. # avoids an O(N) operation in situation of full queue, # where graph update is triggered as soon as an engine becomes # non-full, and all tasks after the first are checked, # even though they can't run. if not self.available_engines(): break if using_queue and put_it_back: # popped a job from the queue but it neither ran nor failed, # so we need to put it back when we are done # make sure to_restore preserves the same ordering to_restore.append(job) # put back any tasks we popped but didn't run if using_queue: self.queue.extendleft(to_restore) # ---------------------------------------------------------------------- # methods to be overridden by subclasses # ---------------------------------------------------------------------- def add_job(self, idx): """Called after self.targets[idx] just got the job with header. Override with subclasses. The default ordering is simple LRU. The default loads are the number of outstanding jobs.""" self.loads[idx] += 1 for lis in (self.targets, self.loads): lis.append(lis.pop(idx)) def finish_job(self, idx): """Called after self.targets[idx] just finished a job. Override with subclasses.""" self.loads[idx] -= 1 ipyparallel-7.1.0/ipyparallel/datapub.py000066400000000000000000000000671412533144600203530ustar00rootroot00000000000000from .engine.datapub import publish_data # noqa: F401 ipyparallel-7.1.0/ipyparallel/engine/000077500000000000000000000000001412533144600176235ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/engine/__init__.py000066400000000000000000000000001412533144600217220ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/engine/__main__.py000066400000000000000000000001011412533144600217050ustar00rootroot00000000000000if __name__ == '__main__': from .app import main main() ipyparallel-7.1.0/ipyparallel/engine/app.py000077500000000000000000001023621412533144600207640ustar00rootroot00000000000000#!/usr/bin/env python # encoding: utf-8 """ The IPython engine application """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import json import os import signal import sys import time from getpass import getpass from io import FileIO from io import TextIOWrapper from logging import StreamHandler import zmq from ipykernel.kernelapp import IPKernelApp from ipykernel.zmqshell import ZMQInteractiveShell from IPython.core.profiledir import ProfileDir from jupyter_client.localinterfaces import localhost from jupyter_client.session import Session from jupyter_client.session import session_aliases from jupyter_client.session import session_flags from tornado import ioloop from traitlets import Bool from traitlets import Bytes from traitlets import default from traitlets import Dict from traitlets import Float from traitlets import Instance from traitlets import Integer from traitlets import List from traitlets import observe from traitlets import Type from traitlets import Unicode from traitlets import validate from traitlets.config import Config from zmq.eventloop import zmqstream from .kernel import IPythonParallelKernel as Kernel from .log import EnginePUBHandler from .nanny import start_nanny from ipyparallel.apps.baseapp import base_aliases from ipyparallel.apps.baseapp import base_flags from ipyparallel.apps.baseapp import BaseParallelApplication from ipyparallel.apps.baseapp import catch_config_error from ipyparallel.controller.heartmonitor import Heart from ipyparallel.util import disambiguate_ip_address from ipyparallel.util import disambiguate_url # ----------------------------------------------------------------------------- # Module level variables # ----------------------------------------------------------------------------- _description = """Start an IPython engine for parallel computing. IPython engines run in parallel and perform computations on behalf of a client and controller. A controller needs to be started before the engines. The engine can be configured using command line options or using a cluster directory. Cluster directories contain config, log and security files and are usually located in your ipython directory and named as "profile_name". See the `profile` and `profile-dir` options for details. """ _examples = """ ipengine --file=path/to/ipcontroller-engine.json # connect to hub using a specific url ipengine --log-level=DEBUG > engine.log 2>&1 # log to a file with DEBUG verbosity """ DEFAULT_MPI_INIT = """ from mpi4py import MPI mpi_rank = MPI.COMM_WORLD.Get_rank() mpi_size = MPI.COMM_WORLD.Get_size() """ # ----------------------------------------------------------------------------- # Main application # ----------------------------------------------------------------------------- aliases = dict( file='IPEngine.url_file', c='IPEngine.startup_command', s='IPEngine.startup_script', url='IPEngine.registration_url', ssh='IPEngine.sshserver', sshkey='IPEngine.sshkey', location='IPEngine.location', timeout='IPEngine.timeout', ) aliases.update(base_aliases) aliases.update(session_aliases) flags = { 'mpi': ( { 'IPEngine': {'use_mpi': True}, }, "enable MPI integration", ), } flags.update(base_flags) flags.update(session_flags) class IPEngine(BaseParallelApplication): name = 'ipengine' description = _description examples = _examples classes = List([ZMQInteractiveShell, ProfileDir, Session, Kernel]) _deprecated_classes = ["EngineFactory", "IPEngineApp"] enable_nanny = Bool( True, config=True, help="""Enable the nanny process. The nanny process enables remote signaling of single engines and more responsive notification of engine shutdown. .. versionadded:: 7.0 """, ) startup_script = Unicode( u'', config=True, help='specify a script to be run at startup' ) startup_command = Unicode( '', config=True, help='specify a command to be run at startup' ) url_file = Unicode( u'', config=True, help="""The full location of the file containing the connection information for the controller. If this is not given, the file must be in the security directory of the cluster directory. This location is resolved using the `profile` or `profile_dir` options.""", ) wait_for_url_file = Float( 10, config=True, help="""The maximum number of seconds to wait for url_file to exist. This is useful for batch-systems and shared-filesystems where the controller and engine are started at the same time and it may take a moment for the controller to write the connector files.""", ) url_file_name = Unicode(u'ipcontroller-engine.json', config=True) @observe('cluster_id') def _cluster_id_changed(self, change): if change['new']: base = 'ipcontroller-{}'.format(change['new']) else: base = 'ipcontroller' self.url_file_name = "%s-engine.json" % base log_url = Unicode( '', config=True, help="""The URL for the iploggerapp instance, for forwarding logging to a central location.""", ) registration_url = Unicode( config=True, help="""Override the registration URL""", ) out_stream_factory = Type( 'ipykernel.iostream.OutStream', config=True, help="""The OutStream for handling stdout/err. Typically 'ipykernel.iostream.OutStream'""", ) display_hook_factory = Type( 'ipykernel.displayhook.ZMQDisplayHook', config=True, help="""The class for handling displayhook. Typically 'ipykernel.displayhook.ZMQDisplayHook'""", ) location = Unicode( config=True, help="""The location (an IP address) of the controller. This is used for disambiguating URLs, to determine whether loopback should be used to connect or the public address.""", ) timeout = Float( 5.0, config=True, help="""The time (in seconds) to wait for the Controller to respond to registration requests before giving up.""", ) max_heartbeat_misses = Integer( 50, config=True, help="""The maximum number of times a check for the heartbeat ping of a controller can be missed before shutting down the engine. If set to 0, the check is disabled.""", ) sshserver = Unicode( config=True, help="""The SSH server to use for tunneling connections to the Controller.""", ) sshkey = Unicode( config=True, help="""The SSH private key file to use when tunneling connections to the Controller.""", ) paramiko = Bool( sys.platform == 'win32', config=True, help="""Whether to use paramiko instead of openssh for tunnels.""", ) use_mpi = Bool( False, config=True, help="""Enable MPI integration. If set, MPI rank will be requested for my rank, and additionally `mpi_init` will be executed in the interactive shell. """, ) init_mpi = Unicode( DEFAULT_MPI_INIT, config=True, help="""Code to execute in the user namespace when initializing MPI""", ) mpi_registration_delay = Float( 0.02, config=True, help="""Per-engine delay for mpiexec-launched engines avoids flooding the controller with registrations, which can stall under heavy load. Default: .02 (50 engines/sec, or 3000 engines/minute) """, ) # not configurable: user_ns = Dict() id = Integer( None, allow_none=True, config=True, help="""Request this engine ID. If run in MPI, will use the MPI rank. Otherwise, let the Hub decide what our rank should be. """, ) @default('id') def _id_default(self): if not self.use_mpi: return None from mpi4py import MPI if MPI.COMM_WORLD.size > 1: self.log.debug("MPI rank = %i", MPI.COMM_WORLD.rank) return MPI.COMM_WORLD.rank registrar = Instance('zmq.eventloop.zmqstream.ZMQStream', allow_none=True) kernel = Instance(Kernel, allow_none=True) hb_check_period = Integer() # States for the heartbeat monitoring # Initial values for monitored and pinged must satisfy "monitored > pinged == False" so that # during the first check no "missed" ping is reported. Must be floats for Python 3 compatibility. _hb_last_pinged = 0.0 _hb_last_monitored = 0.0 _hb_missed_beats = 0 # The zmq Stream which receives the pings from the Heart _hb_listener = None bident = Bytes() ident = Unicode() @default("ident") def _default_ident(self): return self.session.session @default("bident") def _default_bident(self): return self.ident.encode("utf8") @observe("ident") def _ident_changed(self, change): self.bident = self._default_bident() using_ssh = Bool(False) context = Instance(zmq.Context) @default("context") def _default_context(self): return zmq.Context.instance() # an IPKernelApp instance, used to setup listening for shell frontends kernel_app = Instance(IPKernelApp, allow_none=True) aliases = Dict(aliases) flags = Dict(flags) curve_serverkey = Bytes( config=True, help="The controller's public key for CURVE security" ) curve_secretkey = Bytes( config=True, help="""The engine's secret key for CURVE security. Usually autogenerated on launch.""", ) curve_publickey = Bytes( config=True, help="""The engine's public key for CURVE security. Usually autogenerated on launch.""", ) @default("curve_serverkey") def _default_curve_serverkey(self): return os.environ.get("IPP_CURVE_SERVERKEY", "").encode("ascii") @default("curve_secretkey") def _default_curve_secretkey(self): return os.environ.get("IPP_CURVE_SECRETKEY", "").encode("ascii") @default("curve_publickey") def _default_curve_publickey(self): return os.environ.get("IPP_CURVE_PUBLICKEY", "").encode("ascii") @validate("curve_publickey", "curve_secretkey", "curve_serverkey") def _cast_bytes(self, proposal): if isinstance(proposal.value, str): return proposal.value.encode("ascii") return proposal.value def _ensure_curve_keypair(self): if not self.curve_secretkey or not self.curve_publickey: self.log.info("Generating new CURVE credentials") self.curve_publickey, self.curve_secretkey = zmq.curve_keypair() def find_url_file(self): """Set the url file. Here we don't try to actually see if it exists for is valid as that is handled by the connection logic. """ # Find the actual controller key file if not self.url_file: self.url_file = os.path.join( self.profile_dir.security_dir, self.url_file_name ) def load_connector_file(self): """load config from a JSON connector file, at a *lower* priority than command-line/config files. """ self.log.info("Loading connection file %r", self.url_file) config = self.config with open(self.url_file) as f: d = json.load(f) # allow hand-override of location for disambiguation # and ssh-server if 'IPEngine.location' not in self.cli_config: self.location = d['location'] if 'ssh' in d and not self.sshserver: self.sshserver = d.get("ssh") proto, ip = d['interface'].split('://') ip = disambiguate_ip_address(ip, self.location) d['interface'] = '%s://%s' % (proto, ip) if d.get('curve_serverkey'): # connection file takes precedence over env, if present and defined self.curve_serverkey = d['curve_serverkey'].encode('ascii') if self.curve_serverkey: self.log.info("Using CurveZMQ security") self._ensure_curve_keypair() else: self.log.warning("Not using CurveZMQ security") # DO NOT allow override of basic URLs, serialization, or key # JSON file takes top priority there if d.get('key') or 'key' not in config.Session: config.Session.key = d.get('key', '').encode('utf8') config.Session.signature_scheme = d['signature_scheme'] self.registration_url = f"{d['interface']}:{d['registration']}" config.Session.packer = d['pack'] config.Session.unpacker = d['unpack'] self.session = Session(parent=self) self.log.debug("Config changed:") self.log.debug("%r", config) self.connection_info = d def bind_kernel(self, **kwargs): """Promote engine to listening kernel, accessible to frontends.""" if self.kernel_app is not None: return self.log.info("Opening ports for direct connections as an IPython kernel") if self.curve_serverkey: self.log.warning("Bound kernel does not support CURVE security") kernel = self.kernel kwargs.setdefault('config', self.config) kwargs.setdefault('log', self.log) kwargs.setdefault('profile_dir', self.profile_dir) kwargs.setdefault('session', self.session) app = self.kernel_app = IPKernelApp(**kwargs) # allow IPKernelApp.instance(): IPKernelApp._instance = app app.init_connection_file() # relevant contents of init_sockets: app.shell_port = app._bind_socket(kernel.shell_streams[0], app.shell_port) app.log.debug("shell ROUTER Channel on port: %i", app.shell_port) iopub_socket = kernel.iopub_socket # ipykernel 4.3 iopub_socket is an IOThread wrapper: if hasattr(iopub_socket, 'socket'): iopub_socket = iopub_socket.socket app.iopub_port = app._bind_socket(iopub_socket, app.iopub_port) app.log.debug("iopub PUB Channel on port: %i", app.iopub_port) kernel.stdin_socket = self.context.socket(zmq.ROUTER) app.stdin_port = app._bind_socket(kernel.stdin_socket, app.stdin_port) app.log.debug("stdin ROUTER Channel on port: %i", app.stdin_port) # start the heartbeat, and log connection info: app.init_heartbeat() app.log_connection_info() app.connection_dir = self.profile_dir.security_dir app.write_connection_file() @property def tunnel_mod(self): from zmq.ssh import tunnel return tunnel def init_connector(self): """construct connection function, which handles tunnels.""" self.using_ssh = bool(self.sshkey or self.sshserver) if self.sshkey and not self.sshserver: # We are using ssh directly to the controller, tunneling localhost to localhost self.sshserver = self.registration_url.split('://')[1].split(':')[0] if self.using_ssh: if self.tunnel_mod.try_passwordless_ssh( self.sshserver, self.sshkey, self.paramiko ): password = False else: password = getpass("SSH Password for %s: " % self.sshserver) else: password = False def connect(s, url, curve_serverkey=None): url = disambiguate_url(url, self.location) if curve_serverkey is None: curve_serverkey = self.curve_serverkey if curve_serverkey: s.setsockopt(zmq.CURVE_SERVERKEY, curve_serverkey) s.setsockopt(zmq.CURVE_SECRETKEY, self.curve_secretkey) s.setsockopt(zmq.CURVE_PUBLICKEY, self.curve_publickey) if self.using_ssh: self.log.debug("Tunneling connection to %s via %s", url, self.sshserver) return self.tunnel_mod.tunnel_connection( s, url, self.sshserver, keyfile=self.sshkey, paramiko=self.paramiko, password=password, ) else: return s.connect(url) def maybe_tunnel(url): """like connect, but don't complete the connection (for use by heartbeat)""" url = disambiguate_url(url, self.location) if self.using_ssh: self.log.debug("Tunneling connection to %s via %s", url, self.sshserver) url, tunnelobj = self.tunnel_mod.open_tunnel( url, self.sshserver, keyfile=self.sshkey, paramiko=self.paramiko, password=password, ) return str(url) return connect, maybe_tunnel def register(self): """send the registration_request""" if self.use_mpi and self.id and self.id >= 100 and self.mpi_registration_delay: # Some launchres implement delay at the Launcher level, # but mpiexec must implement it int he engine process itself # delay based on our rank delay = self.id * self.mpi_registration_delay self.log.info( f"Delaying registration for {self.id} by {int(delay * 1000)}ms" ) time.sleep(delay) self.log.info("Registering with controller at %s" % self.registration_url) ctx = self.context connect, maybe_tunnel = self.init_connector() reg = ctx.socket(zmq.DEALER) reg.setsockopt(zmq.IDENTITY, self.bident) connect(reg, self.registration_url) self.registrar = zmqstream.ZMQStream(reg, self.loop) content = dict(uuid=self.ident) if self.id is not None: self.log.info("Requesting id: %i", self.id) content['id'] = self.id self._registration_completed = False self.registrar.on_recv( lambda msg: self.complete_registration(msg, connect, maybe_tunnel) ) self.session.send(self.registrar, "registration_request", content=content) def _report_ping(self, msg): """Callback for when the heartmonitor.Heart receives a ping""" # self.log.debug("Received a ping: %s", msg) self._hb_last_pinged = time.time() def complete_registration(self, msg, connect, maybe_tunnel): try: self._complete_registration(msg, connect, maybe_tunnel) except Exception as e: self.log.critical(f"Error completing registration: {e}", exc_info=True) self.exit(255) def _complete_registration(self, msg, connect, maybe_tunnel): ctx = self.context loop = self.loop identity = self.bident idents, msg = self.session.feed_identities(msg) msg = self.session.deserialize(msg) content = msg['content'] info = self.connection_info def url(key): """get zmq url for given channel""" return str(info["interface"] + ":%i" % info[key]) def urls(key): return [f'{info["interface"]}:{port}' for port in info[key]] if content['status'] == 'ok': requested_id = self.id self.id = content['id'] if requested_id is not None and self.id != requested_id: self.log.warning( f"Did not get the requested id: {self.id} != {requested_id}" ) self.log.name = self.log.name.rsplit(".", 1)[0] + f".{self.id}" elif self.id is None: self.log.name += f".{self.id}" # create Shell Connections (MUX, Task, etc.): # select which broadcast endpoint to connect to # use rank % len(broadcast_leaves) broadcast_urls = urls('broadcast') broadcast_leaves = len(broadcast_urls) broadcast_index = self.id % len(broadcast_urls) broadcast_url = broadcast_urls[broadcast_index] shell_addrs = [url('mux'), url('task'), broadcast_url] self.log.info(f'Shell_addrs: {shell_addrs}') # Use only one shell stream for mux and tasks stream = zmqstream.ZMQStream(ctx.socket(zmq.ROUTER), loop) stream.setsockopt(zmq.IDENTITY, identity) # TODO: enable PROBE_ROUTER when schedulers can handle the empty message # stream.setsockopt(zmq.PROBE_ROUTER, 1) self.log.debug("Setting shell identity %r", identity) shell_streams = [stream] for addr in shell_addrs: self.log.info("Connecting shell to %s", addr) connect(stream, addr) # control stream: control_url = url('control') curve_serverkey = self.curve_serverkey if self.enable_nanny: nanny_url, self.nanny_pipe = self.start_nanny( control_url=control_url, ) control_url = nanny_url # nanny uses our curve_publickey, not the controller's publickey curve_serverkey = self.curve_publickey control_stream = zmqstream.ZMQStream(ctx.socket(zmq.ROUTER), loop) control_stream.setsockopt(zmq.IDENTITY, identity) connect(control_stream, control_url, curve_serverkey=curve_serverkey) # create iopub stream: iopub_addr = url('iopub') iopub_socket = ctx.socket(zmq.PUB) iopub_socket.SNDHWM = 0 iopub_socket.setsockopt(zmq.IDENTITY, identity) connect(iopub_socket, iopub_addr) try: from ipykernel.iostream import IOPubThread except ImportError: pass else: iopub_socket = IOPubThread(iopub_socket) iopub_socket.start() # disable history: self.config.HistoryManager.hist_file = ':memory:' # Redirect input streams and set a display hook. if self.out_stream_factory: sys.stdout = self.out_stream_factory( self.session, iopub_socket, u'stdout' ) sys.stdout.topic = f"engine.{self.id}.stdout".encode("ascii") sys.stderr = self.out_stream_factory( self.session, iopub_socket, u'stderr' ) sys.stderr.topic = f"engine.{self.id}.stderr".encode("ascii") # copied from ipykernel 6, which captures sys.__stderr__ at the FD-level if getattr(sys.stderr, "_original_stdstream_copy", None) is not None: for handler in self.log.handlers: if isinstance(handler, StreamHandler) and ( handler.stream.buffer.fileno() == 2 ): self.log.debug( "Seeing logger to stderr, rerouting to raw filedescriptor." ) handler.stream = TextIOWrapper( FileIO(sys.stderr._original_stdstream_copy, "w") ) if self.display_hook_factory: sys.displayhook = self.display_hook_factory(self.session, iopub_socket) sys.displayhook.topic = f"engine.{self.id}.execute_result".encode( "ascii" ) # patch Session to always send engine uuid metadata original_send = self.session.send def send_with_metadata( stream, msg_or_type, content=None, parent=None, ident=None, buffers=None, track=False, header=None, metadata=None, **kwargs, ): """Ensure all messages set engine uuid metadata""" metadata = metadata or {} metadata.setdefault("engine", self.ident) return original_send( stream, msg_or_type, content=content, parent=parent, ident=ident, buffers=buffers, track=track, header=header, metadata=metadata, **kwargs, ) self.session.send = send_with_metadata self.kernel = Kernel.instance( parent=self, engine_id=self.id, ident=self.ident, session=self.session, control_stream=control_stream, shell_streams=shell_streams, iopub_socket=iopub_socket, loop=loop, user_ns=self.user_ns, log=self.log, ) self.kernel.shell.display_pub.topic = f"engine.{self.id}.displaypub".encode( "ascii" ) # FIXME: This is a hack until IPKernelApp and IPEngineApp can be fully merged self.init_signal() app = IPKernelApp( parent=self, shell=self.kernel.shell, kernel=self.kernel, log=self.log ) if self.use_mpi and self.init_mpi: app.exec_lines.insert(0, self.init_mpi) app.init_profile_dir() app.init_code() self.kernel.start() else: self.log.fatal("Registration Failed: %s" % msg) raise Exception("Registration Failed: %s" % msg) self.start_heartbeat( maybe_tunnel(url('hb_ping')), maybe_tunnel(url('hb_pong')), content['hb_period'], identity, ) self.log.info("Completed registration with id %i" % self.id) self.loop.remove_timeout(self._abort_timeout) def start_nanny(self, control_url): self.log.info("Starting nanny") config = Config() config.Session = self.config.Session return start_nanny( engine_id=self.id, identity=self.bident, control_url=control_url, curve_serverkey=self.curve_serverkey, curve_secretkey=self.curve_secretkey, curve_publickey=self.curve_publickey, registration_url=self.registration_url, config=config, ) def start_heartbeat(self, hb_ping, hb_pong, hb_period, identity): """Start our heart beating""" hb_monitor = None if self.max_heartbeat_misses > 0: # Add a monitor socket which will record the last time a ping was seen mon = self.context.socket(zmq.SUB) if self.curve_serverkey: mon.setsockopt(zmq.CURVE_SERVER, 1) mon.setsockopt(zmq.CURVE_SECRETKEY, self.curve_secretkey) mport = mon.bind_to_random_port('tcp://%s' % localhost()) mon.setsockopt(zmq.SUBSCRIBE, b"") self._hb_listener = zmqstream.ZMQStream(mon, self.loop) self._hb_listener.on_recv(self._report_ping) hb_monitor = "tcp://%s:%i" % (localhost(), mport) heart = Heart( hb_ping, hb_pong, hb_monitor, heart_id=identity, curve_serverkey=self.curve_serverkey, curve_secretkey=self.curve_secretkey, curve_publickey=self.curve_publickey, ) heart.start() # periodically check the heartbeat pings of the controller # Should be started here and not in "start()" so that the right period can be taken # from the hubs HeartBeatMonitor.period if self.max_heartbeat_misses > 0: # Use a slightly bigger check period than the hub signal period to not warn unnecessary self.hb_check_period = hb_period + 500 self.log.info( "Starting to monitor the heartbeat signal from the hub every %i ms.", self.hb_check_period, ) self._hb_reporter = ioloop.PeriodicCallback( self._hb_monitor, self.hb_check_period ) self._hb_reporter.start() else: self.log.info( "Monitoring of the heartbeat signal from the hub is not enabled." ) def abort(self): self.log.fatal("Registration timed out after %.1f seconds" % self.timeout) if "127." in self.registration_url: self.log.fatal( """ If the controller and engines are not on the same machine, you will have to instruct the controller to listen on an external IP (in ipcontroller_config.py): c.IPController.ip = '0.0.0.0' # for all interfaces, internal and external c.IPController.ip = '192.168.1.101' # or any interface that the engines can see or tunnel connections via ssh. """ ) self.session.send( self.registrar, "unregistration_request", content=dict(id=self.id) ) time.sleep(1) sys.exit(255) def _hb_monitor(self): """Callback to monitor the heartbeat from the controller""" self._hb_listener.flush() if self._hb_last_monitored > self._hb_last_pinged: self._hb_missed_beats += 1 self.log.warning( "No heartbeat in the last %s ms (%s time(s) in a row).", self.hb_check_period, self._hb_missed_beats, ) else: # self.log.debug("Heartbeat received (after missing %s beats).", self._hb_missed_beats) self._hb_missed_beats = 0 if self._hb_missed_beats >= self.max_heartbeat_misses: self.log.fatal( "Maximum number of heartbeats misses reached (%s times %s ms), shutting down.", self.max_heartbeat_misses, self.hb_check_period, ) self.session.send( self.registrar, "unregistration_request", content=dict(id=self.id) ) self.loop.stop() self._hb_last_monitored = time.time() def init_engine(self): # This is the working dir by now. sys.path.insert(0, '') config = self.config # print config self.find_url_file() if self.wait_for_url_file and not os.path.exists(self.url_file): self.log.warning("url_file %r not found", self.url_file) self.log.warning( "Waiting up to %.1f seconds for it to arrive.", self.wait_for_url_file ) tic = time.time() while not os.path.exists(self.url_file) and ( time.time() - tic < self.wait_for_url_file ): # wait for url_file to exist, or until time limit time.sleep(0.1) if os.path.exists(self.url_file): self.load_connector_file() else: self.log.fatal("Fatal: url file never arrived: %s", self.url_file) self.exit(1) exec_lines = [] for app in ('IPKernelApp', 'InteractiveShellApp'): if '%s.exec_lines' % app in config: exec_lines = config[app].exec_lines break exec_files = [] for app in ('IPKernelApp', 'InteractiveShellApp'): if '%s.exec_files' % app in config: exec_files = config[app].exec_files break config.IPKernelApp.exec_lines = exec_lines config.IPKernelApp.exec_files = exec_files if self.startup_script: exec_files.append(self.startup_script) if self.startup_command: exec_lines.append(self.startup_command) def forward_logging(self): if self.log_url: self.log.info("Forwarding logging to %s", self.log_url) context = self.context lsock = context.socket(zmq.PUB) lsock.connect(self.log_url) handler = EnginePUBHandler(self.engine, lsock) handler.setLevel(self.log_level) self.log.addHandler(handler) @default("log_level") def _default_debug(self): return 10 @catch_config_error def initialize(self, argv=None): self.log_level = 10 super().initialize(argv) self.log_level = 10 self.log.info(f"log level {self.log_level}") self.log.debug("init") self.init_engine() self.log.debug("init engine") self.forward_logging() def init_signal(self): signal.signal(signal.SIGINT, self._signal_sigint) signal.signal(signal.SIGTERM, self._signal_stop) def _signal_sigint(self, sig, frame): self.log.warning("Ignoring SIGINT. Terminate with SIGTERM.") def _signal_stop(self, sig, frame): self.log.critical(f"received signal {sig}, stopping") self.loop.add_callback_from_signal(self.loop.stop) def start(self): if self.id is not None: self.log.name += f".{self.id}" loop = self.loop def _start(): self.register() self._abort_timeout = loop.add_timeout( loop.time() + self.timeout, self.abort ) self.loop.add_callback(_start) try: self.loop.start() except KeyboardInterrupt: self.log.critical("Engine Interrupted, shutting down...\n") main = launch_new_instance = IPEngine.launch_instance if __name__ == '__main__': main() ipyparallel-7.1.0/ipyparallel/engine/datapub.py000066400000000000000000000034671412533144600216270ustar00rootroot00000000000000"""Publishing native (typically pickled) objects.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from ipykernel.jsonutil import json_clean from jupyter_client.session import extract_header from jupyter_client.session import Session from traitlets import Any from traitlets import CBytes from traitlets import Dict from traitlets import Instance from traitlets.config import Configurable from ipyparallel.serialize import serialize_object class ZMQDataPublisher(Configurable): topic = topic = CBytes(b'datapub') session = Instance(Session, allow_none=True) pub_socket = Any(allow_none=True) parent_header = Dict({}) def set_parent(self, parent): """Set the parent for outbound messages.""" self.parent_header = extract_header(parent) def publish_data(self, data): """publish a data_message on the IOPub channel Parameters ---------- data : dict The data to be published. Think of it as a namespace. """ session = self.session buffers = serialize_object( data, buffer_threshold=session.buffer_threshold, item_threshold=session.item_threshold, ) content = json_clean(dict(keys=list(data.keys()))) session.send( self.pub_socket, 'data_message', content=content, parent=self.parent_header, buffers=buffers, ident=self.topic, ) def publish_data(data): """publish a data_message on the IOPub channel Parameters ---------- data : dict The data to be published. Think of it as a namespace. """ from ipykernel.zmqshell import ZMQInteractiveShell ZMQInteractiveShell.instance().data_pub.publish_data(data) ipyparallel-7.1.0/ipyparallel/engine/kernel.py000066400000000000000000000203321412533144600214550ustar00rootroot00000000000000"""IPython kernel for parallel computing""" import sys from ipykernel.ipkernel import IPythonKernel from traitlets import Integer from traitlets import Type from .datapub import ZMQDataPublisher from ipyparallel.serialize import serialize_object from ipyparallel.serialize import unpack_apply_message from ipyparallel.util import utcnow class IPythonParallelKernel(IPythonKernel): """Extend IPython kernel for parallel computing""" engine_id = Integer(-1) msg_types = getattr(IPythonKernel, 'msg_types', []) + ['apply_request'] control_msg_types = getattr(IPythonKernel, 'control_msg_types', []) + [ 'abort_request', 'clear_request', ] _execute_sleep = 0 data_pub_class = Type(ZMQDataPublisher) def _topic(self, topic): """prefixed topic for IOPub messages""" base = "engine.%s" % self.engine_id return f"{base}.{topic}".encode("utf8") def __init__(self, **kwargs): super(IPythonParallelKernel, self).__init__(**kwargs) # add apply_request, in anticipation of upstream deprecation self.shell_handlers['apply_request'] = self.apply_request # set up data pub data_pub = self.shell.data_pub = self.data_pub_class(parent=self) self.shell.configurables.append(data_pub) data_pub.session = self.session data_pub.pub_socket = self.iopub_socket self.aborted = set() def should_handle(self, stream, msg, idents): """Check whether a shell-channel message should be handled Allows subclasses to prevent handling of certain messages (e.g. aborted requests). """ if not super(IPythonParallelKernel, self).should_handle(stream, msg, idents): return False msg_id = msg['header']['msg_id'] msg_type = msg['header']['msg_type'] if msg_id in self.aborted: # is it safe to assume a msg_id will not be resubmitted? self.aborted.remove(msg_id) self._send_abort_reply(stream, msg, idents) return False self.log.info(f"Handling {msg_type}: {msg_id}") return True def init_metadata(self, parent): """init metadata dict, for execute/apply_reply""" parent_metadata = parent.get('metadata', {}) return { 'started': utcnow(), 'dependencies_met': True, 'engine': self.ident, 'is_broadcast': parent_metadata.get('is_broadcast', False), 'is_coalescing': parent_metadata.get('is_coalescing', False), 'original_msg_id': parent_metadata.get('original_msg_id', ''), } def finish_metadata(self, parent, metadata, reply_content): """Finish populating metadata. Run after completing a request handler. """ metadata['status'] = reply_content['status'] if reply_content['status'] == 'error': if reply_content['ename'] == 'UnmetDependency': metadata['dependencies_met'] = False metadata['engine_info'] = dict( engine_uuid=self.ident, engine_id=self.engine_id, ) return metadata def apply_request(self, stream, ident, parent): try: content = parent[u'content'] bufs = parent[u'buffers'] msg_id = parent['header']['msg_id'] except: self.log.error("Got bad msg: %s", parent, exc_info=True) return md = self.init_metadata(parent) reply_content, result_buf = self.do_apply(content, bufs, msg_id, md) # put 'ok'/'error' status in header, for scheduler introspection: md = self.finish_metadata(parent, md, reply_content) # flush i/o sys.stdout.flush() sys.stderr.flush() self.log.debug(f'Sending apply_reply: {msg_id}') self.session.send( stream, u'apply_reply', reply_content, parent=parent, ident=ident, buffers=result_buf, metadata=md, ) def do_apply(self, content, bufs, msg_id, reply_metadata): shell = self.shell try: working = shell.user_ns prefix = "_" + str(msg_id).replace("-", "") + "_" f, args, kwargs = unpack_apply_message(bufs, working, copy=False) fname = getattr(f, '__name__', 'f') fname = prefix + "f" argname = prefix + "args" kwargname = prefix + "kwargs" resultname = prefix + "result" ns = {fname: f, argname: args, kwargname: kwargs, resultname: None} # print ns working.update(ns) code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname) try: exec(code, shell.user_global_ns, shell.user_ns) result = working.get(resultname) finally: for key in ns: working.pop(key) result_buf = serialize_object( result, buffer_threshold=self.session.buffer_threshold, item_threshold=self.session.item_threshold, ) except BaseException as e: # invoke IPython traceback formatting shell.showtraceback() try: str_evalue = str(e) except Exception as str_error: str_evalue = f"Failed to cast exception to string: {str_error}" reply_content = { 'traceback': [], 'ename': str(type(e).__name__), 'evalue': str_evalue, } # get formatted traceback, which ipykernel recorded if hasattr(shell, '_last_traceback'): # ipykernel 4.4 reply_content['traceback'] = shell._last_traceback or [] elif hasattr(shell, '_reply_content'): # ipykernel <= 4.3 if shell._reply_content and 'traceback' in shell._reply_content: reply_content['traceback'] = shell._reply_content['traceback'] else: self.log.warning("Didn't find a traceback where I expected to") shell._last_traceback = None e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply') reply_content['engine_info'] = e_info self.send_response( self.iopub_socket, u'error', reply_content, ident=self._topic('error') ) self.log.info( "Exception in apply request:\n%s", '\n'.join(reply_content['traceback']) ) result_buf = [] reply_content['status'] = 'error' else: reply_content = {'status': 'ok'} return reply_content, result_buf # Control messages for msgspec extensions: def abort_request(self, stream, ident, parent): """abort a specific msg by id""" msg_ids = parent['content'].get('msg_ids', None) if isinstance(msg_ids, str): msg_ids = [msg_ids] if not msg_ids: self._abort_queues() for mid in msg_ids: self.aborted.add(str(mid)) content = dict(status='ok') reply_msg = self.session.send( stream, 'abort_reply', content=content, parent=parent, ident=ident ) self.log.debug("%s", reply_msg) def clear_request(self, stream, idents, parent): """Clear our namespace.""" self.shell.reset(False) content = dict(status='ok') self.session.send( stream, 'clear_reply', ident=idents, parent=parent, content=content ) def _send_abort_reply(self, stream, msg, idents): """Send a reply to an aborted request""" # FIXME: forward-port ipython/ipykernel#684 self.log.info( f"Aborting {msg['header']['msg_id']}: {msg['header']['msg_type']}" ) reply_type = msg["header"]["msg_type"].rsplit("_", 1)[0] + "_reply" status = {"status": "aborted"} md = self.init_metadata(msg) md = self.finish_metadata(msg, md, status) md.update(status) self.session.send( stream, reply_type, metadata=md, content=status, parent=msg, ident=idents, ) ipyparallel-7.1.0/ipyparallel/engine/log.py000066400000000000000000000011261412533144600207560ustar00rootroot00000000000000from zmq.log.handlers import PUBHandler class EnginePUBHandler(PUBHandler): """A simple PUBHandler subclass that sets root_topic""" engine = None def __init__(self, engine, *args, **kwargs): PUBHandler.__init__(self, *args, **kwargs) self.engine = engine @property def root_topic(self): """this is a property, in case the handler is created before the engine gets registered with an id""" if isinstance(getattr(self.engine, 'id', None), int): return "engine.%i" % self.engine.id else: return "engine" ipyparallel-7.1.0/ipyparallel/engine/nanny.py000066400000000000000000000233621412533144600213260ustar00rootroot00000000000000"""Engine subprocess that receives and relays signals Currently the kernel nanny serves two purposes: 1. to relay signals to the process 2. to notice engine shutdown and promptly notify the Hub, rather than waiting for heartbeat failures. This has overlapping functionality with the Cluster API, but has key differences, justifying both existing: - addressing single engines is not feasible with the Cluster API, only engine *sets* - Cluster API can efficiently signal all engines via mpiexec """ import asyncio import logging import os import pickle import signal import sys from subprocess import PIPE from subprocess import Popen from threading import Thread import psutil import zmq from jupyter_client.session import Session from tornado.ioloop import IOLoop from traitlets.config import Config from zmq.eventloop.zmqstream import ZMQStream from ipyparallel import error from ipyparallel import util from ipyparallel.util import local_logger class KernelNanny: """Object for monitoring Must be child of engine Handles signal messages and watches Engine process for exiting """ def __init__( self, *, pid: int, engine_id: int, control_url: str, registration_url: str, identity: bytes, curve_serverkey: bytes, curve_publickey: bytes, curve_secretkey: bytes, config: Config, pipe, log_level: int = logging.INFO, ): self.pid = pid self.engine_id = engine_id self.parent_process = psutil.Process(self.pid) self.control_url = control_url self.registration_url = registration_url self.identity = identity self.curve_serverkey = curve_serverkey self.curve_publickey = curve_publickey self.curve_secretkey = curve_secretkey self.config = config self.pipe = pipe self.session = Session(config=self.config) self.log = local_logger(f"{self.__class__.__name__}.{engine_id}", log_level) self.log.propagate = False self.control_handlers = { "signal_request": self.signal_request, } self._finish_called = False def wait_for_parent_thread(self): """Wait for my parent to exit, then I'll notify the controller and shut down""" self.log.info(f"Nanny watching parent pid {self.pid}.") while True: try: exit_code = self.parent_process.wait(60) except psutil.TimeoutExpired: continue else: break self.log.critical(f"Parent {self.pid} exited with status {exit_code}.") self.loop.add_callback(self.finish) def pipe_handler(self, fd, events): self.log.debug(f"Pipe event {events}") self.loop.remove_handler(fd) try: fd.close() except BrokenPipeError: pass try: status = self.parent_process.wait(0) except psutil.TimeoutExpired: try: status = self.parent_process.status() except psutil.NoSuchProcess: status = "exited" self.log.critical(f"Pipe closed, parent {self.pid} has status: {status}") self.finish() def notify_exit(self): """Notify the Hub that our parent has exited""" self.log.info("Notifying Hub that our parent has shut down") s = self.context.socket(zmq.DEALER) # finite, nonzero LINGER to prevent hang without dropping message during exit s.LINGER = 3000 util.connect( s, self.registration_url, curve_serverkey=self.curve_serverkey, curve_secretkey=self.curve_secretkey, curve_publickey=self.curve_publickey, ) self.session.send(s, "unregistration_request", content={"id": self.engine_id}) s.close() def finish(self): """Prepare to exit and stop our event loop.""" if self._finish_called: return self._finish_called = True self.notify_exit() self.loop.add_callback(self.loop.stop) def dispatch_control(self, stream, raw_msg): """Dispatch message from the control scheduler If we have a handler registered""" try: idents, msg_frames = self.session.feed_identities(raw_msg) except Exception as e: self.log.error(f"Bad control message: {raw_msg}", exc_info=True) return try: msg = self.session.deserialize(msg_frames, content=True) except Exception: content = error.wrap_exception() self.log.error("Bad control message: %r", msg_frames, exc_info=True) return msg_type = msg['header']['msg_type'] if msg_type.endswith("_request"): reply_type = msg_type[-len("_request") :] else: reply_type = "error" self.log.debug(f"Client {idents[-1]} requested {msg_type}") handler = self.control_handlers.get(msg_type, None) if handler is None: # don't have an intercept handler, relay original message to parent self.log.debug(f"Relaying {msg_type} {msg['header']['msg_id']}") self.parent_stream.send_multipart(raw_msg) return try: content = handler(msg['content']) except Exception: content = error.wrap_exception() self.log.error("Error handling request: %r", msg_type, exc_info=True) self.session.send(stream, reply_type, ident=idents, content=content, parent=msg) def dispatch_parent(self, stream, raw_msg): """Relay messages from parent directly to control stream""" self.control_stream.send_multipart(raw_msg) # intercept message handlers def signal_request(self, content): """Handle a signal request: send signal to parent process""" sig = content['sig'] if isinstance(sig, str): sig = getattr(signal, sig) self.log.info(f"Sending signal {sig} to pid {self.pid}") # exception will be caught and wrapped by the caller self.parent_process.send_signal(sig) return {"status": "ok"} def start(self): self.log.info( f"Starting kernel nanny for engine {self.engine_id}, pid={self.pid}, nanny pid={os.getpid()}" ) self._watcher_thread = Thread( target=self.wait_for_parent_thread, name="WatchParent", daemon=True ) self._watcher_thread.start() # ignore SIGINT sent to parent signal.signal(signal.SIGINT, signal.SIG_IGN) self.loop = IOLoop.current() self.context = zmq.Context() # set up control socket (connection to Scheduler) self.control_socket = self.context.socket(zmq.ROUTER) self.control_socket.identity = self.identity util.connect( self.control_socket, self.control_url, curve_serverkey=self.curve_serverkey, ) self.control_stream = ZMQStream(self.control_socket) self.control_stream.on_recv_stream(self.dispatch_control) # set up relay socket (connection to parent's control socket) self.parent_socket = self.context.socket(zmq.DEALER) if self.curve_secretkey: self.parent_socket.setsockopt(zmq.CURVE_SERVER, 1) self.parent_socket.setsockopt(zmq.CURVE_SECRETKEY, self.curve_secretkey) port = self.parent_socket.bind_to_random_port("tcp://127.0.0.1") # now that we've bound, pass port to parent via AsyncResult self.pipe.write(f"tcp://127.0.0.1:{port}\n") if not sys.platform.startswith("win"): # watch for the stdout pipe to close # as a signal that our parent is shutting down self.loop.add_handler( self.pipe, self.pipe_handler, IOLoop.READ | IOLoop.ERROR ) self.parent_stream = ZMQStream(self.parent_socket) self.parent_stream.on_recv_stream(self.dispatch_parent) try: self.loop.start() finally: self.loop.close(all_fds=True) self.context.term() try: self.pipe.close() except BrokenPipeError: pass self.log.debug("exiting") @classmethod def main(cls, *args, **kwargs): """Main body function. Instantiates and starts a nanny. Args and keyword args passed to the constructor. Should be called in a subprocess. """ # start a new event loop for the forked process asyncio.set_event_loop(asyncio.new_event_loop()) IOLoop().make_current() self = cls(*args, **kwargs) self.start() def start_nanny(**kwargs): """Start a nanny subprocess Returns ------- nanny_url: str The proxied URL the engine's control socket should connect to, instead of connecting directly to the control Scheduler. """ kwargs['pid'] = os.getpid() env = os.environ.copy() env['PYTHONUNBUFFERED'] = '1' p = Popen( [sys.executable, '-m', __name__], stdin=PIPE, stdout=PIPE, env=env, start_new_session=True, # don't inherit signals ) p.stdin.write(pickle.dumps(kwargs)) p.stdin.close() out = p.stdout.readline() nanny_url = out.decode("utf8").strip() if not nanny_url: p.terminate() raise RuntimeError("nanny failed") # return the handle on the process # need to keep the pipe open for the nanny return nanny_url, p def main(): """Entrypoint from the command-line Loads kwargs from stdin, sets pipe to stdout """ kwargs = pickle.load(os.fdopen(sys.stdin.fileno(), mode='rb')) kwargs['pipe'] = sys.stdout KernelNanny.main(**kwargs) if __name__ == "__main__": main() ipyparallel-7.1.0/ipyparallel/error.py000066400000000000000000000166211412533144600200670ustar00rootroot00000000000000# encoding: utf-8 """Classes and functions for kernel related errors and exceptions. Inheritance diagram: .. inheritance-diagram:: ipyparallel.error :parts: 3 """ from __future__ import print_function import builtins import sys import traceback __docformat__ = "restructuredtext en" class IPythonError(Exception): """Base exception that all of our exceptions inherit from. This can be raised by code that doesn't have any more specific information.""" pass class KernelError(IPythonError): pass class EngineError(KernelError): pass class NoEnginesRegistered(KernelError): """Exception for operations that require some engines, but none exist""" def __str__(self): return ( "This operation requires engines." " Try client.wait_for_engines(n) to wait for engines to register." ) class TaskAborted(KernelError): pass class TaskTimeout(KernelError): pass # backward-compat: use builtin TimeoutError, but preserve `error.TimeoutError` import TimeoutError = builtins.TimeoutError class UnmetDependency(KernelError): pass class ImpossibleDependency(UnmetDependency): pass class DependencyTimeout(ImpossibleDependency): pass class InvalidDependency(ImpossibleDependency): pass class RemoteError(KernelError): """Error raised elsewhere""" ename = None evalue = None traceback = None engine_info = None def __init__(self, ename, evalue, traceback, engine_info=None): self.ename = ename self.evalue = evalue self.traceback = traceback self.engine_info = engine_info or {} self.args = (ename, evalue) def __repr__(self): engineid = self.engine_info.get('engine_id', ' ') return "" % (engineid, self.ename, self.evalue) def __str__(self): return "%s(%s)" % (self.ename, self.evalue) def render_traceback(self): """render traceback to a list of lines""" return (self.traceback or "No traceback available").splitlines() def _render_traceback_(self): """Special method for custom tracebacks within IPython. This will be called by IPython instead of displaying the local traceback. It should return a traceback rendered as a list of lines. """ return self.render_traceback() def print_traceback(self, excid=None): """print my traceback""" print('\n'.join(self.render_traceback())) class TaskRejectError(KernelError): """Exception to raise when a task should be rejected by an engine. This exception can be used to allow a task running on an engine to test if the engine (or the user's namespace on the engine) has the needed task dependencies. If not, the task should raise this exception. For the task to be retried on another engine, the task should be created with the `retries` argument > 1. The advantage of this approach over our older properties system is that tasks have full access to the user's namespace on the engines and the properties don't have to be managed or tested by the controller. """ class CompositeError(RemoteError): """Error for representing possibly multiple errors on engines""" tb_limit = 4 # limit on how many tracebacks to draw def __init__(self, message, elist): Exception.__init__(self, *(message, elist)) # Don't use pack_exception because it will conflict with the .message # attribute that is being deprecated in 2.6 and beyond. self.msg = message self.elist = elist self.args = [e[0] for e in elist] def _get_engine_str(self, ei): if not ei: return '[Engine Exception]' else: return '[%s:%s]: ' % (ei['engine_id'], ei['method']) def _get_traceback(self, ev): try: tb = ev._ipython_traceback_text except AttributeError: return 'No traceback available' else: return tb def __str__(self): s = str(self.msg) for en, ev, etb, ei in self.elist[: self.tb_limit]: engine_str = self._get_engine_str(ei) s = s + '\n' + engine_str + en + ': ' + str(ev) if len(self.elist) > self.tb_limit: s = s + '\n.... %i more exceptions ...' % (len(self.elist) - self.tb_limit) return s def __repr__(self): return "CompositeError(%i)" % len(self.elist) def render_traceback(self, excid=None): """render one or all of my tracebacks to a list of lines""" lines = [] if excid is None: for (en, ev, etb, ei) in self.elist[: self.tb_limit]: lines.append(self._get_engine_str(ei)) lines.extend((etb or 'No traceback available').splitlines()) lines.append('') if len(self.elist) > self.tb_limit: lines.append( '... %i more exceptions ...' % (len(self.elist) - self.tb_limit) ) else: try: en, ev, etb, ei = self.elist[excid] except: raise IndexError("an exception with index %i does not exist" % excid) else: lines.append(self._get_engine_str(ei)) lines.extend((etb or 'No traceback available').splitlines()) return lines def print_traceback(self, excid=None): print('\n'.join(self.render_traceback(excid))) def raise_exception(self, excid=0): try: en, ev, etb, ei = self.elist[excid] except: raise IndexError("an exception with index %i does not exist" % excid) else: raise RemoteError(en, ev, etb, ei) def collect_exceptions(rdict_or_list, method='unspecified'): """check a result dict for errors, and raise CompositeError if any exist. Passthrough otherwise.""" elist = [] if isinstance(rdict_or_list, dict): rlist = rdict_or_list.values() else: rlist = rdict_or_list for r in rlist: if isinstance(r, RemoteError): en, ev, etb, ei = r.ename, r.evalue, r.traceback, r.engine_info # Sometimes we could have CompositeError in our list. Just take # the errors out of them and put them in our new list. This # has the effect of flattening lists of CompositeErrors into one # CompositeError if en == 'CompositeError': for e in ev.elist: elist.append(e) else: elist.append((en, ev, etb, ei)) if len(elist) == 0: return rdict_or_list else: msg = "one or more exceptions from call to method: %s" % (method) # This silliness is needed so the debugger has access to the exception # instance (e in this case) try: raise CompositeError(msg, elist) except CompositeError as e: raise e def wrap_exception(engine_info={}): etype, evalue, tb = sys.exc_info() stb = traceback.format_exception(etype, evalue, tb) exc_content = { 'status': 'error', 'traceback': stb, 'ename': etype.__name__, 'evalue': str(evalue), 'engine_info': engine_info, } return exc_content def unwrap_exception(content): err = RemoteError( content['ename'], content['evalue'], '\n'.join(content['traceback']), content.get('engine_info', {}), ) return err ipyparallel-7.1.0/ipyparallel/joblib.py000066400000000000000000000017271412533144600202000ustar00rootroot00000000000000"""IPython parallel backend for joblib To enable the default view as a backend for joblib:: import ipyparallel as ipp ipp.register_joblib_backend() Or to enable a particular View you have already set up:: view.register_joblib_backend() At this point, you can use it with:: with parallel_backend('ipyparallel'): Parallel(n_jobs=2)(delayed(some_function)(i) for i in range(10)) .. versionadded:: 5.1 """ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import absolute_import from joblib.parallel import register_parallel_backend from .client._joblib import IPythonParallelBackend def register(name='ipyparallel', make_default=False): """Register the default ipyparallel Client as a joblib backend See joblib.parallel.register_parallel_backend for details. """ return register_parallel_backend( name, IPythonParallelBackend, make_default=make_default ) ipyparallel-7.1.0/ipyparallel/logger.py000066400000000000000000000001561412533144600202110ustar00rootroot00000000000000if __name__ == '__main__': from ipyparallel.apps import iploggerapp as app app.launch_new_instance() ipyparallel-7.1.0/ipyparallel/nbextension/000077500000000000000000000000001412533144600207125ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/nbextension/__init__.py000066400000000000000000000005731412533144600230300ustar00rootroot00000000000000"""Jupyter server extension(s)""" import warnings def load_jupyter_server_extension(app): warnings.warn( "Using ipyparallel.nbextension as server extension is deprecated in IPython Parallel 7.0. Use top-level ipyparallel.", DeprecationWarning, ) from ipyparallel import _load_jupyter_server_extension return _load_jupyter_server_extension(app) ipyparallel-7.1.0/ipyparallel/nbextension/base.py000066400000000000000000000025531412533144600222030ustar00rootroot00000000000000"""Place to put the base Handler""" import warnings from functools import lru_cache _APIHandler = None @lru_cache() def _guess_api_handler(): """Fallback to guess API handler by availability""" try: from notebook.base.handlers import APIHandler except ImportError: from jupyter_server.base.handlers import APIHandler global _APIHandler _APIHandler = APIHandler return _APIHandler def get_api_handler(app=None): """Get the base APIHandler class to use Inferred from app class (either jupyter_server or notebook app) """ global _APIHandler if _APIHandler is not None: return _APIHandler if app is None: warnings.warn( "Guessing base APIHandler class. Specify an app to ensure the right APIHandler is used.", stacklevel=2, ) return _guess_api_handler() top_modules = {cls.__module__.split(".", 1)[0] for cls in app.__class__.mro()} if "jupyter_server" in top_modules: from jupyter_server.base.handlers import APIHandler _APIHandler = APIHandler return APIHandler if "notebook" in top_modules: from notebook.base.handlers import APIHandler _APIHandler = APIHandler return APIHandler warnings.warn(f"Failed to detect base APIHandler class for {app}.", stacklevel=2) return _guess_api_handler() ipyparallel-7.1.0/ipyparallel/nbextension/handlers.py000066400000000000000000000127121412533144600230670ustar00rootroot00000000000000"""Tornado handlers for IPython cluster web service.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import json import os from jupyter_server.utils import url_path_join as ujoin from tornado import web from ..cluster import ClusterManager from ..util import abbreviate_profile_dir from .base import get_api_handler static = os.path.join(os.path.dirname(__file__), 'static') APIHandler = get_api_handler() class ClusterHandler(APIHandler): @property def cluster_manager(self): return self.settings['cluster_manager'] def cluster_model(self, key, cluster): """Create a JSONable cluster model Adds additional fields to make things easier on the client """ d = cluster.to_dict() profile_dir = d['cluster']['profile_dir'] # provide abbreviated profile info d['cluster']['profile'] = abbreviate_profile_dir(profile_dir) # top-level fields, added for easier client-side logic # add 'id' key, since typescript is bad at key-value pairs # so we return a list instead of a dict d['id'] = key # add total engine count d['engines']['n'] = sum(es.get('n', 0) for es in d['engines']['sets'].values()) # add cluster file d['cluster_file'] = cluster.cluster_file return d class ClusterListHandler(ClusterHandler): """List and create new clusters GET /clusters : list current clusters POST / clusters (JSON body) : create a new cluster""" @web.authenticated def get(self): # currently reloads everything from disk. Is that what we want? clusters = self.cluster_manager.load_clusters(init_default_clusters=True) def sort_key(model): """Sort clusters order: - running - default profile - default cluster id """ running = True if model.get("controller") else False profile = model['cluster']['profile'] cluster_id = model['cluster']['cluster_id'] default_profile = profile == 'default' default_cluster = cluster_id == '' return ( not running, not default_profile, not default_cluster, profile, cluster_id, ) self.write( json.dumps( sorted( [ self.cluster_model(key, cluster) for key, cluster in clusters.items() ], key=sort_key, ) ) ) @web.authenticated def post(self): body = self.get_json_body() or {} self.log.info(f"Creating cluster with {body}") # clear null values for key in ('profile', 'cluster_id', 'n'): if key in body and body[key] in {None, ''}: body.pop(key) if 'profile' in body and os.path.sep in body['profile']: # if it looks like a path, use profile_dir body['profile_dir'] = body.pop('profile') if ( 'profile' in body and 'cluster_id' not in body and f"{body['profile']}:" not in self.cluster_mananger.clusters ): # if no cluster exists for a profile, # default for no cluster id instead of random body["cluster_id"] = "" cluster_id, cluster = self.cluster_manager.new_cluster(**body) self.write(json.dumps(self.cluster_model(cluster_id, cluster))) class ClusterActionHandler(ClusterHandler): """Actions on a single cluster GET: read single cluster model POST: start PATCH: engines? DELETE: stop """ def get_cluster(self, cluster_key): try: return self.cluster_manager.get_cluster(cluster_key) except KeyError: raise web.HTTPError(404, f"No such cluster: {cluster_key}") @web.authenticated async def post(self, cluster_key): cluster = self.get_cluster(cluster_key) body = self.get_json_body() or {} n = body.get("n", None) if n is not None and not isinstance(n, int): raise web.HTTPError(400, f"n must be an integer, not {n!r}") await cluster.start_cluster(n=n) self.write(json.dumps(self.cluster_model(cluster_key, cluster))) @web.authenticated async def get(self, cluster_key): cluster = self.get_cluster(cluster_key) self.write(json.dumps(self.cluster_model(cluster_key, cluster))) @web.authenticated async def delete(self, cluster_key): cluster = self.get_cluster(cluster_key) await cluster.stop_cluster() self.cluster_manager.remove_cluster(cluster_key) self.set_status(204) # URL to handler mappings _cluster_key_regex = ( r"(?P[^\/]+)" # there is almost no text that is invalid ) default_handlers = [ (r"/ipyparallel/clusters", ClusterListHandler), ( rf"/ipyparallel/clusters/{_cluster_key_regex}", ClusterActionHandler, ), ] def load_jupyter_server_extension(nbapp): """Load the nbserver extension""" nbapp.log.info("Loading IPython parallel extension") webapp = nbapp.web_app webapp.settings['cluster_manager'] = ClusterManager(parent=nbapp) base_url = webapp.settings['base_url'] webapp.add_handlers( ".*$", [(ujoin(base_url, pat), handler) for pat, handler in default_handlers] ) ipyparallel-7.1.0/ipyparallel/nbextension/install.py000066400000000000000000000045471412533144600227440ustar00rootroot00000000000000"""Install the IPython clusters tab in the Jupyter notebook dashboard""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from jupyter_core.paths import jupyter_config_dir from notebook.services.config import ConfigManager as FrontendConfigManager from traitlets.config.manager import BaseJSONConfigManager def install_extensions(enable=True, user=False): """Register ipyparallel clusters tab as notebook extensions Toggle with enable=True/False. """ from distutils.version import LooseVersion as V import notebook if V(notebook.__version__) < V('4.2'): return _install_extension_nb41(enable) from notebook.nbextensions import ( install_nbextension_python, enable_nbextension, disable_nbextension, ) from notebook.serverextensions import toggle_serverextension_python toggle_serverextension_python('ipyparallel', user=user) install_nbextension_python('ipyparallel', user=user) if enable: enable_nbextension('tree', 'ipyparallel/main', user=user) else: disable_nbextension('tree', 'ipyparallel/main') def _install_extension_nb41(enable=True): """deprecated, pre-4.2 implementation of installing notebook extension""" # server-side server = BaseJSONConfigManager(config_dir=jupyter_config_dir()) server_cfg = server.get('jupyter_notebook_config') app_cfg = server_cfg.get('NotebookApp', {}) server_extensions = app_cfg.get('server_extensions', []) server_ext = 'ipyparallel.nbextension' server_changed = False if enable and server_ext not in server_extensions: server_extensions.append(server_ext) server_changed = True elif (not enable) and server_ext in server_extensions: server_extensions.remove(server_ext) server_changed = True if server_changed: server.update( 'jupyter_notebook_config', { 'NotebookApp': { 'server_extensions': server_extensions, } }, ) # frontend config (*way* easier because it's a dict) frontend = FrontendConfigManager() frontend.update( 'tree', { 'load_extensions': { 'ipyparallel/main': enable or None, } }, ) install_server_extension = install_extensions ipyparallel-7.1.0/ipyparallel/nbextension/static/000077500000000000000000000000001412533144600222015ustar00rootroot00000000000000ipyparallel-7.1.0/ipyparallel/nbextension/static/clusterlist.css000066400000000000000000000001201412533144600252610ustar00rootroot00000000000000.action_col { text-align: right; } input.engine_num_input { width: 60px; } ipyparallel-7.1.0/ipyparallel/nbextension/static/clusterlist.js000066400000000000000000000144401412533144600251170ustar00rootroot00000000000000// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. define(["base/js/namespace", "jquery", "base/js/utils"], function ( IPython, $, utils ) { "use strict"; // use base utils.ajax in notebook >= 4.3 // for xsrf settings, etc. var ajax = utils.ajax || $.ajax; var ClusterList = function (selector, options) { this.selector = selector; if (this.selector !== undefined) { this.element = $(selector); this.style(); this.bind_events(); } options = options || {}; this.options = options; this.base_url = options.base_url || utils.get_body_data("baseUrl"); this.notebook_path = options.notebook_path || utils.get_body_data("notebookPath"); }; ClusterList.prototype.style = function () { $("#cluster_list").addClass("list_container"); $("#cluster_toolbar").addClass("list_toolbar"); $("#cluster_list_info").addClass("toolbar_info"); $("#cluster_buttons").addClass("toolbar_buttons"); }; ClusterList.prototype.bind_events = function () { var that = this; $("#refresh_cluster_list").click(function () { that.load_list(); }); }; ClusterList.prototype.load_list = function () { var settings = { processData: false, cache: false, type: "GET", dataType: "json", success: $.proxy(this.load_list_success, this), error: utils.log_ajax_error, }; var url = utils.url_join_encode(this.base_url, "ipyparallel/clusters"); ajax(url, settings); }; ClusterList.prototype.clear_list = function () { this.element.children(".list_item").remove(); }; ClusterList.prototype.load_list_success = function (data, status, xhr) { this.clear_list(); var len = data.length; for (var cluster of data) { var element = $("