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

Resolves #262: add qiskit.backend methods... #363

Merged
merged 44 commits into from
Mar 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
845a841
Resolves #262: add qiskit.backend methods...
Mar 23, 2018
dde5740
linting quantumprogram
ajavadia Mar 25, 2018
e25b43a
Update _backendutils.py
ajavadia Mar 25, 2018
d2bdde2
Update _backendutils.py
ajavadia Mar 25, 2018
b88a852
Update _basebackend.py
ajavadia Mar 25, 2018
47aab7e
Update _qeremote.py
ajavadia Mar 25, 2018
cb078a6
Update _basebackend.py
ajavadia Mar 25, 2018
327435c
Update _quantumprogram.py
ajavadia Mar 25, 2018
358e58b
Update _qeremote.py
ajavadia Mar 25, 2018
db252f2
Warning, updates to test, and backencs
Mar 25, 2018
8f86819
Fixing some spelling and linting
Mar 25, 2018
ad19099
Spelling fixes
jaygambetta Mar 25, 2018
eef2617
Lint and spelling
jaygambetta Mar 25, 2018
0a86e48
Linter fixes and some spelling in backendutils
jaygambetta Mar 25, 2018
9d9e56e
Cleaning up the coupling_map
jaygambetta Mar 25, 2018
4950752
Renaming configuration as config to fix scope error
jaygambetta Mar 25, 2018
ad71fe1
Linting in test
jaygambetta Mar 25, 2018
0fe296b
Linting more
jaygambetta Mar 25, 2018
f46c5a3
Fixing the vqe
jaygambetta Mar 25, 2018
a00a12d
More linting
jaygambetta Mar 25, 2018
c989be4
Update test_quantumprogram.py
ajavadia Mar 25, 2018
95eee8e
lint errors I found trying to debug the error
jaygambetta Mar 25, 2018
e698686
Removing backend from quantum_program test
jaygambetta Mar 26, 2018
c4bd73a
Making the backend object based
jaygambetta Mar 26, 2018
a5577b6
Adding tests for the backend object
jaygambetta Mar 26, 2018
28df243
Linting tests
jaygambetta Mar 26, 2018
d6fa6cf
Spelling and linting
jaygambetta Mar 26, 2018
9e2dd29
Removing some set_api that are not needed
jaygambetta Mar 26, 2018
e6dec7a
set log level to debug
ewinston Mar 26, 2018
72800cc
Removing the api from the tests and needed by the program
jaygambetta Mar 26, 2018
ace23ec
Cleaning up and adding a discover
jaygambetta Mar 26, 2018
c4adaa8
correct travis.yml env variable specification
ewinston Mar 26, 2018
a24bcdb
Removing more old code
jaygambetta Mar 26, 2018
6cd7810
Updated lengths with rename of q_name and c_name
jaygambetta Mar 26, 2018
9028eb3
Lint errors found from travis fixed
jaygambetta Mar 26, 2018
1b22e45
Moving import order to help travis linter
jaygambetta Mar 26, 2018
0baac1a
Revise DeprecationWarnings, add note to docstrings
diego-plan9 Mar 26, 2018
91ed3e8
Enable deprecation warnings during __init__
diego-plan9 Mar 26, 2018
cce3be0
fix cyclic import and scope of configuration
ewinston Mar 26, 2018
a71f67e
fix linter
ewinston Mar 26, 2018
9cc3adf
remove converting coupling_map to dict
ewinston Mar 26, 2018
9ef34b3
minor lint fix
ewinston Mar 26, 2018
83ec75c
Small fix to the backends.
jaygambetta Mar 26, 2018
864a068
Fix LookupErrors, style, comments
diego-plan9 Mar 27, 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
2 changes: 2 additions & 0 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@
from ._quantumprogram import QuantumProgram
from ._result import Result

from . import backends

__version__ = '0.5.0'
426 changes: 233 additions & 193 deletions qiskit/_quantumprogram.py

Large diffs are not rendered by default.

26 changes: 25 additions & 1 deletion qiskit/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
# =============================================================================
"""Common utilities for QISKit."""

import sys
import logging
import re
import sys
import warnings

API_NAME = 'IBMQuantumExperience'
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -85,5 +87,27 @@ def _check_ibmqx_version():
str(ibmqx_require))


def _enable_deprecation_warnings():
"""
Force the `DeprecationWarning` warnings to be displayed for the qiskit
module, overriding the system configuration as they are ignored by default
[1] for end-users.

TODO: on Python 3.7, this might not be needed due to PEP-0565 [2].

[1] https://docs.python.org/3/library/warnings.html#default-warning-filters
[2] https://www.python.org/dev/peps/pep-0565/
"""
# pylint: disable=invalid-name
deprecation_filter = ('always', None, DeprecationWarning,
re.compile(r'^qiskit\.*', re.UNICODE), 0)

# Instead of using warnings.simple_filter() directly, the internal
# _add_filter() function is used for being able to match against the
# module.
warnings._add_filter(*deprecation_filter, append=False)


_check_python_version()
_check_ibmqx_version()
_enable_deprecation_warnings()
5 changes: 4 additions & 1 deletion qiskit/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
from ._basebackend import BaseBackend
from ._backendutils import (get_backend_class,
get_backend_instance,
get_backend_configuration,
configuration,
calibration,
parameters,
status,
local_backends,
remote_backends,
register_backend,
Expand Down
110 changes: 82 additions & 28 deletions qiskit/backends/_backendutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import re
from collections import namedtuple

from qiskit import mapper
import qiskit
from ._basebackend import BaseBackend
from .. import QISKitError

Expand Down Expand Up @@ -89,41 +89,36 @@ def discover_local_backends(directory=os.path.dirname(__file__)):


def discover_remote_backends(api):
"""Discover backends available on the Quantum Experience
"""Discover backends available from IBM Q

Args:
api (IBMQuantumExperience): Quantum Experience API
api (IBMQuantumExperience): IBM Q API
Returns:
list: list of discovered backend names
"""
from ._qeremote import QeRemote
QeRemote.set_api(api)
configuration_list = api.available_backends()
config_list = api.available_backends()
backend_name_list = []
for configuration in configuration_list:
configuration_edit = {}
backend_name = configuration['name']
for config in config_list:
config_edit = {}
backend_name = config['name']
backend_name_list.append(backend_name)
configuration_edit['local'] = False
for key in configuration.keys():
config_edit['local'] = False
for key in config.keys():
new_key = _snake_case_to_camel_case(key)
if new_key not in ['id', 'serial_number', 'topology_id', 'status']:
configuration_edit[new_key] = configuration[key]
if new_key == 'coupling_map':
if isinstance(configuration[key], list):
cmap = mapper.coupling_list2dict(configuration[key])
configuration_edit[new_key] = cmap
config_edit[new_key] = config[key]
# online_qasm_simulator uses different name for basis_gates
if 'gateSet' in configuration:
configuration_edit['basis_gates'] = configuration['gateSet']
del configuration_edit['gate_set']
if 'gateSet' in config:
config_edit['basis_gates'] = config['gateSet']
del config_edit['gate_set']
# ibmqx_qasm_simulator doesn't report coupling_map
if ('coupling_map' not in configuration_edit.keys() and
configuration['simulator']):
configuration_edit['coupling_map'] = 'all-to-all'
if 'coupling_map' not in config_edit.keys() and config['simulator']:
config_edit['coupling_map'] = 'all-to-all'
registered_backend = RegisteredBackend(backend_name,
QeRemote,
configuration_edit)
config_edit)
_REGISTERED_BACKENDS[backend_name] = registered_backend
return backend_name_list

Expand Down Expand Up @@ -153,7 +148,7 @@ def update_backends(api=None):
return backend_name_list


def register_backend(cls, configuration=None):
def register_backend(cls, configuration_=None):
"""Register a backend in the list of available backends.

Register a `cls` backend in the `_REGISTERED_BACKENDS` dict, validating
Expand All @@ -164,7 +159,7 @@ def register_backend(cls, configuration=None):

Args:
cls (class): a subclass of BaseBackend that contains a backend
configuration (dict): backend configuration to use instead of class'
configuration_ (dict): backend configuration to use instead of class'
default.

Returns:
Expand All @@ -184,7 +179,7 @@ def register_backend(cls, configuration=None):
raise QISKitError('Could not register backend: %s is not a subclass '
'of BaseBackend' % cls)
try:
backend_instance = cls(configuration=configuration)
backend_instance = cls(configuration=configuration_)
except Exception as err:
raise QISKitError('Could not register backend: %s could not be '
'instantiated: %s' % (cls, err))
Expand All @@ -207,7 +202,7 @@ def deregister_backend(backend_name):
"""Remove backend from list of available backens

Args:
backend_name (str): name of backend to unregister
backend_name (str): name of backend to deregister

Raises:
KeyError if backend_name is not registered.
Expand Down Expand Up @@ -247,13 +242,13 @@ def get_backend_instance(backend_name):
"""
try:
registered_backend = _REGISTERED_BACKENDS[backend_name]
return registered_backend.cls(
configuration=registered_backend.configuration)
except KeyError:
raise LookupError('backend "{}" is not available'.format(backend_name))
return registered_backend.cls(
configuration=registered_backend.configuration)


def get_backend_configuration(backend_name):
def configuration(backend_name):
"""Return the configuration for the named backend.

Args:
Expand All @@ -271,6 +266,65 @@ def get_backend_configuration(backend_name):
raise LookupError('backend "{}" is not available'.format(backend_name))


def calibration(backend_name):
"""Return the calibration for the named backend.

Args:
backend_name (str): the backend name

Returns:
dict: calibration dict

Raises:
LookupError: if backend is unavailable
"""
try:
backend = qiskit.backends.get_backend_instance(backend_name)
return backend.calibration
except KeyError:
raise LookupError('backend "{}" is not available'.format(backend_name))


def parameters(backend_name):
"""Return the online backend parameters.

Args:
backend_name (str): Name of the backend.

Returns:
dict: The parameters of the named backend.

Raises:
ConnectionError: if the API call failed.
LookupError: If parameters for the named backend can't be
found.
"""
try:
backend = qiskit.backends.get_backend_instance(backend_name)
return backend.parameters
except KeyError:
raise LookupError('backend "{}" is not available'.format(backend_name))


def status(backend_name):
"""Return the status for the named backend.

Args:
backend_name (str): the backend name

Returns:
dict: status dict

Raises:
LookupError: if backend is unavailable
"""
try:
backend = qiskit.backends.get_backend_instance(backend_name)
return backend.status
except KeyError:
raise LookupError('backend "{}" is not available'.format(backend_name))


def local_backends():
"""Get the local backends."""
return [backend.name for backend in _REGISTERED_BACKENDS.values()
Expand Down
17 changes: 17 additions & 0 deletions qiskit/backends/_basebackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,20 @@ def run(self, q_job):
def configuration(self):
"""Return backend configuration"""
return self._configuration

@property
def calibration(self):
"""Return backend calibration"""
backend_name = self.configuration['name']
return {'backend': backend_name, 'calibrations': None}

@property
def parameters(self):
"""Return backend parameters"""
backend_name = self.configuration['name']
return {'backend': backend_name, 'parameters': None}

@property
def status(self):
"""Return backend status"""
return {'available': True}
92 changes: 92 additions & 0 deletions qiskit/backends/_qeremote.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import time
import logging
import pprint
import re
from qiskit.backends._basebackend import BaseBackend
from qiskit import _openquantumcompiler as openquantumcompiler
from qiskit import QISKitError
Expand All @@ -29,6 +30,9 @@

logger = logging.getLogger(__name__)

FIRST_CAP_RE = re.compile('(.)([A-Z][a-z]+)')
ALL_CAP_RE = re.compile('([a-z0-9])([A-Z])')


class QeRemote(BaseBackend):
"""Backend class interfacing with the Quantum Experience remotely.
Expand Down Expand Up @@ -115,6 +119,87 @@ def set_api(cls, api):
"""Associate API with class"""
cls._api = api

@property
def calibration(self):
"""Return the online backend calibrations.

The return is via QX API call.

Returns:
dict: The calibration of the backend.

Raises:
ConnectionError: if the API call failed.
LookupError: If a configuration for the backend can't be found.
"""
if not self._api:
raise ConnectionError('API not set')

try:
backend_name = self.configuration['name']
calibrations = self._api.backend_calibration(backend_name)
except Exception as ex:
raise LookupError(
"Couldn't get backend calibration: {0}".format(ex))

calibrations_edit = {}
for key, vals in calibrations.items():
new_key = _snake_case_to_camel_case(key)
calibrations_edit[new_key] = vals

return calibrations_edit

@property
def parameters(self):
"""Return the online backend parameters.

Returns:
dict: The parameters of the backend.

Raises:
ConnectionError: if the API call faled.
LookupError: If parameters for the backend can't be found.
"""
if not self._api:
raise ConnectionError('API not set')

try:
backend_name = self.configuration['name']
parameters = self._api.backend_parameters(backend_name)
except Exception as ex:
raise LookupError(
"Couldn't get backend parameters: {0}".format(ex))

parameters_edit = {}
for key, vals in parameters.items():
new_key = _snake_case_to_camel_case(key)
parameters_edit[new_key] = vals

return parameters_edit

@property
def status(self):
"""Return the online backend status.

Returns:
dict: The status of the backend.

Raises:
ConnectionError: if the API call failed.
LookupError: If status for the backend can't be found.
"""
if not self._api:
raise ConnectionError('API not set')

try:
backend_name = self.configuration['name']
status = self._api.backend_status(backend_name)
except Exception as ex:
raise LookupError(
"Couldn't get backend status: {0}".format(ex))

return status


def _wait_for_job(jobid, api, wait=5, timeout=60):
"""Wait until all online ran circuits of a qobj are 'COMPLETED'.
Expand Down Expand Up @@ -161,3 +246,10 @@ def _wait_for_job(jobid, api, wait=5, timeout=60):
'status': job_result['qasms'][index]['status']})
return {'job_id': jobid, 'status': job_result['status'],
'result': job_result_return}


# this is also in _backendutils but using that was creating cyclic import.
def _snake_case_to_camel_case(name):
"""Return a snake case string from a camelcase string."""
string_1 = FIRST_CAP_RE.sub(r'\1_\2', name)
return ALL_CAP_RE.sub(r'\1_\2', string_1).lower()
Loading