From 3ad5ec437d6782ebf27ab17841dc861c824c976c Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Fri, 3 May 2019 21:42:01 +0200 Subject: [PATCH 01/13] Add IBMQ.circuits Add initial draft of the Qcircuits module, porting the original code. --- qiskit/providers/ibmq/api_v2/exceptions.py | 7 +- qiskit/providers/ibmq/api_v2/ibmqclient.py | 12 ++ qiskit/providers/ibmq/api_v2/rest/root.py | 28 ++++- qiskit/providers/ibmq/api_v2/session.py | 6 +- qiskit/providers/ibmq/ibmqprovider.py | 25 +++++ qiskit/providers/ibmq/qcircuits/__init__.py | 17 +++ qiskit/providers/ibmq/qcircuits/manager.py | 118 ++++++++++++++++++++ 7 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 qiskit/providers/ibmq/qcircuits/__init__.py create mode 100644 qiskit/providers/ibmq/qcircuits/manager.py diff --git a/qiskit/providers/ibmq/api_v2/exceptions.py b/qiskit/providers/ibmq/api_v2/exceptions.py index 22d2ca40a..46adc7bde 100644 --- a/qiskit/providers/ibmq/api_v2/exceptions.py +++ b/qiskit/providers/ibmq/api_v2/exceptions.py @@ -18,6 +18,11 @@ class ApiError(ApiErrorV1): """Generic IBM Q API error.""" - def __init__(self, *args, original_exception=None, **kwargs): + pass + + +class RequestsApiError(ApiError): + """Exception re-raising a RequestException.""" + def __init__(self, original_exception, *args, **kwargs): self.original_exception = original_exception super().__init__(*args, **kwargs) diff --git a/qiskit/providers/ibmq/api_v2/ibmqclient.py b/qiskit/providers/ibmq/api_v2/ibmqclient.py index 5296d193a..40e004f86 100644 --- a/qiskit/providers/ibmq/api_v2/ibmqclient.py +++ b/qiskit/providers/ibmq/api_v2/ibmqclient.py @@ -203,6 +203,18 @@ def api_version(self): """ return self.api_client.version() + def qcircuit_run(self, name, **kwargs): + """Execute a Qcircuit. + + Args: + name (str): name of the Qcircuit. + **kwargs (dict): arguments for the Qcircuit. + + Returns: + dict: json response. + """ + return self.api_client.qcircuit(name, **kwargs) + # Endpoints for compatibility with classic IBMQConnector. These functions # are meant to facilitate the transition, and should be removed moving # forward. diff --git a/qiskit/providers/ibmq/api_v2/rest/root.py b/qiskit/providers/ibmq/api_v2/rest/root.py index 1ecf0c272..37f384566 100644 --- a/qiskit/providers/ibmq/api_v2/rest/root.py +++ b/qiskit/providers/ibmq/api_v2/rest/root.py @@ -29,6 +29,7 @@ class Api(RestAdapterBase): 'hubs': '/Network', 'jobs': '/Jobs', 'jobs_status': '/Jobs/status', + 'qcircuit': '/QCircuitApiModels', 'version': '/version' } @@ -100,9 +101,30 @@ def run_job(self, backend_name, qobj_dict): """ url = self.get_url('jobs') - payload = {'qObject': qobj_dict, - 'backend': {'name': backend_name}, - 'shots': qobj_dict.get('config', {}).get('shots', 1)} + payload = { + 'qObject': qobj_dict, + 'backend': {'name': backend_name}, + 'shots': qobj_dict.get('config', {}).get('shots', 1) + } + + return self.session.post(url, json=payload).json() + + def qcircuit(self, name, **kwargs): + """Execute a Qcircuit. + + Args: + name (str): name of the Qcircuit. + **kwargs (dict): arguments for the Qcircuit. + + Returns: + dict: json response. + """ + url = self.get_url('qcircuit') + + payload = { + 'name': name, + 'params': kwargs + } return self.session.post(url, json=payload).json() diff --git a/qiskit/providers/ibmq/api_v2/session.py b/qiskit/providers/ibmq/api_v2/session.py index 4c57d0ce8..daf63f2a4 100644 --- a/qiskit/providers/ibmq/api_v2/session.py +++ b/qiskit/providers/ibmq/api_v2/session.py @@ -18,8 +18,7 @@ from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry -from .exceptions import ApiError - +from .exceptions import RequestsApiError STATUS_FORCELIST = ( 500, # Internal Server Error @@ -116,7 +115,6 @@ def request(self, method, url, **kwargs): if self.access_token: message = message.replace(self.access_token, '[redacted]') - raise ApiError(message, - original_exception=ex) + raise RequestsApiError(ex, message) from None return response diff --git a/qiskit/providers/ibmq/ibmqprovider.py b/qiskit/providers/ibmq/ibmqprovider.py index 4c6d65e76..8ac0f87f6 100644 --- a/qiskit/providers/ibmq/ibmqprovider.py +++ b/qiskit/providers/ibmq/ibmqprovider.py @@ -24,6 +24,7 @@ read_credentials_from_qiskitrc, store_credentials, discover_credentials) from .exceptions import IBMQAccountError from .ibmqsingleprovider import IBMQSingleProvider +from .qcircuits import QcircuitsManager QE_URL = 'https://quantumexperience.ng.bluemix.net/api' @@ -42,6 +43,12 @@ def __init__(self): # keys are tuples (hub, group, project), as the convention is that # that tuple uniquely identifies a set of credentials. self._accounts = OrderedDict() + self._qcircuits_manager = QcircuitsManager() + + @property + def circuits(self): + """Entry point for Qcircuit invocation.""" + return self._qcircuits_manager def backends(self, name=None, filters=None, **kwargs): """Return all backends accessible via IBMQ provider, subject to optional filtering. @@ -211,6 +218,11 @@ def disable_accounts(self, **kwargs): credentials = Credentials(current_creds[creds].credentials.token, current_creds[creds].credentials.url) if self._credentials_match_filter(credentials, kwargs): + # Remove api from qcircuits manager if in use. + if (self._accounts[credentials.unique_id()]._api == + self._qcircuits_manager.client): + self._qcircuits_manager.client = None + del self._accounts[credentials.unique_id()] disabled = True @@ -249,14 +261,27 @@ def _append_account(self, credentials): Returns: IBMQSingleProvider: new single-account provider. """ + update_qcircuits_manager = False + # Use the first account as the account for qcircuits. + if not self._accounts: + update_qcircuits_manager = True + # Check if duplicated credentials are already in use. By convention, # we assume (hub, group, project) is always unique. if credentials.unique_id() in self._accounts.keys(): warnings.warn('Credentials are already in use.') + # Remove api from qcircuits manager if in use. + if (self._accounts[credentials.unique_id()]._api == + self._qcircuits_manager.client): + update_qcircuits_manager = True + single_provider = IBMQSingleProvider(credentials, self) self._accounts[credentials.unique_id()] = single_provider + if update_qcircuits_manager: + self._qcircuits_manager.client = single_provider._api + return single_provider def _credentials_match_filter(self, credentials, filter_dict): diff --git a/qiskit/providers/ibmq/qcircuits/__init__.py b/qiskit/providers/ibmq/qcircuits/__init__.py new file mode 100644 index 000000000..a489505f7 --- /dev/null +++ b/qiskit/providers/ibmq/qcircuits/__init__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Module for interacting with QCircuits.""" + +from .manager import QcircuitsManager diff --git a/qiskit/providers/ibmq/qcircuits/manager.py b/qiskit/providers/ibmq/qcircuits/manager.py new file mode 100644 index 000000000..a33b77f80 --- /dev/null +++ b/qiskit/providers/ibmq/qcircuits/manager.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Manager for interacting with QCircuits.""" + +from functools import wraps + +from qiskit.providers.ibmq.api_v2.exceptions import RequestsApiError + +from ..exceptions import IBMQError + + +GRAPH_STATE = 'graph_state' +HARDWARE_EFFICIENT = 'hardware_efficient' +RANDOM_UNIFORM = 'random_uniform' + + +def requires_api_connection(func): + """Decorator that ensures that a QCircuitsManager has a valid API.""" + @wraps(func) + def wrapper(self, *args, **kwargs): + if not self.client: + raise IBMQError( + 'An account must be loaded in order to use QCircuits') + + return func(self, *args, **kwargs) + + return wrapper + + +class QcircuitsManager: + """Class that provides access to the different qcircuits.""" + def __init__(self): + self.client = None + + def _call_qcircuit(self, name, **kwargs): + """Execute a Qcircuit. + + Args: + name (str): name of the Qcircuit. + **kwargs (dict): parameters passed to the Qcircuit. + + Returns: + dict: response. + + Raises: + IBMQError: if the Qcircuit could not be executed. + RequestsApiError: if the request could not be completed. + """ + try: + response = self.client.qcircuit_run(name=name, **kwargs) + except RequestsApiError as ex: + # Revise the original requests exception to intercept. + response = ex.original_exception.response + if response.status_code == 400: + if response.json()['error']['code'] == 'HUB_NOT_FOUND': + raise IBMQError('Qcircuit support is not available') from None + + raise + + return response + + @requires_api_connection + def graph_state(self, number_of_qubits, adjacency_matrix, angles): + """Execute the graph state Qcircuit. + + Args: + number_of_qubits: + adjacency_matrix: + angles: + + Returns: + dict: response. + """ + + return self._call_qcircuit(name=GRAPH_STATE, + number_of_qubits=number_of_qubits, + adjacency_matrix=adjacency_matrix, + angles=angles) + + @requires_api_connection + def hardware_efficient(self, number_of_qubits, angles): + """Execute the hardware efficient Qcircuit. + + Args: + number_of_qubits: + angles: + + Returns: + dict: response. + """ + return self._call_qcircuit(name=HARDWARE_EFFICIENT, + number_of_qubits=number_of_qubits, + angles=angles) + + @requires_api_connection + def random_uniform(self, number_of_qubits): + """Execute the random uniform Qcircuit. + + Args: + number_of_qubits: + + Returns: + dict: response + """ + return self._call_qcircuit(name=RANDOM_UNIFORM, + number_of_qubits=number_of_qubits) From 9f583c7b6a2006712ce680da247a651f74507301 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sat, 4 May 2019 11:36:25 +0200 Subject: [PATCH 02/13] Update qcircuit return value and error handling --- qiskit/providers/ibmq/qcircuits/manager.py | 37 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/qiskit/providers/ibmq/qcircuits/manager.py b/qiskit/providers/ibmq/qcircuits/manager.py index a33b77f80..324c27e0d 100644 --- a/qiskit/providers/ibmq/qcircuits/manager.py +++ b/qiskit/providers/ibmq/qcircuits/manager.py @@ -16,6 +16,7 @@ from functools import wraps +from qiskit.providers.ibmq.ibmqjob import IBMQJob from qiskit.providers.ibmq.api_v2.exceptions import RequestsApiError from ..exceptions import IBMQError @@ -52,7 +53,7 @@ def _call_qcircuit(self, name, **kwargs): **kwargs (dict): parameters passed to the Qcircuit. Returns: - dict: response. + Result: the result of executing the circuit. Raises: IBMQError: if the Qcircuit could not be executed. @@ -63,13 +64,35 @@ def _call_qcircuit(self, name, **kwargs): except RequestsApiError as ex: # Revise the original requests exception to intercept. response = ex.original_exception.response + + # Check for specific error due to hub not available. if response.status_code == 400: - if response.json()['error']['code'] == 'HUB_NOT_FOUND': + try: + response_body = response.json() + except ValueError: + response_body = {} + + if response_body.get('error', {}).get('code') == 'HUB_NOT_FOUND': raise IBMQError('Qcircuit support is not available') from None - raise + if response.status_code == 401: + raise IBMQError('Qcircuit support is not available') from None + except Exception as ex: + raise IBMQError('Qcircuit could not be executed: {}'.format(ex)) + + # Create a Job for the qcircuit. + try: + job = IBMQJob(backend=None, + job_id=response['id'], + api=self.client, + creation_date=response['creationDate'], + api_status=response['status']) + except Exception as ex: + raise IBMQError( + 'Qcircuit could not be executed: invalid response: {}'.format(ex)) - return response + # Wait for the job to complete. + return job.result() @requires_api_connection def graph_state(self, number_of_qubits, adjacency_matrix, angles): @@ -81,7 +104,7 @@ def graph_state(self, number_of_qubits, adjacency_matrix, angles): angles: Returns: - dict: response. + Result: the result of executing the circuit. """ return self._call_qcircuit(name=GRAPH_STATE, @@ -98,7 +121,7 @@ def hardware_efficient(self, number_of_qubits, angles): angles: Returns: - dict: response. + Result: the result of executing the circuit. """ return self._call_qcircuit(name=HARDWARE_EFFICIENT, number_of_qubits=number_of_qubits, @@ -112,7 +135,7 @@ def random_uniform(self, number_of_qubits): number_of_qubits: Returns: - dict: response + Result: the result of executing the circuit. """ return self._call_qcircuit(name=RANDOM_UNIFORM, number_of_qubits=number_of_qubits) From 6468ff3fbd6ca4ab80c9bf75b17d1cf2f4d1d3fe Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sat, 4 May 2019 11:46:21 +0200 Subject: [PATCH 03/13] Update qcircuits documentation --- qiskit/providers/ibmq/qcircuits/manager.py | 47 +++++++++++++++++----- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/qiskit/providers/ibmq/qcircuits/manager.py b/qiskit/providers/ibmq/qcircuits/manager.py index 324c27e0d..969ae7f79 100644 --- a/qiskit/providers/ibmq/qcircuits/manager.py +++ b/qiskit/providers/ibmq/qcircuits/manager.py @@ -50,7 +50,7 @@ def _call_qcircuit(self, name, **kwargs): Args: name (str): name of the Qcircuit. - **kwargs (dict): parameters passed to the Qcircuit. + **kwargs: parameters passed to the Qcircuit. Returns: Result: the result of executing the circuit. @@ -98,10 +98,24 @@ def _call_qcircuit(self, name, **kwargs): def graph_state(self, number_of_qubits, adjacency_matrix, angles): """Execute the graph state Qcircuit. + This circuit implements graph state circuits that are measured in a + product basis. Measurement angles can be chosen to measure graph state + stabilizers (for validation/characterization) or to measure in a basis + such that the circuit family may be hard to classically simulate. + Args: - number_of_qubits: - adjacency_matrix: - angles: + number_of_qubits (int): number of qubits to use, in the 2-20 range. + adjacency_matrix (list[list]): square matrix of elements whose + values are 0 or 1. The matrix size is `number_of_qubits` by + `number_of_qubits` and is expected to be symmetric and have + zeros on the diagonal. + angles (list[float]): list of phase angles, each in the interval + `[0, 2*pi)` radians. There should be 3 * number_of_qubits + elements in the array. The first three elements are the + theta, phi, and lambda angles, respectively, of a u3 gate + acting on the first qubit. Each of the number_of_qubits triples + is interpreted accordingly as the parameters of a u3 gate + acting on subsequent qubits. Returns: Result: the result of executing the circuit. @@ -116,9 +130,15 @@ def graph_state(self, number_of_qubits, adjacency_matrix, angles): def hardware_efficient(self, number_of_qubits, angles): """Execute the hardware efficient Qcircuit. + This circuit implements the random lattice circuit across a user + specified number of qubits and phase angles. + Args: - number_of_qubits: - angles: + number_of_qubits (int): number of qubits to use, in the 4-20 range. + angles (list): array of three phase angles (x/y/z) each from + 0 to 4*Pi, one set for each qubit of each layer of the lattice. + There should be 3 * number_of_qubits * desired lattice depth + entries in the array. Returns: Result: the result of executing the circuit. @@ -128,14 +148,21 @@ def hardware_efficient(self, number_of_qubits, angles): angles=angles) @requires_api_connection - def random_uniform(self, number_of_qubits): + def random_uniform(self, number_of_qubits=None): """Execute the random uniform Qcircuit. + This circuit implements hadamard gates across all available qubits on + the device. + Args: - number_of_qubits: + number_of_qubits (int) : optional argument for number of qubits to use. + If not specified will use all qubits on device. Returns: Result: the result of executing the circuit. """ - return self._call_qcircuit(name=RANDOM_UNIFORM, - number_of_qubits=number_of_qubits) + kwargs = {} + if number_of_qubits is not None: + kwargs['number_of_qubits'] = number_of_qubits + + return self._call_qcircuit(name=RANDOM_UNIFORM, **kwargs) From df70ea042265673b02eba75a38120cf8b5557c47 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sat, 4 May 2019 14:35:09 +0200 Subject: [PATCH 04/13] Add compatible qcircuit_run to IBMQConnector --- qiskit/providers/ibmq/api/ibmqconnector.py | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/qiskit/providers/ibmq/api/ibmqconnector.py b/qiskit/providers/ibmq/api/ibmqconnector.py index 75ebaf32b..2511673a9 100644 --- a/qiskit/providers/ibmq/api/ibmqconnector.py +++ b/qiskit/providers/ibmq/api/ibmqconnector.py @@ -385,6 +385,30 @@ def available_backends(self): return response + def qcircuit_run(self, name, **kwargs): + """Execute a Qcircuit. + + Args: + name (str): name of the Qcircuit. + **kwargs (dict): arguments for the Qcircuit. + + Returns: + dict: json response. + """ + if not self.check_credentials(): + raise CredentialsError('credentials invalid') + + url = '/QCircuitApiModels' + + payload = { + 'name': name, + 'params': kwargs + } + + response = self.req.post(url, data=json.dumps(payload)) + + return response + def websocket_client(self): """Return a websocket client for interacting with IBMQ. From 7549db4a285a536d875b33d466b77c8dd6af2c83 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sat, 4 May 2019 14:38:27 +0200 Subject: [PATCH 05/13] Update qcircuit error handling --- qiskit/providers/ibmq/qcircuits/manager.py | 53 ++++++++++++++++------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/qiskit/providers/ibmq/qcircuits/manager.py b/qiskit/providers/ibmq/qcircuits/manager.py index 969ae7f79..d8dcaa4d3 100644 --- a/qiskit/providers/ibmq/qcircuits/manager.py +++ b/qiskit/providers/ibmq/qcircuits/manager.py @@ -16,6 +16,7 @@ from functools import wraps +from qiskit.providers.models import JobStatus from qiskit.providers.ibmq.ibmqjob import IBMQJob from qiskit.providers.ibmq.api_v2.exceptions import RequestsApiError @@ -26,6 +27,11 @@ HARDWARE_EFFICIENT = 'hardware_efficient' RANDOM_UNIFORM = 'random_uniform' +# User friendly error messages. +QCIRCUIT_NOT_ALLOWED = 'Qcircuit support is not available yet in this account' +QCIRCUIT_SUBMIT_ERROR = 'Qcircuit could not be submitted: {}' +QCIRCUIT_RESULT_ERROR = 'Qcircuit result could not be returned: {}' + def requires_api_connection(func): """Decorator that ensures that a QCircuitsManager has a valid API.""" @@ -65,20 +71,37 @@ def _call_qcircuit(self, name, **kwargs): # Revise the original requests exception to intercept. response = ex.original_exception.response - # Check for specific error due to hub not available. + # Check for errors related to the submission. + try: + body = response.json() + except ValueError: + body = {} + + # Generic authorization or unavailable endpoint error. + if response.status_code in (401, 404): + raise IBMQError(QCIRCUIT_NOT_ALLOWED) from None + if response.status_code == 400: - try: - response_body = response.json() - except ValueError: - response_body = {} + # Hub permission error. + if body.get('error', {}).get('code') == 'HUB_NOT_FOUND': + raise IBMQError(QCIRCUIT_NOT_ALLOWED) from None - if response_body.get('error', {}).get('code') == 'HUB_NOT_FOUND': - raise IBMQError('Qcircuit support is not available') from None + # Generic error. + if body.get('error', {}).get('code') == 'GENERIC_ERROR': + raise IBMQError(QCIRCUIT_NOT_ALLOWED) from None - if response.status_code == 401: - raise IBMQError('Qcircuit support is not available') from None + # Handle the rest of the exceptions as unexpected. + raise IBMQError(QCIRCUIT_SUBMIT_ERROR.format(ex)) except Exception as ex: - raise IBMQError('Qcircuit could not be executed: {}'.format(ex)) + # Handle non-requests exception as unexpected. + raise IBMQError(QCIRCUIT_SUBMIT_ERROR.format(ex)) + + # Extra check for IBMQConnector code path. + if 'error' in response: + if response['error'].get('code') == 'HUB_NOT_FOUND': + raise IBMQError(QCIRCUIT_NOT_ALLOWED) from None + else: + raise IBMQError(QCIRCUIT_SUBMIT_ERROR.format(response)) # Create a Job for the qcircuit. try: @@ -88,10 +111,14 @@ def _call_qcircuit(self, name, **kwargs): creation_date=response['creationDate'], api_status=response['status']) except Exception as ex: - raise IBMQError( - 'Qcircuit could not be executed: invalid response: {}'.format(ex)) + raise IBMQError(QCIRCUIT_RESULT_ERROR.format(ex)) + + # Wait for the job to complete, explicitly checking for errors. + job._wait_for_completion() + if job.status() is JobStatus.ERROR: + raise IBMQError(QCIRCUIT_RESULT_ERROR.format( + 'Job {} finished with an error'.format(job.job_id()))) - # Wait for the job to complete. return job.result() @requires_api_connection From 9f812dbd86b1a8725de3b6cfa1c2cdd6b5ef4e89 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sat, 4 May 2019 14:52:38 +0200 Subject: [PATCH 06/13] Add Qcircuit exceptions --- qiskit/providers/ibmq/api/ibmqconnector.py | 3 ++ qiskit/providers/ibmq/qcircuits/exceptions.py | 48 +++++++++++++++++++ qiskit/providers/ibmq/qcircuits/manager.py | 40 ++++++++-------- 3 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 qiskit/providers/ibmq/qcircuits/exceptions.py diff --git a/qiskit/providers/ibmq/api/ibmqconnector.py b/qiskit/providers/ibmq/api/ibmqconnector.py index 2511673a9..38a25cd4a 100644 --- a/qiskit/providers/ibmq/api/ibmqconnector.py +++ b/qiskit/providers/ibmq/api/ibmqconnector.py @@ -394,6 +394,9 @@ def qcircuit_run(self, name, **kwargs): Returns: dict: json response. + + Raises: + CredentialsError: if the user was not authenticated. """ if not self.check_credentials(): raise CredentialsError('credentials invalid') diff --git a/qiskit/providers/ibmq/qcircuits/exceptions.py b/qiskit/providers/ibmq/qcircuits/exceptions.py new file mode 100644 index 000000000..72b2ab03b --- /dev/null +++ b/qiskit/providers/ibmq/qcircuits/exceptions.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Exceptions related to Qcircuits.""" + +from qiskit.providers.ibmq.exceptions import IBMQError + + +QCIRCUIT_NOT_ALLOWED = 'Qcircuit support is not available yet in this account' +QCIRCUIT_SUBMIT_ERROR = 'Qcircuit could not be submitted: {}' +QCIRCUIT_RESULT_ERROR = 'Qcircuit result could not be returned: {}' + + +class QcircuitError(IBMQError): + """Generic Qcircuit exception.""" + pass + + +class QcircuitAvailabilityError(QcircuitError): + """Error while accessing a Qcircuit.""" + + def __init__(self, message=''): + super().__init__(message or QCIRCUIT_NOT_ALLOWED) + + +class QcircuitSubmitError(QcircuitError): + """Error while submitting a Qcircuit.""" + + def __init__(self, message): + super().__init__(QCIRCUIT_SUBMIT_ERROR.format(message)) + + +class QcircuitResultError(QcircuitError): + """Error during the results of a Qcircuit.""" + + def __init__(self, message): + super().__init__(QCIRCUIT_RESULT_ERROR.format(message)) diff --git a/qiskit/providers/ibmq/qcircuits/manager.py b/qiskit/providers/ibmq/qcircuits/manager.py index d8dcaa4d3..d9daa23d9 100644 --- a/qiskit/providers/ibmq/qcircuits/manager.py +++ b/qiskit/providers/ibmq/qcircuits/manager.py @@ -20,25 +20,21 @@ from qiskit.providers.ibmq.ibmqjob import IBMQJob from qiskit.providers.ibmq.api_v2.exceptions import RequestsApiError -from ..exceptions import IBMQError +from .exceptions import (QcircuitAvailabilityError, QcircuitResultError, + QcircuitSubmitError) GRAPH_STATE = 'graph_state' HARDWARE_EFFICIENT = 'hardware_efficient' RANDOM_UNIFORM = 'random_uniform' -# User friendly error messages. -QCIRCUIT_NOT_ALLOWED = 'Qcircuit support is not available yet in this account' -QCIRCUIT_SUBMIT_ERROR = 'Qcircuit could not be submitted: {}' -QCIRCUIT_RESULT_ERROR = 'Qcircuit result could not be returned: {}' - def requires_api_connection(func): """Decorator that ensures that a QCircuitsManager has a valid API.""" @wraps(func) def wrapper(self, *args, **kwargs): if not self.client: - raise IBMQError( + raise QcircuitAvailabilityError( 'An account must be loaded in order to use QCircuits') return func(self, *args, **kwargs) @@ -62,8 +58,10 @@ def _call_qcircuit(self, name, **kwargs): Result: the result of executing the circuit. Raises: - IBMQError: if the Qcircuit could not be executed. - RequestsApiError: if the request could not be completed. + QcircuitAvailabilityError: if Qcircuits are not available. + QcircuitSubmitError: if there was an error submitting the Qcircuit. + QcircuitResultError: if the result of the Qcircuit could not be + returned. """ try: response = self.client.qcircuit_run(name=name, **kwargs) @@ -79,29 +77,29 @@ def _call_qcircuit(self, name, **kwargs): # Generic authorization or unavailable endpoint error. if response.status_code in (401, 404): - raise IBMQError(QCIRCUIT_NOT_ALLOWED) from None + raise QcircuitAvailabilityError() from None if response.status_code == 400: # Hub permission error. if body.get('error', {}).get('code') == 'HUB_NOT_FOUND': - raise IBMQError(QCIRCUIT_NOT_ALLOWED) from None + raise QcircuitAvailabilityError() from None # Generic error. if body.get('error', {}).get('code') == 'GENERIC_ERROR': - raise IBMQError(QCIRCUIT_NOT_ALLOWED) from None + raise QcircuitAvailabilityError() from None # Handle the rest of the exceptions as unexpected. - raise IBMQError(QCIRCUIT_SUBMIT_ERROR.format(ex)) + raise QcircuitSubmitError(str(ex)) except Exception as ex: # Handle non-requests exception as unexpected. - raise IBMQError(QCIRCUIT_SUBMIT_ERROR.format(ex)) + raise QcircuitSubmitError(str(ex)) # Extra check for IBMQConnector code path. if 'error' in response: if response['error'].get('code') == 'HUB_NOT_FOUND': - raise IBMQError(QCIRCUIT_NOT_ALLOWED) from None + raise QcircuitAvailabilityError() from None else: - raise IBMQError(QCIRCUIT_SUBMIT_ERROR.format(response)) + raise QcircuitSubmitError(str(response)) # Create a Job for the qcircuit. try: @@ -111,13 +109,13 @@ def _call_qcircuit(self, name, **kwargs): creation_date=response['creationDate'], api_status=response['status']) except Exception as ex: - raise IBMQError(QCIRCUIT_RESULT_ERROR.format(ex)) + raise QcircuitResultError(str(ex)) # Wait for the job to complete, explicitly checking for errors. job._wait_for_completion() if job.status() is JobStatus.ERROR: - raise IBMQError(QCIRCUIT_RESULT_ERROR.format( - 'Job {} finished with an error'.format(job.job_id()))) + raise QcircuitResultError( + 'Job {} finished with an error'.format(job.job_id())) return job.result() @@ -182,8 +180,8 @@ def random_uniform(self, number_of_qubits=None): the device. Args: - number_of_qubits (int) : optional argument for number of qubits to use. - If not specified will use all qubits on device. + number_of_qubits (int) : optional argument for number of qubits to + use. If not specified will use all qubits on device. Returns: Result: the result of executing the circuit. From 8f9f124b07181877b3c188d88a1bab5ff7ccbbd5 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sat, 4 May 2019 15:54:31 +0200 Subject: [PATCH 07/13] Add minimal parameter validation for qcircuits --- qiskit/providers/ibmq/qcircuits/manager.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/qiskit/providers/ibmq/qcircuits/manager.py b/qiskit/providers/ibmq/qcircuits/manager.py index d9daa23d9..fd7b627bc 100644 --- a/qiskit/providers/ibmq/qcircuits/manager.py +++ b/qiskit/providers/ibmq/qcircuits/manager.py @@ -20,7 +20,8 @@ from qiskit.providers.ibmq.ibmqjob import IBMQJob from qiskit.providers.ibmq.api_v2.exceptions import RequestsApiError -from .exceptions import (QcircuitAvailabilityError, QcircuitResultError, +from .exceptions import (QcircuitError, + QcircuitAvailabilityError, QcircuitResultError, QcircuitSubmitError) @@ -144,7 +145,14 @@ def graph_state(self, number_of_qubits, adjacency_matrix, angles): Returns: Result: the result of executing the circuit. + + Raises: + QcircuitError: if the parameters are not valid. """ + if 2 <= number_of_qubits <= 20: + raise QcircuitError('Invalid number_of_qubits') + if len(angles) != number_of_qubits*3: + raise QcircuitError('Invalid angles length') return self._call_qcircuit(name=GRAPH_STATE, number_of_qubits=number_of_qubits, @@ -167,7 +175,15 @@ def hardware_efficient(self, number_of_qubits, angles): Returns: Result: the result of executing the circuit. + + Raises: + QcircuitError: if the parameters are not valid. """ + if 4 <= number_of_qubits <= 20: + raise QcircuitError('Invalid number_of_qubits') + if len(angles) % 3*number_of_qubits != 0: + raise QcircuitError('Invalid angles length') + return self._call_qcircuit(name=HARDWARE_EFFICIENT, number_of_qubits=number_of_qubits, angles=angles) From 5602ef49730ead890521edfdbfdd8823ab58f750 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sat, 4 May 2019 16:04:35 +0200 Subject: [PATCH 08/13] Fix linter warning --- qiskit/providers/ibmq/qcircuits/manager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qiskit/providers/ibmq/qcircuits/manager.py b/qiskit/providers/ibmq/qcircuits/manager.py index fd7b627bc..8cd737da7 100644 --- a/qiskit/providers/ibmq/qcircuits/manager.py +++ b/qiskit/providers/ibmq/qcircuits/manager.py @@ -99,8 +99,7 @@ def _call_qcircuit(self, name, **kwargs): if 'error' in response: if response['error'].get('code') == 'HUB_NOT_FOUND': raise QcircuitAvailabilityError() from None - else: - raise QcircuitSubmitError(str(response)) + raise QcircuitSubmitError(str(response)) # Create a Job for the qcircuit. try: From dbbcb835263236db7df86ca47015791eda934dbe Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sun, 5 May 2019 12:40:27 +0200 Subject: [PATCH 09/13] Rename qcircuits folder --- qiskit/providers/ibmq/{qcircuits => circuits}/__init__.py | 2 +- qiskit/providers/ibmq/{qcircuits => circuits}/exceptions.py | 0 qiskit/providers/ibmq/{qcircuits => circuits}/manager.py | 2 +- qiskit/providers/ibmq/ibmqprovider.py | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename qiskit/providers/ibmq/{qcircuits => circuits}/__init__.py (93%) rename qiskit/providers/ibmq/{qcircuits => circuits}/exceptions.py (100%) rename qiskit/providers/ibmq/{qcircuits => circuits}/manager.py (99%) diff --git a/qiskit/providers/ibmq/qcircuits/__init__.py b/qiskit/providers/ibmq/circuits/__init__.py similarity index 93% rename from qiskit/providers/ibmq/qcircuits/__init__.py rename to qiskit/providers/ibmq/circuits/__init__.py index a489505f7..6f39ad46c 100644 --- a/qiskit/providers/ibmq/qcircuits/__init__.py +++ b/qiskit/providers/ibmq/circuits/__init__.py @@ -14,4 +14,4 @@ """Module for interacting with QCircuits.""" -from .manager import QcircuitsManager +from .manager import CircuitsManager diff --git a/qiskit/providers/ibmq/qcircuits/exceptions.py b/qiskit/providers/ibmq/circuits/exceptions.py similarity index 100% rename from qiskit/providers/ibmq/qcircuits/exceptions.py rename to qiskit/providers/ibmq/circuits/exceptions.py diff --git a/qiskit/providers/ibmq/qcircuits/manager.py b/qiskit/providers/ibmq/circuits/manager.py similarity index 99% rename from qiskit/providers/ibmq/qcircuits/manager.py rename to qiskit/providers/ibmq/circuits/manager.py index 8cd737da7..412edd977 100644 --- a/qiskit/providers/ibmq/qcircuits/manager.py +++ b/qiskit/providers/ibmq/circuits/manager.py @@ -43,7 +43,7 @@ def wrapper(self, *args, **kwargs): return wrapper -class QcircuitsManager: +class CircuitsManager: """Class that provides access to the different qcircuits.""" def __init__(self): self.client = None diff --git a/qiskit/providers/ibmq/ibmqprovider.py b/qiskit/providers/ibmq/ibmqprovider.py index 8ac0f87f6..f95450fdf 100644 --- a/qiskit/providers/ibmq/ibmqprovider.py +++ b/qiskit/providers/ibmq/ibmqprovider.py @@ -24,7 +24,7 @@ read_credentials_from_qiskitrc, store_credentials, discover_credentials) from .exceptions import IBMQAccountError from .ibmqsingleprovider import IBMQSingleProvider -from .qcircuits import QcircuitsManager +from .circuits import CircuitsManager QE_URL = 'https://quantumexperience.ng.bluemix.net/api' @@ -43,7 +43,7 @@ def __init__(self): # keys are tuples (hub, group, project), as the convention is that # that tuple uniquely identifies a set of credentials. self._accounts = OrderedDict() - self._qcircuits_manager = QcircuitsManager() + self._qcircuits_manager = CircuitsManager() @property def circuits(self): From 9cc3bc522b0b5c22627e747856d7b8a85aedd4a0 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sun, 5 May 2019 13:01:01 +0200 Subject: [PATCH 10/13] Fix import --- qiskit/providers/ibmq/circuits/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/ibmq/circuits/manager.py b/qiskit/providers/ibmq/circuits/manager.py index 412edd977..890ec9cde 100644 --- a/qiskit/providers/ibmq/circuits/manager.py +++ b/qiskit/providers/ibmq/circuits/manager.py @@ -16,7 +16,7 @@ from functools import wraps -from qiskit.providers.models import JobStatus +from qiskit.providers import JobStatus from qiskit.providers.ibmq.ibmqjob import IBMQJob from qiskit.providers.ibmq.api_v2.exceptions import RequestsApiError From 26a10723e8538c0c89df04b63b4a8ff686d61752 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sun, 5 May 2019 13:11:42 +0200 Subject: [PATCH 11/13] Update travis and requirements for terra stable --- .travis.yml | 9 +-------- requirements.txt | 1 + 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ae695563..643652dfa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,15 +33,8 @@ sudo: false stage_generic: &stage_generic install: # Install step for jobs that require compilation and qa. - # TODO: until terra 0.8 is released, install a terra branch and this - # package dependencies manually. - # - pip install -U -r requirements.txt - - pip install "requests>=2.19" - - pip install "requests-ntlm>=1.1.0" - - pip install "websockets>=7,<8" - - git clone https://github.com/Qiskit/qiskit-terra.git - pip install cython - - pip install -e qiskit-terra + - pip install -U -r requirements.txt - pip install -U -r requirements-dev.txt script: # Run the tests. diff --git a/requirements.txt b/requirements.txt index f199e5d3a..6b05fc6c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +qiskit-terra>=0.8 requests>=2.19 requests-ntlm>=1.1.0 websockets>=7,<8 From e23761643143b8661b1a7ca9fcf750516c64e903 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sun, 5 May 2019 13:55:19 +0200 Subject: [PATCH 12/13] Rename Qcircuits to Circuits --- qiskit/providers/ibmq/api/ibmqconnector.py | 8 +- qiskit/providers/ibmq/api_v2/ibmqclient.py | 10 +-- qiskit/providers/ibmq/api_v2/rest/auth.py | 5 +- qiskit/providers/ibmq/api_v2/rest/root.py | 12 +-- qiskit/providers/ibmq/circuits/__init__.py | 2 +- qiskit/providers/ibmq/circuits/exceptions.py | 30 +++---- qiskit/providers/ibmq/circuits/manager.py | 86 ++++++++++---------- qiskit/providers/ibmq/ibmqprovider.py | 28 +++---- 8 files changed, 90 insertions(+), 91 deletions(-) diff --git a/qiskit/providers/ibmq/api/ibmqconnector.py b/qiskit/providers/ibmq/api/ibmqconnector.py index 38a25cd4a..3136c0ff9 100644 --- a/qiskit/providers/ibmq/api/ibmqconnector.py +++ b/qiskit/providers/ibmq/api/ibmqconnector.py @@ -385,12 +385,12 @@ def available_backends(self): return response - def qcircuit_run(self, name, **kwargs): - """Execute a Qcircuit. + def circuit_run(self, name, **kwargs): + """Execute a Circuit. Args: - name (str): name of the Qcircuit. - **kwargs (dict): arguments for the Qcircuit. + name (str): name of the Circuit. + **kwargs (dict): arguments for the Circuit. Returns: dict: json response. diff --git a/qiskit/providers/ibmq/api_v2/ibmqclient.py b/qiskit/providers/ibmq/api_v2/ibmqclient.py index 40e004f86..a83c5a4b5 100644 --- a/qiskit/providers/ibmq/api_v2/ibmqclient.py +++ b/qiskit/providers/ibmq/api_v2/ibmqclient.py @@ -203,17 +203,17 @@ def api_version(self): """ return self.api_client.version() - def qcircuit_run(self, name, **kwargs): - """Execute a Qcircuit. + def circuit_run(self, name, **kwargs): + """Execute a Circuit. Args: - name (str): name of the Qcircuit. - **kwargs (dict): arguments for the Qcircuit. + name (str): name of the Circuit. + **kwargs (dict): arguments for the Circuit. Returns: dict: json response. """ - return self.api_client.qcircuit(name, **kwargs) + return self.api_client.circuit(name, **kwargs) # Endpoints for compatibility with classic IBMQConnector. These functions # are meant to facilitate the transition, and should be removed moving diff --git a/qiskit/providers/ibmq/api_v2/rest/auth.py b/qiskit/providers/ibmq/api_v2/rest/auth.py index d07e3a564..8e0bb2a93 100644 --- a/qiskit/providers/ibmq/api_v2/rest/auth.py +++ b/qiskit/providers/ibmq/api_v2/rest/auth.py @@ -45,9 +45,8 @@ def user_info(self): # Revise the URL. try: api_url = response['urls']['http'] - if api_url.endswith('.com?private=true'): - response['urls']['http'] = '{}/api'.format( - api_url.split('?')[0]) + if not api_url.endswith('/api'): + response['urls']['http'] = '{}/api'.format(api_url) except KeyError: pass diff --git a/qiskit/providers/ibmq/api_v2/rest/root.py b/qiskit/providers/ibmq/api_v2/rest/root.py index 37f384566..4a9b9cf06 100644 --- a/qiskit/providers/ibmq/api_v2/rest/root.py +++ b/qiskit/providers/ibmq/api_v2/rest/root.py @@ -29,7 +29,7 @@ class Api(RestAdapterBase): 'hubs': '/Network', 'jobs': '/Jobs', 'jobs_status': '/Jobs/status', - 'qcircuit': '/QCircuitApiModels', + 'circuit': '/QCircuitApiModels', 'version': '/version' } @@ -109,17 +109,17 @@ def run_job(self, backend_name, qobj_dict): return self.session.post(url, json=payload).json() - def qcircuit(self, name, **kwargs): - """Execute a Qcircuit. + def circuit(self, name, **kwargs): + """Execute a Circuit. Args: - name (str): name of the Qcircuit. - **kwargs (dict): arguments for the Qcircuit. + name (str): name of the Circuit. + **kwargs (dict): arguments for the Circuit. Returns: dict: json response. """ - url = self.get_url('qcircuit') + url = self.get_url('circuit') payload = { 'name': name, diff --git a/qiskit/providers/ibmq/circuits/__init__.py b/qiskit/providers/ibmq/circuits/__init__.py index 6f39ad46c..96dae257d 100644 --- a/qiskit/providers/ibmq/circuits/__init__.py +++ b/qiskit/providers/ibmq/circuits/__init__.py @@ -12,6 +12,6 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Module for interacting with QCircuits.""" +"""Module for interacting with Circuits.""" from .manager import CircuitsManager diff --git a/qiskit/providers/ibmq/circuits/exceptions.py b/qiskit/providers/ibmq/circuits/exceptions.py index 72b2ab03b..5c0de6605 100644 --- a/qiskit/providers/ibmq/circuits/exceptions.py +++ b/qiskit/providers/ibmq/circuits/exceptions.py @@ -12,37 +12,37 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Exceptions related to Qcircuits.""" +"""Exceptions related to Circuits.""" from qiskit.providers.ibmq.exceptions import IBMQError -QCIRCUIT_NOT_ALLOWED = 'Qcircuit support is not available yet in this account' -QCIRCUIT_SUBMIT_ERROR = 'Qcircuit could not be submitted: {}' -QCIRCUIT_RESULT_ERROR = 'Qcircuit result could not be returned: {}' +CIRCUIT_NOT_ALLOWED = 'Circuit support is not available yet in this account' +CIRCUIT_SUBMIT_ERROR = 'Circuit could not be submitted: {}' +CIRCUIT_RESULT_ERROR = 'Circuit result could not be returned: {}' -class QcircuitError(IBMQError): - """Generic Qcircuit exception.""" +class CircuitError(IBMQError): + """Generic Circuit exception.""" pass -class QcircuitAvailabilityError(QcircuitError): - """Error while accessing a Qcircuit.""" +class CircuitAvailabilityError(CircuitError): + """Error while accessing a Circuit.""" def __init__(self, message=''): - super().__init__(message or QCIRCUIT_NOT_ALLOWED) + super().__init__(message or CIRCUIT_NOT_ALLOWED) -class QcircuitSubmitError(QcircuitError): - """Error while submitting a Qcircuit.""" +class CircuitSubmitError(CircuitError): + """Error while submitting a Circuit.""" def __init__(self, message): - super().__init__(QCIRCUIT_SUBMIT_ERROR.format(message)) + super().__init__(CIRCUIT_SUBMIT_ERROR.format(message)) -class QcircuitResultError(QcircuitError): - """Error during the results of a Qcircuit.""" +class CircuitResultError(CircuitError): + """Error during the results of a Circuit.""" def __init__(self, message): - super().__init__(QCIRCUIT_RESULT_ERROR.format(message)) + super().__init__(CIRCUIT_RESULT_ERROR.format(message)) diff --git a/qiskit/providers/ibmq/circuits/manager.py b/qiskit/providers/ibmq/circuits/manager.py index 890ec9cde..922886172 100644 --- a/qiskit/providers/ibmq/circuits/manager.py +++ b/qiskit/providers/ibmq/circuits/manager.py @@ -12,7 +12,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Manager for interacting with QCircuits.""" +"""Manager for interacting with Circuits.""" from functools import wraps @@ -20,9 +20,9 @@ from qiskit.providers.ibmq.ibmqjob import IBMQJob from qiskit.providers.ibmq.api_v2.exceptions import RequestsApiError -from .exceptions import (QcircuitError, - QcircuitAvailabilityError, QcircuitResultError, - QcircuitSubmitError) +from .exceptions import (CircuitError, + CircuitAvailabilityError, CircuitResultError, + CircuitSubmitError) GRAPH_STATE = 'graph_state' @@ -31,12 +31,12 @@ def requires_api_connection(func): - """Decorator that ensures that a QCircuitsManager has a valid API.""" + """Decorator that ensures that a CircuitsManager has a valid API.""" @wraps(func) def wrapper(self, *args, **kwargs): if not self.client: - raise QcircuitAvailabilityError( - 'An account must be loaded in order to use QCircuits') + raise CircuitAvailabilityError( + 'An account must be loaded in order to use Circuits') return func(self, *args, **kwargs) @@ -44,28 +44,28 @@ def wrapper(self, *args, **kwargs): class CircuitsManager: - """Class that provides access to the different qcircuits.""" + """Class that provides access to the different Circuits.""" def __init__(self): self.client = None - def _call_qcircuit(self, name, **kwargs): - """Execute a Qcircuit. + def _call_circuit(self, name, **kwargs): + """Execute a Circuit. Args: - name (str): name of the Qcircuit. - **kwargs: parameters passed to the Qcircuit. + name (str): name of the Circuit. + **kwargs: parameters passed to the Circuit. Returns: Result: the result of executing the circuit. Raises: - QcircuitAvailabilityError: if Qcircuits are not available. - QcircuitSubmitError: if there was an error submitting the Qcircuit. - QcircuitResultError: if the result of the Qcircuit could not be + CircuitAvailabilityError: if Circuits are not available. + CircuitSubmitError: if there was an error submitting the Circuit. + CircuitResultError: if the result of the Circuit could not be returned. """ try: - response = self.client.qcircuit_run(name=name, **kwargs) + response = self.client.circuit_run(name=name, **kwargs) except RequestsApiError as ex: # Revise the original requests exception to intercept. response = ex.original_exception.response @@ -78,30 +78,30 @@ def _call_qcircuit(self, name, **kwargs): # Generic authorization or unavailable endpoint error. if response.status_code in (401, 404): - raise QcircuitAvailabilityError() from None + raise CircuitAvailabilityError() from None if response.status_code == 400: # Hub permission error. if body.get('error', {}).get('code') == 'HUB_NOT_FOUND': - raise QcircuitAvailabilityError() from None + raise CircuitAvailabilityError() from None # Generic error. if body.get('error', {}).get('code') == 'GENERIC_ERROR': - raise QcircuitAvailabilityError() from None + raise CircuitAvailabilityError() from None # Handle the rest of the exceptions as unexpected. - raise QcircuitSubmitError(str(ex)) + raise CircuitSubmitError(str(ex)) except Exception as ex: # Handle non-requests exception as unexpected. - raise QcircuitSubmitError(str(ex)) + raise CircuitSubmitError(str(ex)) # Extra check for IBMQConnector code path. if 'error' in response: if response['error'].get('code') == 'HUB_NOT_FOUND': - raise QcircuitAvailabilityError() from None - raise QcircuitSubmitError(str(response)) + raise CircuitAvailabilityError() from None + raise CircuitSubmitError(str(response)) - # Create a Job for the qcircuit. + # Create a Job for the circuit. try: job = IBMQJob(backend=None, job_id=response['id'], @@ -109,19 +109,19 @@ def _call_qcircuit(self, name, **kwargs): creation_date=response['creationDate'], api_status=response['status']) except Exception as ex: - raise QcircuitResultError(str(ex)) + raise CircuitResultError(str(ex)) # Wait for the job to complete, explicitly checking for errors. job._wait_for_completion() if job.status() is JobStatus.ERROR: - raise QcircuitResultError( + raise CircuitResultError( 'Job {} finished with an error'.format(job.job_id())) return job.result() @requires_api_connection def graph_state(self, number_of_qubits, adjacency_matrix, angles): - """Execute the graph state Qcircuit. + """Execute the graph state Circuit. This circuit implements graph state circuits that are measured in a product basis. Measurement angles can be chosen to measure graph state @@ -146,21 +146,21 @@ def graph_state(self, number_of_qubits, adjacency_matrix, angles): Result: the result of executing the circuit. Raises: - QcircuitError: if the parameters are not valid. + CircuitError: if the parameters are not valid. """ if 2 <= number_of_qubits <= 20: - raise QcircuitError('Invalid number_of_qubits') + raise CircuitError('Invalid number_of_qubits') if len(angles) != number_of_qubits*3: - raise QcircuitError('Invalid angles length') + raise CircuitError('Invalid angles length') - return self._call_qcircuit(name=GRAPH_STATE, - number_of_qubits=number_of_qubits, - adjacency_matrix=adjacency_matrix, - angles=angles) + return self._call_circuit(name=GRAPH_STATE, + number_of_qubits=number_of_qubits, + adjacency_matrix=adjacency_matrix, + angles=angles) @requires_api_connection def hardware_efficient(self, number_of_qubits, angles): - """Execute the hardware efficient Qcircuit. + """Execute the hardware efficient Circuit. This circuit implements the random lattice circuit across a user specified number of qubits and phase angles. @@ -176,20 +176,20 @@ def hardware_efficient(self, number_of_qubits, angles): Result: the result of executing the circuit. Raises: - QcircuitError: if the parameters are not valid. + CircuitError: if the parameters are not valid. """ if 4 <= number_of_qubits <= 20: - raise QcircuitError('Invalid number_of_qubits') + raise CircuitError('Invalid number_of_qubits') if len(angles) % 3*number_of_qubits != 0: - raise QcircuitError('Invalid angles length') + raise CircuitError('Invalid angles length') - return self._call_qcircuit(name=HARDWARE_EFFICIENT, - number_of_qubits=number_of_qubits, - angles=angles) + return self._call_circuit(name=HARDWARE_EFFICIENT, + number_of_qubits=number_of_qubits, + angles=angles) @requires_api_connection def random_uniform(self, number_of_qubits=None): - """Execute the random uniform Qcircuit. + """Execute the random uniform Circuit. This circuit implements hadamard gates across all available qubits on the device. @@ -205,4 +205,4 @@ def random_uniform(self, number_of_qubits=None): if number_of_qubits is not None: kwargs['number_of_qubits'] = number_of_qubits - return self._call_qcircuit(name=RANDOM_UNIFORM, **kwargs) + return self._call_circuit(name=RANDOM_UNIFORM, **kwargs) diff --git a/qiskit/providers/ibmq/ibmqprovider.py b/qiskit/providers/ibmq/ibmqprovider.py index f95450fdf..ae50c9c52 100644 --- a/qiskit/providers/ibmq/ibmqprovider.py +++ b/qiskit/providers/ibmq/ibmqprovider.py @@ -43,12 +43,12 @@ def __init__(self): # keys are tuples (hub, group, project), as the convention is that # that tuple uniquely identifies a set of credentials. self._accounts = OrderedDict() - self._qcircuits_manager = CircuitsManager() + self._circuits_manager = CircuitsManager() @property def circuits(self): - """Entry point for Qcircuit invocation.""" - return self._qcircuits_manager + """Entry point for Circuit invocation.""" + return self._circuits_manager def backends(self, name=None, filters=None, **kwargs): """Return all backends accessible via IBMQ provider, subject to optional filtering. @@ -218,10 +218,10 @@ def disable_accounts(self, **kwargs): credentials = Credentials(current_creds[creds].credentials.token, current_creds[creds].credentials.url) if self._credentials_match_filter(credentials, kwargs): - # Remove api from qcircuits manager if in use. + # Remove api from circuits manager if in use. if (self._accounts[credentials.unique_id()]._api == - self._qcircuits_manager.client): - self._qcircuits_manager.client = None + self._circuits_manager.client): + self._circuits_manager.client = None del self._accounts[credentials.unique_id()] disabled = True @@ -261,26 +261,26 @@ def _append_account(self, credentials): Returns: IBMQSingleProvider: new single-account provider. """ - update_qcircuits_manager = False - # Use the first account as the account for qcircuits. + update_circuits_manager = False + # Use the first account as the account for circuits. if not self._accounts: - update_qcircuits_manager = True + update_circuits_manager = True # Check if duplicated credentials are already in use. By convention, # we assume (hub, group, project) is always unique. if credentials.unique_id() in self._accounts.keys(): warnings.warn('Credentials are already in use.') - # Remove api from qcircuits manager if in use. + # Remove api from circuits manager if in use. if (self._accounts[credentials.unique_id()]._api == - self._qcircuits_manager.client): - update_qcircuits_manager = True + self._circuits_manager.client): + update_circuits_manager = True single_provider = IBMQSingleProvider(credentials, self) self._accounts[credentials.unique_id()] = single_provider - if update_qcircuits_manager: - self._qcircuits_manager.client = single_provider._api + if update_circuits_manager: + self._circuits_manager.client = single_provider._api return single_provider From 0cd70a79c47bf97b6fd8912d60ace084ab0e3bc6 Mon Sep 17 00:00:00 2001 From: "Diego M. Rodriguez" Date: Sun, 5 May 2019 13:58:32 +0200 Subject: [PATCH 13/13] Fix hardware_efficient angles docstring --- qiskit/providers/ibmq/circuits/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/ibmq/circuits/manager.py b/qiskit/providers/ibmq/circuits/manager.py index 922886172..42eec1045 100644 --- a/qiskit/providers/ibmq/circuits/manager.py +++ b/qiskit/providers/ibmq/circuits/manager.py @@ -168,7 +168,7 @@ def hardware_efficient(self, number_of_qubits, angles): Args: number_of_qubits (int): number of qubits to use, in the 4-20 range. angles (list): array of three phase angles (x/y/z) each from - 0 to 4*Pi, one set for each qubit of each layer of the lattice. + 0 to 2*Pi, one set for each qubit of each layer of the lattice. There should be 3 * number_of_qubits * desired lattice depth entries in the array.