Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel tools and compiling #701

Merged
merged 55 commits into from
Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
b8db2ea
Parallel tools and compiling
nonhermitian Jul 31, 2018
fa8f7e8
add blank line
nonhermitian Jul 31, 2018
28a3243
Add parallel compile test
nonhermitian Jul 31, 2018
cc1ce7b
disable import-error for optional import
nonhermitian Jul 31, 2018
d66ced3
fix indent
nonhermitian Jul 31, 2018
2342f14
Major update wiht magics
nonhermitian Aug 6, 2018
4be9865
fix compile lint
nonhermitian Aug 6, 2018
7f6a326
Merge branch 'master' into parallel_compile
nonhermitian Aug 6, 2018
7cd624f
fix style
nonhermitian Aug 6, 2018
ea567e2
disable import errors
nonhermitian Aug 6, 2018
ffcc1f5
white space
nonhermitian Aug 6, 2018
764225a
Add compile with TextProgressBar test
nonhermitian Aug 6, 2018
5062fea
Remove jupyter components
nonhermitian Aug 7, 2018
e218732
shorten parallel test time
nonhermitian Aug 7, 2018
2c81613
Minimize the parallel routines
nonhermitian Aug 7, 2018
510b3ec
Merge branch 'master' into parallel_compile
nonhermitian Aug 20, 2018
bb3daf7
cleanup merge
nonhermitian Aug 20, 2018
4ce6eea
updates
nonhermitian Aug 21, 2018
be2605d
more updates
nonhermitian Aug 21, 2018
11f9253
fix comtypes issue
nonhermitian Aug 21, 2018
7edd07a
add comtypes
nonhermitian Aug 21, 2018
a2b1bbb
make comtypes requirement on windows
nonhermitian Aug 21, 2018
3dd06b0
Merge branch 'master' into parallel_compile
nonhermitian Aug 21, 2018
a5bd946
Merge branch 'master' into parallel_compile
nonhermitian Aug 21, 2018
f8438bb
update changelog
nonhermitian Aug 21, 2018
d6690f6
review updates
nonhermitian Aug 27, 2018
1ec3f34
Merge branch 'master' into parallel_compile
nonhermitian Aug 27, 2018
681d14d
more updates
nonhermitian Aug 27, 2018
ae5eebb
Merge branch 'master' into parallel_compile
nonhermitian Aug 28, 2018
2386b3f
remove callback function
nonhermitian Aug 29, 2018
e270de7
Merge branch 'master' into parallel_compile
nonhermitian Sep 7, 2018
b4851f2
fix conflict
nonhermitian Sep 7, 2018
d7feb10
Add progressbars back
nonhermitian Sep 9, 2018
3e73fec
fix lint errors
nonhermitian Sep 9, 2018
ea8d11e
better grabbing of progress bars
nonhermitian Sep 10, 2018
86f57c7
grab bars in order created
nonhermitian Sep 10, 2018
4a0e813
Merge branch 'master' into parallel_compile
nonhermitian Sep 10, 2018
4f9f69c
Make jupyter tools availabel by default
nonhermitian Sep 10, 2018
ec65037
fix circular import
nonhermitian Sep 10, 2018
eb170fd
switch used order
nonhermitian Sep 10, 2018
aea459f
compile in parallel
nonhermitian Sep 12, 2018
fded2ba
fix move lint disable
nonhermitian Sep 12, 2018
c344021
fix signature
nonhermitian Sep 13, 2018
6f2077e
Merge branch 'master' into parallel_compile
nonhermitian Sep 13, 2018
7888db4
fix changelog and releases
nonhermitian Sep 13, 2018
c960df6
revert erroneous changes from git history
nonhermitian Sep 13, 2018
784fe1c
revert one more
nonhermitian Sep 13, 2018
ca5be80
Merge branch 'master' into parallel_compile
nonhermitian Sep 13, 2018
4de103d
add back psutil requirement
nonhermitian Sep 13, 2018
78ad402
remove high-level imports
nonhermitian Sep 13, 2018
6551c04
Merge branch 'master' into parallel_compile
nonhermitian Sep 13, 2018
d087b0f
refine changelog
ajavadia Sep 13, 2018
cc78073
Merge branch 'master' into parallel_compile
nonhermitian Sep 17, 2018
a0258f2
Merge branch 'parallel_compile' of https://github.com/nonhermitian/qi…
nonhermitian Sep 17, 2018
2c90a06
move everything to transpiler folder
nonhermitian Sep 18, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ Added
- Introduced new options for handling credentials (qiskitrc file, environment
variables) and automatic registration. (#547)
- Add OpenMP parallelization for Apple builds of the cpp simulator (#698).
- Add parallelization utilities (#701)
- Parallelize transpilation (#701)
- New interactive visualizations (#765).
- Added option to reverse the qubit order when plotting a circuit. (#762, #786)
- Jupyter notebook magic function qiskit_job_status (#734).
- Jupyter notebook magic function qiskit_job_status, qiskit_progress_bar (#701, #734)
- Add a new function ``qobj_to_circuits`` to convert a Qobj object to
a list of QuantumCircuit objects (#877)

Expand Down
5 changes: 5 additions & 0 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"""Main QISKit public functionality."""

import os
import sys
import pkgutil

# First, check for required Python and API version
Expand All @@ -37,6 +38,10 @@
# to be placed *before* the wrapper imports or any non-import code.
__path__ = pkgutil.extend_path(__path__, __name__)

# Allow extending this namespace. Please note that currently this line needs
# to be placed *before* the wrapper imports.
__path__ = pkgutil.extend_path(__path__, __name__)

from .wrapper._wrapper import (
available_backends, local_backends, remote_backends,
get_backend, compile, execute, register, unregister,
Expand Down
19 changes: 18 additions & 1 deletion qiskit/_util.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-

# Copyright 2017, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
Expand All @@ -12,9 +11,11 @@
import logging
import re
import sys
import platform
import warnings
import socket
from collections import UserDict
import psutil

API_NAME = 'IBMQuantumExperience'
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -160,6 +161,22 @@ def _parse_ibmq_credentials(url, hub=None, group=None, project=None):
return url


def local_hardware_info():
"""Basic hardware information about the local machine.

Gives actual number of CPU's in the machine, even when hyperthreading is
turned on.

Returns:
dict: The hardware information.

"""
results = {'os': platform.system()}
results['memory'] = psutil.virtual_memory().total / (1024**3)
results['cpus'] = psutil.cpu_count(logical=False)
return results


def _has_connection(hostname, port):
"""Checks to see if internet connection exists to host
via specified port
Expand Down
3 changes: 3 additions & 0 deletions qiskit/transpiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@

# pylint: disable=redefined-builtin
from ._transpiler import compile, transpile

from ._parallel import parallel_map
from ._progressbar import TextProgressBar
144 changes: 144 additions & 0 deletions qiskit/transpiler/_parallel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-

# Copyright 2018, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.

# This file is part of QuTiP: Quantum Toolbox in Python.
#
# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. 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.
#
# 3. Neither the name of the QuTiP: Quantum Toolbox in Python 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
# HOLDER 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.
###############################################################################

"""Routines for running Python functions in parallel using process pools
from the multiprocessing library.
"""

import os
import platform
from multiprocessing import Pool
from qiskit._qiskiterror import QISKitError
from qiskit._util import local_hardware_info
from ._receiver import receiver as rec
from ._progressbar import BaseProgressBar

# Number of local physical cpus
CPU_COUNT = local_hardware_info()['cpus']

# Set parallel ennvironmental variable
os.environ['QISKIT_IN_PARALLEL'] = 'FALSE'


def parallel_map(task, values, task_args=tuple(), task_kwargs={}, # pylint: disable=W0102
num_processes=CPU_COUNT):
"""
Parallel execution of a mapping of `values` to the function `task`. This
is functionally equivalent to::
result = [task(value, *task_args, **task_kwargs) for value in values]

On Windows this function defaults to a serial implimentation to avoid the
overhead from spawning processes in Windows.

Parameters:
task (func): Function that is to be called for each value in ``task_vec``.
values (array_like): List or array of values for which the ``task``
function is to be evaluated.
task_args (list): Optional additional arguments to the ``task`` function.
task_kwargs (dict): Optional additional keyword argument to the ``task`` function.
num_processes (int): Number of processes to spawn.

Returns:
result: The result list contains the value of
``task(value, *task_args, **task_kwargs)`` for
each value in ``values``.

Raises:
QISKitError: If user interupts via keyboard.
"""
# len(values) == 1
if len(values) == 1:
return [task(values[0], *task_args, **task_kwargs)]

# Get last element of the receiver channels
if any(rec.channels):
progress_bar = None
for idx in rec.channels:
if rec.channels[idx].type == 'progressbar' and not rec.channels[idx].touched:
progress_bar = rec.channels[idx]
break
if progress_bar is None:
progress_bar = BaseProgressBar()
else:
progress_bar = BaseProgressBar()

progress_bar.start(len(values))
nfinished = [0]

def _callback(x): # pylint: disable=W0613
nfinished[0] += 1
progress_bar.update(nfinished[0])

# Run in parallel if not Win and not in parallel already
if platform.system() != 'Windows' and num_processes > 1 \
and os.getenv('QISKIT_IN_PARALLEL') == 'FALSE':
os.environ['QISKIT_IN_PARALLEL'] = 'TRUE'
try:
pool = Pool(processes=num_processes)

async_res = [pool.apply_async(task, (value,) + task_args, task_kwargs,
_callback) for value in values]

while not all([item.ready() for item in async_res]):
for item in async_res:
item.wait(timeout=0.1)

pool.terminate()
pool.join()

except KeyboardInterrupt:
pool.terminate()
pool.join()
progress_bar.finished()
raise QISKitError('Keyboard interrupt in parallel_map.')

progress_bar.finished()
os.environ['QISKIT_IN_PARALLEL'] = 'FALSE'
return [ar.get() for ar in async_res]

# Cannot do parallel on Windows , if another parallel_map is running in parallel,
# or len(values) == 1.
results = []
for _, value in enumerate(values):
result = task(value, *task_args, **task_kwargs)
results.append(result)
_callback(0)
progress_bar.finished()
return results
129 changes: 129 additions & 0 deletions qiskit/transpiler/_progressbar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-

# Copyright 2018, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.

# This file is part of QuTiP: Quantum Toolbox in Python.
#
# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. 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.
#
# 3. Neither the name of the QuTiP: Quantum Toolbox in Python 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
# HOLDER 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.
###############################################################################

"""Progress bars module"""

import time
import datetime
import sys
from ._receiver import receiver as rec


class BaseProgressBar(object):
"""An abstract progress bar with some shared functionality.
"""

def __init__(self):
self.type = 'progressbar'
self.touched = False
self.channel_id = rec.add_channel(self)
self.iter = None
self.t_start = None
self.t_done = None

def start(self, iterations):
"""Start the progress bar.

Parameters:
iterations (int): Number of iterations.
"""
self.touched = True
self.iter = int(iterations)
self.t_start = time.time()

def update(self, n):
"""Update status of progress bar.
"""
pass

def time_elapsed(self):
"""Return the time elapsed since start.

Returns:
elapsed_time: Time since progress bar started.
"""
return "%6.2fs" % (time.time() - self.t_start)

def time_remaining_est(self, completed_iter):
"""Estimate the remaining time left.

Parameters:
completed_iter (int): Number of iterations completed.

Returns:
est_time: Estimated time remaining.
"""
if completed_iter:
t_r_est = (time.time() - self.t_start) / \
completed_iter*(self.iter-completed_iter)
else:
t_r_est = 0
date_time = datetime.datetime(1, 1, 1) + datetime.timedelta(seconds=t_r_est)
time_string = "%02d:%02d:%02d:%02d" % \
(date_time.day - 1, date_time.hour, date_time.minute, date_time.second)

return time_string

def finished(self):
"""Run when progress bar has completed.
"""
rec.remove_channel(self.channel_id)


class TextProgressBar(BaseProgressBar):
"""
A simple text-based progress bar.
"""
def start(self, iterations):
self.touched = True
self.iter = int(iterations)
self.t_start = time.time()
pbar = '-' * 50
sys.stdout.write('\r|%s| %s%s%s [%s]' %
(pbar, 0, '/', self.iter, ''))

def update(self, n):
filled_length = int(round(50 * n / self.iter))
pbar = u'█' * filled_length + '-' * (50 - filled_length)
time_left = self.time_remaining_est(n)
sys.stdout.write('\r|%s| %s%s%s [%s]' % (pbar, n, '/', self.iter, time_left))
if n == self.iter:
sys.stdout.write('\n')
sys.stdout.flush()
Loading