qiskit-ibmq-provider-0.4.6/0000775000372000037200000000000013616666025016460 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/MANIFEST.in0000664000372000037200000000014413616666011020210 0ustar travistravis00000000000000include LICENSE.txt README.md include qiskit/providers/ibmq/VERSION.txt recursive-include test *.py qiskit-ibmq-provider-0.4.6/LICENSE.txt0000664000372000037200000002623013616666011020301 0ustar travistravis00000000000000 Copyright 2017 IBM and its contributors Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2017 IBM and its contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. qiskit-ibmq-provider-0.4.6/qiskit/0000775000372000037200000000000013616666025017764 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/0000775000372000037200000000000013616666025022001 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/0000775000372000037200000000000013616666025022731 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/ibmqfactory.py0000664000372000037200000003471713616666011025632 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """Factory and credentials manager for IBM Q Experience.""" import logging from typing import Dict, List, Union, Optional, Any from collections import OrderedDict from .accountprovider import AccountProvider from .api.clients import AuthClient, VersionClient from .credentials import Credentials, HubGroupProject, discover_credentials from .credentials.configrc import (read_credentials_from_qiskitrc, remove_credentials, store_credentials) from .credentials.updater import update_credentials from .exceptions import (IBMQAccountError, IBMQProviderError, IBMQAccountCredentialsNotFound, IBMQAccountCredentialsInvalidUrl, IBMQAccountCredentialsInvalidToken, IBMQAccountMultipleCredentialsFound) logger = logging.getLogger(__name__) QX_AUTH_URL = 'https://auth.quantum-computing.ibm.com/api' UPDATE_ACCOUNT_TEXT = "Please update your accounts and programs by following the " \ "instructions here: https://github.com/Qiskit/qiskit-ibmq-provider#" \ "updating-to-the-new-ibm-q-experience " class IBMQFactory: """Factory and credentials manager for IBM Q Experience.""" def __init__(self) -> None: self._credentials = None # type: Optional[Credentials] self._providers = OrderedDict() # type: Dict[HubGroupProject, AccountProvider] # Account management functions. def enable_account( self, token: str, url: str = QX_AUTH_URL, **kwargs: Any ) -> Optional[AccountProvider]: """Authenticate against IBM Q Experience for use during this session. Note: with version 0.4 of this qiskit-ibmq-provider package, use of the legacy Quantum Experience and Qconsole (also known as the IBM Q Experience v1) credentials is fully deprecated. Args: token: IBM Q Experience API token. url: URL for the IBM Q Experience authentication server. **kwargs: additional settings for the connection: * proxies (dict): proxy configuration. * verify (bool): verify the server's TLS certificate. Returns: the provider for the default open access project. Raises: IBMQAccountError: if an IBM Q Experience account is already in use. IBMQAccountCredentialsInvalidUrl: if the URL specified is not a valid IBM Q Experience authentication URL. """ # Check if an IBM Q Experience account is already in use. if self._credentials: raise IBMQAccountError('An IBM Q Experience account is already ' 'enabled.') # Check the version used by these credentials. credentials = Credentials(token, url, **kwargs) version_info = self._check_api_version(credentials) # Check the URL is a valid authentication URL. if not version_info['new_api'] or 'api-auth' not in version_info: raise IBMQAccountCredentialsInvalidUrl( 'The URL specified ({}) is not an IBM Q Experience ' 'authentication URL'.format(credentials.url)) # Initialize the providers. self._initialize_providers(credentials) # Prevent edge case where no hubs are available. providers = self.providers() if not providers: logger.warning('No Hub/Group/Projects could be found for this ' 'account.') return None return providers[0] def disable_account(self) -> None: """Disable the account in the current session. Raises: IBMQAccountCredentialsNotFound: if no account is in use in the session. """ if not self._credentials: raise IBMQAccountCredentialsNotFound('No account is in use for this session.') self._credentials = None self._providers = OrderedDict() def load_account(self) -> Optional[AccountProvider]: """Authenticate against IBM Q Experience from stored credentials. Returns: the provider for the default open access project. Raises: IBMQAccountCredentialsNotFound: if no IBM Q Experience credentials can be found. IBMQAccountMultipleCredentialsFound: if multiple IBM Q Experience credentials found. IBMQAccountCredentialsInvalidUrl: if invalid IBM Q Experience credentials found. """ # Check for valid credentials. credentials_list = list(discover_credentials().values()) if not credentials_list: raise IBMQAccountCredentialsNotFound( 'No IBM Q Experience credentials found on disk.') if len(credentials_list) > 1: raise IBMQAccountMultipleCredentialsFound( 'Multiple IBM Q Experience credentials found. ' + UPDATE_ACCOUNT_TEXT) credentials = credentials_list[0] # Explicitly check via an API call, to allow environment auth URLs # contain API 2 URL (but not auth) slipping through. version_info = self._check_api_version(credentials) # Check the URL is a valid authentication URL. if not version_info['new_api'] or 'api-auth' not in version_info: raise IBMQAccountCredentialsInvalidUrl( 'Invalid IBM Q Experience credentials found. ' + UPDATE_ACCOUNT_TEXT) # Initialize the providers. if self._credentials: # For convention, emit a warning instead of raising. logger.warning('Credentials are already in use. The existing ' 'account in the session will be replaced.') self.disable_account() self._initialize_providers(credentials) # Prevent edge case where no hubs are available. providers = self.providers() if not providers: logger.warning('No Hub/Group/Projects could be found for this ' 'account.') return None return providers[0] @staticmethod def save_account( token: str, url: str = QX_AUTH_URL, overwrite: bool = False, **kwargs: Any ) -> None: """Save the account to disk for future use. Args: token: IBM Q Experience API token. url: URL for the IBM Q Experience authentication server. overwrite: overwrite existing credentials. **kwargs: * proxies (dict): Proxy configuration for the API. * verify (bool): If False, ignores SSL certificates errors Raises: IBMQAccountCredentialsInvalidUrl: if the URL is not a valid IBM Q Experience authentication URL. IBMQAccountCredentialsInvalidToken: if the token is not a valid IBM Q Experience token. """ if url != QX_AUTH_URL: raise IBMQAccountCredentialsInvalidUrl( 'Invalid IBM Q Experience credentials found. ' + UPDATE_ACCOUNT_TEXT) if not token or not isinstance(token, str): raise IBMQAccountCredentialsInvalidToken( 'Invalid token found: "%s" %s' % (token, type(token))) credentials = Credentials(token, url, **kwargs) store_credentials(credentials, overwrite=overwrite) @staticmethod def delete_account() -> None: """Delete the saved account from disk. Raises: IBMQAccountCredentialsNotFound: if no valid IBM Q Experience credentials found. IBMQAccountMultipleCredentialsFound: if multiple IBM Q Experience credentials found. IBMQAccountCredentialsInvalidUrl: if invalid IBM Q Experience credentials found. """ stored_credentials = read_credentials_from_qiskitrc() if not stored_credentials: raise IBMQAccountCredentialsNotFound('No credentials found.') if len(stored_credentials) != 1: raise IBMQAccountMultipleCredentialsFound( 'Multiple credentials found. ' + UPDATE_ACCOUNT_TEXT) credentials = list(stored_credentials.values())[0] if credentials.url != QX_AUTH_URL: raise IBMQAccountCredentialsInvalidUrl( 'Invalid IBM Q Experience credentials found. ' + UPDATE_ACCOUNT_TEXT) remove_credentials(credentials) @staticmethod def stored_account() -> Dict[str, str]: """List the account stored on disk. Returns: dictionary with information about the account stored on disk. Raises: IBMQAccountMultipleCredentialsFound: if multiple IBM Q Experience credentials found. IBMQAccountCredentialsInvalidUrl: if invalid IBM Q Experience credentials found. """ stored_credentials = read_credentials_from_qiskitrc() if not stored_credentials: return {} if len(stored_credentials) > 1: raise IBMQAccountMultipleCredentialsFound( 'Multiple credentials found. ' + UPDATE_ACCOUNT_TEXT) credentials = list(stored_credentials.values())[0] if credentials.url != QX_AUTH_URL: raise IBMQAccountCredentialsInvalidUrl( 'Invalid IBM Q Experience credentials found. ' + UPDATE_ACCOUNT_TEXT) return { 'token': credentials.token, 'url': credentials.url } def active_account(self) -> Optional[Dict[str, str]]: """List the IBM Q Experience account currently in the session. Returns: information about the account currently in the session. """ if not self._credentials: # Return None instead of raising, maintaining the same behavior # of the classic active_accounts() method. return None return { 'token': self._credentials.token, 'url': self._credentials.url, } @staticmethod def update_account(force: bool = False) -> Optional[Credentials]: """Interactive helper from migrating stored credentials to IBM Q Experience v2. Args: force: if `True`, disable interactive prompts and perform the changes. Returns: if the updating is possible, credentials for the API version 2; and `None` otherwise. """ return update_credentials(force) # Provider management functions. def providers( self, hub: Optional[str] = None, group: Optional[str] = None, project: Optional[str] = None ) -> List[AccountProvider]: """Return a list of providers with optional filtering. Args: hub: name of the hub. group: name of the group. project: name of the project. Returns: list of providers that match the specified criteria. """ filters = [] if hub: filters.append(lambda hgp: hgp.hub == hub) if group: filters.append(lambda hgp: hgp.group == group) if project: filters.append(lambda hgp: hgp.project == project) providers = [provider for key, provider in self._providers.items() if all(f(key) for f in filters)] # type: ignore[arg-type] return providers def get_provider( self, hub: Optional[str] = None, group: Optional[str] = None, project: Optional[str] = None ) -> AccountProvider: """Return a provider for a single hub/group/project combination. Returns: provider that match the specified criteria. Raises: IBMQProviderError: if no provider matches the specified criteria, or more than one provider match the specified criteria. """ providers = self.providers(hub, group, project) if not providers: raise IBMQProviderError('No provider matching the criteria') if len(providers) > 1: raise IBMQProviderError('More than one provider matching the ' 'criteria') return providers[0] # Private functions. @staticmethod def _check_api_version(credentials: Credentials) -> Dict[str, Union[bool, str]]: """Check the version of the API in a set of credentials. Returns: dictionary with version information. """ version_finder = VersionClient(credentials.base_url, **credentials.connection_parameters()) return version_finder.version() def _initialize_providers(self, credentials: Credentials) -> None: """Authenticate against IBM Q Experience and populate the providers. Args: credentials: credentials for IBM Q Experience. """ auth_client = AuthClient(credentials.token, credentials.base_url, **credentials.connection_parameters()) service_urls = auth_client.current_service_urls() user_hubs = auth_client.user_hubs() self._credentials = credentials for hub_info in user_hubs: # Build credentials. provider_credentials = Credentials( credentials.token, url=service_urls['http'], websockets_url=service_urls['ws'], proxies=credentials.proxies, verify=credentials.verify, **hub_info, ) # Build the provider. try: provider = AccountProvider(provider_credentials, auth_client.current_access_token()) self._providers[provider_credentials.unique_id()] = provider except Exception as ex: # pylint: disable=broad-except # Catch-all for errors instantiating the provider. logger.warning('Unable to instantiate provider for %s: %s', hub_info, ex) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/VERSION.txt0000664000372000037200000000000613616666011024606 0ustar travistravis000000000000000.4.6 qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/apiconstants.py0000664000372000037200000000354513616666011026013 0ustar travistravis00000000000000# -*- 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. """Values used by the API for different values.""" import enum class ApiJobStatus(enum.Enum): """Possible values used by the API for a job status. The enum names represent the strings returned by the API verbatim in several endpoints (`status()`, websocket information, etc). The general flow is: `CREATING -> CREATED -> VALIDATING -> VALIDATED -> RUNNING -> COMPLETED` """ CREATING = 'CREATING' CREATED = 'CREATED' VALIDATING = 'VALIDATING' VALIDATED = 'VALIDATED' RUNNING = 'RUNNING' COMPLETED = 'COMPLETED' PENDING_IN_QUEUE = 'PENDING_IN_QUEUE' QUEUED = 'QUEUED' CANCELLED = 'CANCELLED' ERROR_CREATING_JOB = 'ERROR_CREATING_JOB' ERROR_VALIDATING_JOB = 'ERROR_VALIDATING_JOB' ERROR_RUNNING_JOB = 'ERROR_RUNNING_JOB' API_JOB_FINAL_STATES = ( ApiJobStatus.COMPLETED, ApiJobStatus.CANCELLED, ApiJobStatus.ERROR_CREATING_JOB, ApiJobStatus.ERROR_VALIDATING_JOB, ApiJobStatus.ERROR_RUNNING_JOB ) class ApiJobKind(enum.Enum): """Possible values used by the API for a job kind.""" QOBJECT = 'q-object' QOBJECT_STORAGE = 'q-object-external-storage' CIRCUIT = 'q-circuit' class ApiJobShareLevel(enum.Enum): """Possible values used by the API for job share levels.""" GLOBAL = 'global' HUB = 'hub' GROUP = 'group' PROJECT = 'project' NONE = 'none' qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/job/0000775000372000037200000000000013616666025023503 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/job/ibmqjob.py0000664000372000037200000007056213616666011025505 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2020. # # 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. """IBMQJob module This module is used for creating a job objects for the IBM Q Experience. """ import logging from typing import Dict, Optional, Tuple, Any, List, Callable import warnings from datetime import datetime from collections import deque from concurrent import futures from threading import Event from marshmallow import ValidationError from qiskit.providers import (BaseJob, # type: ignore[attr-defined] BaseBackend) from qiskit.providers.jobstatus import JOB_FINAL_STATES, JobStatus from qiskit.providers.models import BackendProperties from qiskit.qobj import Qobj from qiskit.result import Result from qiskit.validation import BaseModel, ModelValidationError, bind_schema from ..apiconstants import ApiJobStatus, ApiJobKind from ..api.clients import AccountClient from ..api.exceptions import ApiError, UserTimeoutExceededError from .exceptions import (IBMQJobApiError, IBMQJobFailureError, IBMQJobTimeoutError, IBMQJobInvalidStateError) from .queueinfo import QueueInfo from .schema import JobResponseSchema from .utils import build_error_report, api_status_to_job_status, api_to_job_error logger = logging.getLogger(__name__) @bind_schema(JobResponseSchema) class IBMQJob(BaseModel, BaseJob): """Representation of a job that will be execute on a IBMQ backend. Represent a job that is or has been executed on an IBMQ simulator or real device. New jobs are intended to be created by calling ``run()`` on a particular backend. If the job was successfully submitted, you can inspect the job's status by using ``status()``. Status can be one of ``JobStatus`` members:: from qiskit.providers.jobstatus import JobStatus job = IBMQBackend.run(...) try: job_status = job.status() # It will query the backend API. if job_status is JobStatus.RUNNING: print('The job is still running') except IBMQJobApiError as ex: print("Something wrong happened!: {}".format(ex)) A call to ``status()`` can raise if something happens at the server level that prevents Qiskit from determining the status of the job. An example of this is a temporary connection lose or a network failure. The ``status()`` method is an example of non-blocking API. The ``result()`` method is an example of blocking API: job = IBMQBackend.run(...) try: job_result = job.result() # It will block until finishing. print('The job finished with result {}'.format(job_result)) except JobError as ex: print("Something wrong happened!: {}".format(ex)) Many of the ``IBMQJob`` methods can raise ``IBMQJobApiError`` if unexpected failures happened at the server level. Job information retrieved from the API server is attached to the ``IBMQJob`` instance as attributes. Given that Qiskit and the API server can be updated independently, some of these attributes might be deprecated or experimental. Supported attributes can be retrieved via methods. For example, you can use ``IBMQJob.creation_date()`` to retrieve the job creation date, which is a supported attribute. Note: When querying the server for getting the job information, two kinds of errors are possible. The most severe is the one preventing Qiskit from getting a response from the server. This can be caused by a network failure or a temporary system break. In these cases, the job method will raise. If Qiskit successfully retrieves the status of a job, it could be it finished with errors. In that case, ``status()`` will simply return ``JobStatus.ERROR`` and you can call ``error_message()`` to get more info. """ _executor = futures.ThreadPoolExecutor() """Threads used for asynchronous processing.""" def __init__(self, _backend: BaseBackend, api: AccountClient, _job_id: str, _creation_date: datetime, _api_status: ApiJobStatus, **kwargs: Any) -> None: """IBMQJob init function. Args: _backend: the backend instance used to run this job. api: object for connecting to the API. _job_id: job ID of this job. _creation_date: job creation date. _api_status: API job status. kwargs: additional job attributes, that will be added as instance members. """ # pylint: disable=redefined-builtin BaseModel.__init__(self, _backend=_backend, _job_id=_job_id, _creation_date=_creation_date, _api_status=_api_status, **kwargs) BaseJob.__init__(self, self.backend(), self.job_id()) # Model attributes. self._api = api self._use_object_storage = (self.kind == ApiJobKind.QOBJECT_STORAGE) self._queue_info = None # type: Optional[QueueInfo] self._status, self._queue_info = self._get_status_position( _api_status, kwargs.pop('info_queue', None)) # Properties used for caching. self._cancelled = False self._job_error_msg = None # type: Optional[str] def qobj(self) -> Optional[Qobj]: """Return the Qobj for this job. Note that this method might involve querying the API for results if the Job has been created in a previous Qiskit session. Returns: the Qobj for this job, or None if the job does not have a Qobj. Raises: IBMQJobApiError: if there was some unexpected failure in the server. """ if not self.kind: return None # pylint: disable=access-member-before-definition,attribute-defined-outside-init if not self._qobj: # type: ignore[has-type] self._wait_for_completion() with api_to_job_error(): qobj = self._api.job_download_qobj( self.job_id(), self._use_object_storage) self._qobj = Qobj.from_dict(qobj) return self._qobj def properties(self) -> Optional[BackendProperties]: """Return the backend properties for this job. Returns: the backend properties used for this job, or None if properties are not available. Raises: IBMQJobApiError: if there was some unexpected failure in the server. """ with api_to_job_error(): properties = self._api.job_properties(job_id=self.job_id()) if not properties: return None return BackendProperties.from_dict(properties) def result( self, timeout: Optional[float] = None, wait: float = 5, partial: bool = False, refresh: bool = False ) -> Result: """Return the result of the job. Note: Some IBMQ job results can be read only once. A second attempt to query the API for the job will fail, as the job is "consumed". The first call to this method in an ``IBMQJob`` instance will query the API and consume the job if it finished successfully (otherwise it will raise a ``JobError`` exception without consuming the job). Subsequent calls to that instance's method will also return the results, since they are cached. However, attempting to retrieve the results again in another instance or session might fail due to the job having been consumed. When `partial=True`, the result method returns a `Result` object containing partial results. If partial results are returned, precaution should be taken when accessing individual experiments, as doing so might cause an exception. Verifying whether some experiments of a job failed can be done by checking the boolean attribute `Result.success`. For example: If there is a job with two experiments (where one fails), getting the counts of the unsuccessful experiment would raise an exception since there are no counts to return for it: i.e. try: counts = result.get_counts("failed_experiment") except QiskitError: print("Experiment failed!") Args: timeout: number of seconds to wait for job. wait: time in seconds between queries to IBM Q server. Default: 5. partial: if true attempts to return partial results for the job. Default: False. refresh: if true, query the API for the result again. Otherwise return the cached value. Default: False. Returns: Result object. Raises: IBMQJobInvalidStateError: if the job was cancelled. IBMQJobFailureError: If the job failed. IBMQJobApiError: If there was some unexpected failure in the server. """ # pylint: disable=arguments-differ # pylint: disable=access-member-before-definition,attribute-defined-outside-init if not self._wait_for_completion(timeout=timeout, wait=wait, required_status=(JobStatus.DONE,)): if self._status is JobStatus.CANCELLED: raise IBMQJobInvalidStateError('Unable to retrieve job result. Job was cancelled.') if self._status is JobStatus.ERROR and not partial: raise IBMQJobFailureError('Unable to retrieve job result. Job has failed. ' 'Use job.error_message() to get more details.') return self._retrieve_result(refresh=refresh) def cancel(self) -> bool: """Attempt to cancel a job. Returns: True if job can be cancelled, else False. Note this operation might not be possible depending on the environment. Raises: IBMQJobApiError: if there was some unexpected failure in the server. """ try: response = self._api.job_cancel(self.job_id()) self._cancelled = 'error' not in response and response.get('cancelled', False) return self._cancelled except ApiError as error: self._cancelled = False raise IBMQJobApiError('Error cancelling job: %s' % error) def status(self) -> JobStatus: """Query the API to update the status. Note: This method is not designed to be invoked repeatedly in a loop for an extended period of time. Doing so may cause an exception. Use `wait_for_final_state()` if you want to wait for the job to finish. Returns: The status of the job, once updated. Raises: IBMQJobApiError: if there was some unexpected failure in the server. """ if self._status in JOB_FINAL_STATES: return self._status with api_to_job_error(): api_response = self._api.job_status(self.job_id()) self._status, self._queue_info = self._get_status_position( ApiJobStatus(api_response['status']), api_response.get('infoQueue', None)) # Get all job attributes if the job is done. if self._status in JOB_FINAL_STATES: self.refresh() return self._status def done(self) -> bool: """Return whether the job has successfully run. Returns: True if job status is done, else false. """ return self._is_job_status(JobStatus.DONE) def running(self) -> bool: """Return whether the job is actively running. Returns: True if job status is running, else false. """ return self._is_job_status(JobStatus.RUNNING) def cancelled(self) -> bool: """Return whether the job has been cancelled. Returns: True if job status is cancelled, else false. """ return self._is_job_status(JobStatus.CANCELLED) def _is_job_status(self, job_status: JobStatus) -> bool: """Return whether the current job status matches the desired one. Args: job_status: the job status to check against. Returns: True if the current job status matches the desired one, else false. """ return self.status() == job_status def error_message(self) -> Optional[str]: """Provide details about the reason of failure. Note: Some IBMQ job results can be read only once. A second attempt to query the API for the job will fail, as the job is "consumed". The first call to this method in an ``IBMQJob`` instance will query the API and consume the job if it failed at some point (otherwise it will return ``None``). Subsequent calls to that instance's method will also return the failure details, since they are cached. However, attempting to retrieve the error details again in another instance or session might fail due to the job having been consumed. Returns: An error report if the job failed or ``None`` otherwise. """ # pylint: disable=attribute-defined-outside-init if not self._wait_for_completion(required_status=(JobStatus.ERROR,)): return None if not self._job_error_msg: # First try getting error messages from the result. try: self._retrieve_result() except IBMQJobFailureError: pass if not self._job_error_msg: # Then try refreshing the job if not self._error: self.refresh() if self._error: self._job_error_msg = self._format_message_from_error( self._error.__dict__) elif self._api_status: self._job_error_msg = self._api_status.value else: self._job_error_msg = "Unknown error." return self._job_error_msg def queue_position(self, refresh: bool = False) -> Optional[int]: """Return the position in the server queue for the provider. Note: The position returned is within the scope of the account provider and may differ from the global queue position for the device. Args: refresh: if True, query the API and return the latest value. Otherwise return the cached value. Default: False. Returns: Position in the queue or ``None`` if position is unknown or not applicable. """ if refresh: # Get latest position self.status() if self._queue_info: return self._queue_info.position return None def queue_info(self) -> Optional[QueueInfo]: """Return queue information for this job. The queue information may include queue position, estimated start and end time, and dynamic priorities for the hub/group/project. Note: Even if the job is queued, some of its queue information may not be immediately available. Returns: An QueueInfo instance that contains queue information for this job, or ``None`` if queue information is unknown or not applicable. """ # Get latest queue information. self.status() # Return queue information only if it has any useful information. if self._queue_info and any( value is not None for attr, value in self._queue_info.__dict__.items() if not attr.startswith('_') and attr != 'job_id'): return self._queue_info return None def creation_date(self) -> str: """Return creation date. Returns: Job creation date. """ return self._creation_date.strftime('%Y-%m-%dT%H:%M:%S.%fZ') def job_id(self) -> str: """Return the job ID assigned by the API. Returns: the job ID. """ return self._job_id def name(self) -> Optional[str]: """Return the name assigned to this job. Returns: the job name or ``None`` if no name was assigned to the job. """ return self._name def tags(self) -> List[str]: """Return the tags assigned to this job. Returns: Tags assigned to this job. """ return self._tags.copy() def time_per_step(self) -> Optional[Dict]: """Return the date and time information on each step of the job processing. Returns: a dictionary containing the date and time information on each step of the job processing. The keys of the dictionary are the names of the steps, and the values are the date and time information. ``None`` is returned if the information is not yet available. """ if not self._time_per_step or self._status not in JOB_FINAL_STATES: self.refresh() return self._time_per_step def submit(self) -> None: """Submit job to IBM-Q. Note: This function is deprecated, please use ``IBMQBackend.run()`` to submit a job. Events: The job has started. Raises: IBMQJobApiError: if there was some unexpected failure in the server. IBMQJobInvalidStateError: If the job has already been submitted. """ if self.job_id() is not None: raise IBMQJobInvalidStateError("We have already submitted the job!") warnings.warn("job.submit() is deprecated. Please use " "IBMQBackend.run() to submit a job.", DeprecationWarning, stacklevel=2) def refresh(self) -> None: """Obtain the latest job information from the API.""" with api_to_job_error(): api_response = self._api.job_get(self.job_id()) saved_model_cls = JobResponseSchema.model_cls try: # Load response into a dictionary JobResponseSchema.model_cls = dict data = self.schema.load(api_response) BaseModel.__init__(self, **data) # Model attributes. self._use_object_storage = (self.kind == ApiJobKind.QOBJECT_STORAGE) self._status, self._queue_info = self._get_status_position( data.pop('_api_status'), data.pop('info_queue', None)) except ValidationError as ex: raise IBMQJobApiError("Unexpected return value received from the server.") from ex finally: JobResponseSchema.model_cls = saved_model_cls def to_dict(self) -> None: """Serialize the model into a Python dict of simple types.""" warnings.warn("IBMQJob.to_dict() is not supported and may not work properly.", stacklevel=2) return BaseModel.to_dict(self) def wait_for_final_state( self, timeout: Optional[float] = None, wait: float = 5, callback: Callable = None ) -> None: """Wait until the job progresses to a final state such as DONE or ERROR. Args: timeout: seconds to wait for the job. If ``None``, wait indefinitely. Default: None. wait: seconds to wait between queries. Default: 5. callback: callback function invoked after each querying iteration. Default: None. The following positional arguments are provided to the callback function: * job_id: job ID * job_status: status of the job from the last query * job: this IBMQJob instance In addition, the following keyword arguments are also provided: * queue_info: A ``QueueInfo`` instance with job queue information, or ``None`` if queue information is unknown or not applicable. You can use the ``to_dict()`` method to convert the ``QueueInfo`` instance to a dictionary, if desired. Raises: IBMQJobTimeoutError: if the job does not reach a final state before the specified timeout. """ exit_event = Event() status_deque = deque(maxlen=1) # type: deque future = None if callback: future = self._executor.submit(self._status_callback, status_deque=status_deque, exit_event=exit_event, callback=callback, wait=wait) try: self._wait_for_completion(timeout=timeout, wait=wait, status_deque=status_deque) finally: if future: exit_event.set() future.result() def _wait_for_completion( self, timeout: Optional[float] = None, wait: float = 5, required_status: Tuple[JobStatus] = JOB_FINAL_STATES, status_deque: Optional[deque] = None ) -> bool: """Wait until the job progress to a final state such as DONE or ERROR. Args: timeout: seconds to wait for job. If None, wait indefinitely. Default: None. wait: seconds between queries. Default: 5. required_status: the final job status required. Default: ``JOB_FINAL_STATES``. status_deque: deque used to share the latest status. Default: None. Returns: True if the final job status matches one of the required states. Raises: IBMQJobTimeoutError: if the job does not return results before a specified timeout. """ if self._status in JOB_FINAL_STATES: return self._status in required_status with api_to_job_error(): try: status_response = self._api.job_final_status( self.job_id(), timeout=timeout, wait=wait, status_deque=status_deque) except UserTimeoutExceededError: raise IBMQJobTimeoutError( 'Timeout while waiting for job {}'.format(self._job_id)) self._status, self._queue_info = self._get_status_position( ApiJobStatus(status_response['status']), status_response.get('infoQueue', None)) # Get all job attributes if the job is done. if self._status in JOB_FINAL_STATES: self.refresh() return self._status in required_status def _retrieve_result(self, refresh: bool = False) -> Result: """Retrieve the job result response. Args: refresh: if true, query the API for the result again. Otherwise return the cached value. Default: False. Returns: The job result. Raises: IBMQJobApiError: If there was some unexpected failure in the server. IBMQJobFailureError: If the job failed and partial result could not be retrieved. IBMQJobInvalidStateError: If result is in an unsupported format. """ # pylint: disable=access-member-before-definition,attribute-defined-outside-init result_response = None if not self._result or refresh: # type: ignore[has-type] try: result_response = self._api.job_result(self.job_id(), self._use_object_storage) self._result = Result.from_dict(result_response) except (ModelValidationError, ApiError) as err: if self._status is JobStatus.ERROR: raise IBMQJobFailureError('Unable to retrieve job result. Job has failed. ' 'Use job.error_message() to get more details.') if not self.kind: raise IBMQJobInvalidStateError('Job result is in an unsupported format.') raise IBMQJobApiError(str(err)) finally: # In case partial results are returned or job failure, an error message is cached. if result_response: self._check_for_error_message(result_response) if self._status is JobStatus.ERROR and not self._result.results: raise IBMQJobFailureError('Unable to retrieve job result. Job has failed. ' 'Use job.error_message() to get more details.') return self._result def _check_for_error_message(self, result_response: Dict[str, Any]) -> None: """Retrieves the error message from the result response. Args: result_response: Dictionary of the result response. """ if result_response.get('results', None): # If individual errors given self._job_error_msg = build_error_report(result_response['results']) elif 'error' in result_response: self._job_error_msg = self._format_message_from_error(result_response['error']) def _format_message_from_error(self, error: Dict) -> str: """Format message from the error field. Args: The error field. Returns: A formatted error message. Raises: IBMQJobApiError: If there was some unexpected failure in the server. """ try: return "{}. Error code: {}.".format(error['message'], error['code']) except KeyError: raise IBMQJobApiError('Failed to get job error message. Invalid error data received: {}' .format(error)) def _status_callback( self, status_deque: deque, exit_event: Event, callback: Callable, wait: float ) -> None: """Invoke the callback function with the latest job status. Args: status_deque: Deque containing the latest status. exit_event: Event used to notify this thread to quit. callback: Callback function to invoke. wait: Time between each callback function call. """ while not exit_event.is_set(): exit_event.wait(wait) try: status_response = status_deque.pop() except IndexError: continue try: status, queue_info = self._get_status_position( ApiJobStatus(status_response['status']), status_response.get('infoQueue', None)) except IBMQJobApiError as ex: logger.warning("Unexpected error when getting job status: %s", ex) continue callback(self.job_id(), status, self, queue_info=queue_info) def _get_status_position( self, api_status: ApiJobStatus, api_info_queue: Optional[Dict] = None ) -> Tuple[JobStatus, Optional[QueueInfo]]: """Return the corresponding job status for the input API job status. Args: api_status: API job status api_info_queue: job queue information from the API response. Returns: A tuple of job status and queue information (``None`` if not available). Raises: IBMQJobApiError: if unexpected return value received from the server. """ queue_info = None try: status = api_status_to_job_status(api_status) if api_status is ApiJobStatus.RUNNING and api_info_queue: api_info_queue['job_id'] = self.job_id() # job_id is used for QueueInfo.format(). queue_info = QueueInfo.from_dict(api_info_queue) if queue_info._status == ApiJobStatus.PENDING_IN_QUEUE.value: status = JobStatus.QUEUED except (KeyError, ValidationError) as ex: raise IBMQJobApiError("Unexpected return value received from the server.") from ex if status is not JobStatus.QUEUED: queue_info = None return status, queue_info qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/job/schema.py0000664000372000037200000000726513616666011025322 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """Schemas for job.""" from marshmallow import pre_load from qiskit.validation import BaseSchema from qiskit.validation.fields import Dict, String, Nested, Integer, Boolean, DateTime, List from qiskit.qobj.qobj import QobjSchema from qiskit.result.models import ResultSchema from qiskit.providers.ibmq.utils.fields import Enum, map_field_names from qiskit.providers.ibmq.apiconstants import ApiJobKind, ApiJobStatus # Mapping between 'API job field': 'IBMQJob attribute', for solving name # clashes. FIELDS_MAP = { 'id': '_job_id', 'status': '_api_status', 'backend': '_backend_info', 'creationDate': '_creation_date', 'qObject': '_qobj', 'qObjectResult': '_result', 'error': '_error', 'name': '_name', 'timePerStep': '_time_per_step', 'shots': '_api_shots', 'tags': '_tags' } # Helper schemas. class JobResponseBackendSchema(BaseSchema): """Nested schema for the backend field in JobResponseSchema.""" # Required properties name = String(required=True) class JobResponseErrorSchema(BaseSchema): """Nested schema for the error field in JobResponseSchema.""" # Required properties code = Integer(required=True) message = String(required=True) # Endpoint schemas. class JobResponseSchema(BaseSchema): """Schema for IBMQJob. Schema for an `IBMQJob`. The following conventions are in use in order to provide enough flexibility in regards to attributes: * the "Required properties" reflect attributes that will always be present in the model. * the "Optional properties with a default value" reflect attributes that are always present in the model, but might contain uninitialized values depending on the state of the job. * some properties are prepended by underscore due to name clashes and extra constraints in the IBMQJob class (for example, existing IBMQJob methods that have the same name as a response field). The schema is used for GET Jobs, GET Jobs/{id}, and POST Jobs responses. """ # pylint: disable=invalid-name # Required properties. _creation_date = DateTime(required=True) _job_id = String(required=True) _api_status = Enum(required=True, enum_cls=ApiJobStatus) # Optional properties with a default value. kind = Enum(enum_cls=ApiJobKind, missing=None) _name = String(missing=None) _time_per_step = Dict(keys=String, values=String, missing=None) _result = Nested(ResultSchema, missing=None) _qobj = Nested(QobjSchema, missing=None) _error = Nested(JobResponseErrorSchema, missing=None) _tags = List(String, missing=[]) # Optional properties _backend_info = Nested(JobResponseBackendSchema) allow_object_storage = Boolean() @pre_load def preprocess_field_names(self, data, **_): # type: ignore """Pre-process the job response fields. Rename selected fields of the job response due to name clashes, and convert from camel-case the rest of the fields. TODO: when updating to terra 0.10, check if changes related to marshmallow 3 allow to use directly `data_key`, as in 0.9 terra duplicates the unknown keys. """ return map_field_names(FIELDS_MAP, data) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/job/exceptions.py0000664000372000037200000000233313616666011026232 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 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 IBMQJob.""" from qiskit.providers.exceptions import JobError, JobTimeoutError from ..exceptions import IBMQError class IBMQJobError(JobError, IBMQError): """Base class for job errors raised by the IBMQ provider module.""" pass class IBMQJobApiError(IBMQJobError): """Error that occurs unexpectedly when querying the API.""" pass class IBMQJobFailureError(IBMQJobError): """Error that occurs because the job failed.""" pass class IBMQJobInvalidStateError(IBMQJobError): """Error that occurs because a job is not in a state for the operation.""" pass class IBMQJobTimeoutError(JobTimeoutError, IBMQJobError): """Error raised when a job operation times out.""" pass qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/job/utils.py0000664000372000037200000000463613616666011025221 0ustar travistravis00000000000000# -*- 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. """Utilities for working with IBM Q Jobs.""" from typing import Dict, List, Generator, Any from contextlib import contextmanager from qiskit.providers.jobstatus import JobStatus from ..apiconstants import ApiJobStatus from ..api.exceptions import ApiError from .exceptions import IBMQJobApiError API_TO_JOB_STATUS = { ApiJobStatus.CREATING: JobStatus.INITIALIZING, ApiJobStatus.CREATED: JobStatus.INITIALIZING, ApiJobStatus.VALIDATING: JobStatus.VALIDATING, ApiJobStatus.VALIDATED: JobStatus.VALIDATING, ApiJobStatus.RUNNING: JobStatus.RUNNING, ApiJobStatus.PENDING_IN_QUEUE: JobStatus.QUEUED, ApiJobStatus.QUEUED: JobStatus.QUEUED, ApiJobStatus.COMPLETED: JobStatus.DONE, ApiJobStatus.CANCELLED: JobStatus.CANCELLED, ApiJobStatus.ERROR_CREATING_JOB: JobStatus.ERROR, ApiJobStatus.ERROR_VALIDATING_JOB: JobStatus.ERROR, ApiJobStatus.ERROR_RUNNING_JOB: JobStatus.ERROR } def build_error_report(results: List[Dict[str, Any]]) -> str: """Build an user-friendly error report for a failed job. Args: results: result section of the job response. Returns: the error report. """ error_list = [] for index, result in enumerate(results): if not result['success']: error_list.append('Experiment {}: {}'.format(index, result['status'])) error_report = 'The following experiments failed:\n{}'.format('\n'.join(error_list)) return error_report def api_status_to_job_status(api_status: ApiJobStatus) -> JobStatus: """Return the corresponding job status for the input API job status. Args: api_status: API job status Returns: job status """ return API_TO_JOB_STATUS[api_status] @contextmanager def api_to_job_error() -> Generator[None, None, None]: """Convert an ApiError to an IBMQJobApiError.""" try: yield except ApiError as api_err: raise IBMQJobApiError(str(api_err)) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/job/queueinfo.py0000664000372000037200000001233413616666011026053 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2020. # # 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. """Queue information related to a job.""" from typing import Any, Optional from datetime import datetime import arrow from qiskit.validation import BaseModel, bind_schema from ..api.rest.validation import InfoQueueResponseSchema from ..apiconstants import ApiJobStatus from .utils import api_status_to_job_status @bind_schema(InfoQueueResponseSchema) class QueueInfo(BaseModel): """Queue information related to a job.""" def __init__( self, position: Optional[int], _status: Optional[str], estimated_start_time: Optional[datetime], estimated_complete_time: Optional[datetime], hub_priority: Optional[float], group_priority: Optional[float], project_priority: Optional[float], job_id: Optional[str] = None, **kwargs: Any ) -> None: """Creates a new QueueInfo instance. Args: position: Position in the queue. _status: Job status. estimated_start_time: Estimated start time for the job, in UTC. estimated_complete_time: Estimated complete time for the job, in UTC. hub_priority: Dynamic priority for the hub. group_priority: Dynamic priority for the group. project_priority: Dynamic priority for the project. job_id: The ID of the job. kwargs: additional attributes that will be added as instance members. """ self.position = position self._status = _status self.estimated_start_time = estimated_start_time self.estimated_complete_time = estimated_complete_time self.hub_priority = hub_priority self.group_priority = group_priority self.project_priority = project_priority self.job_id = job_id super().__init__(**kwargs) def __repr__(self) -> str: """Return the official string representation of QueueInfo. Returns: a string representation of QueueInfo. """ status = api_status_to_job_status(ApiJobStatus(self._status)).value \ if self._status else self._get_value(self._status) estimated_start_time = self.estimated_start_time.isoformat() \ if self.estimated_start_time else self._get_value(self.estimated_start_time) estimated_complete_time = self.estimated_complete_time.isoformat() \ if self.estimated_complete_time else self._get_value(self.estimated_complete_time) queue_info = [ "job_id='{}'".format(self._get_value(self.job_id)), "_status='{}'".format(self._get_value(status)), "estimated_start_time='{}'".format(estimated_start_time), "estimated_complete_time='{}'".format(estimated_complete_time), "position={}".format(self._get_value(self.position)), "hub_priority={}".format(self._get_value(self.hub_priority)), "group_priority={}".format(self._get_value(self.group_priority)), "project_priority={}".format(self._get_value(self.project_priority)) ] return "<{}({})>".format(self.__class__.__name__, ', '.join(queue_info)) def format(self) -> str: """Build an user-friendly report for the job queue information. Returns: The job queue information report. """ status = api_status_to_job_status(ApiJobStatus(self._status)).value \ if self._status else self._get_value(self._status) estimated_start_time = arrow.get(self.estimated_start_time).humanize() \ if self.estimated_start_time else self._get_value(self.estimated_start_time) estimated_complete_time = arrow.get(self.estimated_complete_time).humanize() \ if self.estimated_complete_time else self._get_value(self.estimated_complete_time) queue_info = [ "Job {} queue information:".format(self._get_value(self.job_id)), " queue position: {}".format(self._get_value(self.position)), " status: {}".format(status), " estimated start time: {}".format(estimated_start_time), " estimated completion time: {}".format(estimated_complete_time), " hub priority: {}".format(self._get_value(self.hub_priority)), " group priority: {}".format(self._get_value(self.group_priority)), " project priority: {}".format(self._get_value(self.project_priority)) ] return '\n'.join(queue_info) def _get_value(self, value: Optional[Any], default_value: str = 'unknown') -> Optional[Any]: """Returns the value if it exists or a default. Returns: The value if it is not None, else a default value. """ return value or default_value qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/job/__init__.py0000664000372000037200000000112413616666011025605 0ustar travistravis00000000000000# -*- 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 representing Jobs communicating with IBM Q.""" from .ibmqjob import IBMQJob qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/ibmqbackend.py0000664000372000037200000005636513616666011025555 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2020. # # 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 interfacing with an IBMQ Backend.""" import logging import warnings from typing import Dict, List, Union, Optional, Any from datetime import datetime as python_datetime from marshmallow import ValidationError from qiskit.qobj import Qobj, validate_qobj_against_schema from qiskit.providers.basebackend import BaseBackend # type: ignore[attr-defined] from qiskit.providers.jobstatus import JobStatus from qiskit.providers.models import (BackendStatus, BackendProperties, PulseDefaults, BackendConfiguration, GateConfig) from qiskit.validation.exceptions import ModelValidationError from qiskit.tools.events.pubsub import Publisher from qiskit.providers.ibmq import accountprovider # pylint: disable=unused-import from .apiconstants import ApiJobShareLevel, ApiJobStatus, API_JOB_FINAL_STATES from .job.utils import api_status_to_job_status from .api.clients import AccountClient from .api.exceptions import ApiError from .backendjoblimit import BackendJobLimit from .credentials import Credentials from .exceptions import (IBMQBackendError, IBMQBackendValueError, IBMQBackendApiError, IBMQBackendApiProtocolError) from .job import IBMQJob from .utils import update_qobj_config, validate_job_tags logger = logging.getLogger(__name__) class IBMQBackend(BaseBackend): """Backend class interfacing with an IBMQ backend.""" def __init__( self, configuration: BackendConfiguration, provider: 'accountprovider.AccountProvider', credentials: Credentials, api: AccountClient ) -> None: """Initialize remote backend for IBM Quantum Experience. Args: configuration: configuration of backend. provider: provider. credentials: credentials. api: api for communicating with the Quantum Experience. """ super().__init__(provider=provider, configuration=configuration) self._api = api self._credentials = credentials self.hub = credentials.hub self.group = credentials.group self.project = credentials.project # Attributes used by caching functions. self._properties = None self._defaults = None def run( self, qobj: Qobj, job_name: Optional[str] = None, job_share_level: Optional[str] = None, job_tags: Optional[List[str]] = None ) -> IBMQJob: """Run a Qobj asynchronously. Args: qobj: description of job. job_name: custom name to be assigned to the job. This job name can subsequently be used as a filter in the ``jobs()`` function call. Job names do not need to be unique. Default: None. job_share_level: allows sharing a job at the hub/group/project and global level. The possible job share levels are: "global", "hub", "group", "project", and "none". * global: the job is public to any user. * hub: the job is shared between the users in the same hub. * group: the job is shared between the users in the same group. * project: the job is shared between the users in the same project. * none: the job is not shared at any level. If the job share level is not specified, then the job is not shared at any level. job_tags: tags to be assigned to the job. The tags can subsequently be used as a filter in the ``jobs()`` function call. Default: None. Returns: an instance derived from BaseJob Raises: SchemaValidationError: If the job validation fails. IBMQBackendApiError: If an unexpected error occurred while submitting the job. IBMQBackendApiProtocolError: If an unexpected value received from the server. IBMQBackendValueError: If an input parameter value is not valid. """ # pylint: disable=arguments-differ if job_share_level: try: api_job_share_level = ApiJobShareLevel(job_share_level.lower()) except ValueError: raise IBMQBackendValueError( '"{}" is not a valid job share level. ' 'Valid job share levels are: {}' .format(job_share_level, ', '.join(level.value for level in ApiJobShareLevel))) else: api_job_share_level = ApiJobShareLevel.NONE validate_job_tags(job_tags, IBMQBackendValueError) validate_qobj_against_schema(qobj) return self._submit_job(qobj, job_name, api_job_share_level, job_tags) def _submit_job( self, qobj: Qobj, job_name: Optional[str] = None, job_share_level: Optional[ApiJobShareLevel] = None, job_tags: Optional[List[str]] = None ) -> IBMQJob: """Submit qobj job to IBM-Q. Args: qobj: description of job. job_name: custom name to be assigned to the job. This job name can subsequently be used as a filter in the ``jobs()`` function call. Job names do not need to be unique. job_share_level: level the job should be shared at. job_tags: tags to be assigned to the job. Returns: an instance derived from BaseJob Events: ibmq.job.start: The job has started. Raises: IBMQBackendApiError: If an unexpected error occurred while submitting the job. IBMQBackendError: If an unexpected error occurred after submitting the job. IBMQBackendApiProtocolError: If an unexpected value received from the server. """ try: qobj_dict = qobj.to_dict() submit_info = self._api.job_submit( backend_name=self.name(), qobj_dict=qobj_dict, use_object_storage=getattr(self.configuration(), 'allow_object_storage', False), job_name=job_name, job_share_level=job_share_level, job_tags=job_tags) except ApiError as ex: raise IBMQBackendApiError('Error submitting job: {}'.format(str(ex))) # Error in the job after submission: # Transition to the `ERROR` final state. if 'error' in submit_info: raise IBMQBackendError( 'Error submitting job: {}'.format(str(submit_info['error']))) # Submission success. submit_info.update({ '_backend': self, 'api': self._api, 'qObject': qobj_dict }) try: job = IBMQJob.from_dict(submit_info) except ModelValidationError as err: raise IBMQBackendApiProtocolError('Unexpected return value from the server ' 'when submitting job: {}'.format(str(err))) Publisher().publish("ibmq.job.start", job) return job def properties( self, refresh: bool = False, datetime: Optional[python_datetime] = None ) -> Optional[BackendProperties]: """Return the online backend properties with optional filtering. Args: refresh: if True, the return is via a QX API call. Otherwise, a cached version is returned. datetime: by specifying a datetime, this function returns an instance of the BackendProperties whose timestamp is closest to, but older than, the specified datetime. Returns: The properties of the backend. If the backend has no properties to display, it returns ``None``. """ # pylint: disable=arguments-differ if datetime: # Do not use cache for specific datetime properties. api_properties = self._api.backend_properties(self.name(), datetime=datetime) if not api_properties: return None return BackendProperties.from_dict(api_properties) if refresh or self._properties is None: api_properties = self._api.backend_properties(self.name()) self._properties = BackendProperties.from_dict(api_properties) return self._properties def status(self) -> BackendStatus: """Return the online backend status. Returns: The status of the backend. Raises: LookupError: If status for the backend can't be found. IBMQBackendError: If the status can't be formatted properly. """ api_status = self._api.backend_status(self.name()) try: return BackendStatus.from_dict(api_status) except ValidationError as ex: raise LookupError( "Couldn't get backend status: {0}".format(ex)) def defaults(self, refresh: bool = False) -> Optional[PulseDefaults]: """Return the pulse defaults for the backend. Args: refresh: if True, the return is via a QX API call. Otherwise, a cached version is returned. Returns: the pulse defaults for the backend. If the backend does not support defaults, it returns ``None``. """ if not self.configuration().open_pulse: return None if refresh or self._defaults is None: api_defaults = self._api.backend_pulse_defaults(self.name()) if api_defaults: self._defaults = PulseDefaults.from_dict(api_defaults) else: self._defaults = None return self._defaults def job_limit(self) -> BackendJobLimit: """Return the job limit for the backend. The job limit information for this backend includes the current number of active jobs you have and the maximum number of active jobs you can have. Note: The job limit information for the backend is provider specific. For example, if you have access to the same backend via different providers, the job limit information might be different for each provider. If the method call was successful, you can inspect the job limit for the backend by accessing the ``maximum_jobs`` and ``active_jobs`` attributes of the ``BackendJobLimit`` instance returned. For example: backend_job_limit = backend.job_limit() maximum_jobs = backend_job_limit.maximum_jobs active_jobs = backend_job_limit.active_jobs * If ``maximum_jobs`` is equal to ``None``, then there are no limits to the maximum number of active jobs a user could have on the backend at any given time. Returns: the job limit for the backend with this provider. Raises: IBMQBackendApiProtocolError: If an unexpected value received from the server. """ api_job_limit = self._api.backend_job_limit(self.name()) try: job_limit = BackendJobLimit.from_dict(api_job_limit) if job_limit.maximum_jobs == -1: # Manually set `maximum` to `None` if backend has no job limit. job_limit.maximum_jobs = None return job_limit except ValidationError as ex: raise IBMQBackendApiProtocolError( 'Unexpected return value from the server when ' 'querying job limit data for the backend: {}.'.format(ex)) def remaining_jobs_count(self) -> Optional[int]: """Return the number of remaining jobs that could be submitted to the backend. Return the number of jobs that can be submitted to this backend with this provider before the maximum limit on active jobs is reached. Note: The number of remaining jobs for the backend is provider specific. For example, if you have access to the same backend via different providers, the number of remaining jobs might be different. See ``IBMQBackend.job_limit()`` for the job limit information of the backend. * If ``None`` is returned, then there are no limits to the maximum number of active jobs a user could have on the backend at any given time. Returns: Remaining number of jobs a user could submit to the backend with this provider before the maximum limit on active jobs is reached. Raises: IBMQBackendApiProtocolError: If an unexpected value received from the server. """ job_limit = self.job_limit() if job_limit.maximum_jobs is None: return None return job_limit.maximum_jobs - job_limit.active_jobs def jobs( self, limit: int = 10, skip: int = 0, status: Optional[Union[JobStatus, str, List[Union[JobStatus, str]]]] = None, job_name: Optional[str] = None, start_datetime: Optional[python_datetime] = None, end_datetime: Optional[python_datetime] = None, job_tags: Optional[List[str]] = None, job_tags_operator: Optional[str] = "OR", db_filter: Optional[Dict[str, Any]] = None ) -> List[IBMQJob]: """Return the jobs submitted to this backend. Return the jobs submitted to this backend, with optional filtering and pagination. Note that the API has a limit for the number of jobs returned in a single call, and this function might involve making several calls to the API. See also the `skip` parameter for more control over pagination. Note that jobs submitted with earlier versions of Qiskit (in particular, those that predate the Qobj format) are not included in the returned list. Args: limit: number of jobs to retrieve. skip: starting index for the job retrieval. status: only get jobs with this status or one of the statuses. Default: None. For example, you can specify `status=JobStatus.RUNNING` or `status="RUNNING"` or `status=["RUNNING", "ERROR"] job_name: filter by job name. The `job_name` is matched partially and `regular expressions `_ can be used. start_datetime: filter by start date. This is used to find jobs whose creation dates are after (greater than or equal to) this date/time. end_datetime: filter by end date. This is used to find jobs whose creation dates are before (less than or equal to) this date/time. job_tags: filter by tags assigned to jobs. Default: None. job_tags_operator: logical operator to use when filtering by job tags. Valid values are "AND" and "OR": * If "AND" is specified, then a job must have all of the tags specified in ``job_tags`` to be included. * If "OR" is specified, then a job only needs to have any of the tags specified in ``job_tags`` to be included. Default: OR. db_filter: `loopback-based filter `_. This is an interface to a database ``where`` filter. Some examples of its usage are: Filter last five jobs with errors:: job_list = backend.jobs(limit=5, status=JobStatus.ERROR) Filter last five jobs with hub name ``ibm-q``:: filter = {'hubInfo.hub.name': 'ibm-q'} job_list = backend.jobs(limit=5, db_filter=filter) Returns: list of IBMQJob instances Raises: IBMQBackendValueError: if a keyword value is not recognized. """ return self._provider.backends.jobs( limit, skip, self.name(), status, job_name, start_datetime, end_datetime, job_tags, job_tags_operator, db_filter) def active_jobs(self, limit: int = 10) -> List[IBMQJob]: """Return the current, unfinished jobs submitted to this backend. Return the jobs submitted to this backend with this provider that are currently in an unfinished status, including: "INITIALIZING", "VALIDATING", "QUEUED", and "RUNNING". Args: limit: number of jobs to retrieve. Default: 10. Returns: a list of the current unfinished jobs for this backend on this provider. """ # Get the list of api job statuses which are not a final api job status. active_job_states = list({api_status_to_job_status(status) for status in ApiJobStatus if status not in API_JOB_FINAL_STATES}) return self.jobs(status=active_job_states, limit=limit) def retrieve_job(self, job_id: str) -> IBMQJob: """Return a job submitted to this backend. Args: job_id: the job id of the job to retrieve Returns: class instance Raises: IBMQBackendError: if retrieval failed """ job = self._provider.backends.retrieve_job(job_id) job_backend = job.backend() if self.name() != job_backend.name(): warnings.warn('Job "{}" belongs to another backend than the one queried. ' 'The query was made on backend "{}", ' 'but the job actually belongs to backend "{}".' .format(job_id, self.name(), job_backend.name())) raise IBMQBackendError('Failed to get job "{}": ' 'job does not belong to backend "{}".' .format(job_id, self.name())) return self._provider.backends.retrieve_job(job_id) def __repr__(self) -> str: credentials_info = '' if self.hub: credentials_info = "hub='{}', group='{}', project='{}'".format( self.hub, self.group, self.project) return "<{}('{}') from IBMQ({})>".format( self.__class__.__name__, self.name(), credentials_info) class IBMQSimulator(IBMQBackend): """Backend class interfacing with an IBMQ simulator.""" def properties( self, refresh: bool = False, datetime: Optional[python_datetime] = None ) -> None: """Return the online backend properties. Returns: None """ return None def run( self, qobj: Qobj, job_name: Optional[str] = None, job_share_level: Optional[str] = None, job_tags: Optional[List[str]] = None, backend_options: Optional[Dict] = None, noise_model: Any = None ) -> IBMQJob: """Run qobj asynchronously. Args: qobj: description of job backend_options: backend options noise_model: noise model job_name: custom name to be assigned to the job job_share_level: allows sharing a job at the hub/group/project and global level (see `IBMQBackend.run()` for more details). job_tags: tags to be assigned to the job. The tags can subsequently be used as a filter in the ``jobs()`` function call. Default: None. Returns: an instance derived from BaseJob """ # pylint: disable=arguments-differ qobj = update_qobj_config(qobj, backend_options, noise_model) return super(IBMQSimulator, self).run(qobj, job_name, job_share_level, job_tags) class IBMQRetiredBackend(IBMQBackend): """Backend class interfacing with an IBMQ device that is no longer available.""" def __init__( self, configuration: BackendConfiguration, provider: 'accountprovider.AccountProvider', credentials: Credentials, api: AccountClient ) -> None: """Initialize remote backend for IBM Quantum Experience. Args: configuration: configuration of backend. provider: provider. credentials: credentials. api: api for communicating with the Quantum Experience. """ super().__init__(configuration, provider, credentials, api) self._status = BackendStatus( backend_name=self.name(), backend_version=self.configuration().backend_version, operational=False, pending_jobs=0, status_msg='This backend is no longer available.') def properties( self, refresh: bool = False, datetime: Optional[python_datetime] = None ) -> None: """Return the online backend properties.""" return None def defaults(self, refresh: bool = False) -> None: """Return the pulse defaults for the backend.""" return None def status(self) -> BackendStatus: """Return the online backend status.""" return self._status def job_limit(self) -> None: """Return the job limits for the backend.""" return None def remaining_jobs_count(self) -> None: """Return the number of remaining jobs that could be submitted to the backend.""" return None def active_jobs(self, limit: int = 10) -> None: """Return the current, unfinished jobs submitted to this backend.""" return None def run( self, qobj: Qobj, job_name: Optional[str] = None, job_share_level: Optional[str] = None, job_tags: Optional[List[str]] = None ) -> None: """Run a Qobj.""" raise IBMQBackendError('This backend is no longer available.') @classmethod def from_name( cls, backend_name: str, provider: 'accountprovider.AccountProvider', credentials: Credentials, api: AccountClient ) -> 'IBMQRetiredBackend': """Return a retired backend from its name.""" configuration = BackendConfiguration( backend_name=backend_name, backend_version='0.0.0', n_qubits=1, basis_gates=[], simulator=False, local=False, conditional=False, open_pulse=False, memory=False, max_shots=1, gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], coupling_map=[[0, 1]], ) return cls(configuration, provider, credentials, api) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/ibmqbackendservice.py0000664000372000037200000003754213616666011027132 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """Backend namespace for an IBM Quantum Experience account provider.""" import logging from typing import Dict, List, Callable, Optional, Any, Union from types import SimpleNamespace from datetime import datetime from qiskit.providers import JobStatus, QiskitBackendNotFoundError # type: ignore[attr-defined] from qiskit.providers.providerutils import filter_backends from qiskit.validation.exceptions import ModelValidationError from qiskit.providers.ibmq import accountprovider # pylint: disable=unused-import from .api.exceptions import ApiError from .apiconstants import ApiJobStatus from .exceptions import (IBMQBackendValueError, IBMQBackendApiError, IBMQBackendApiProtocolError) from .ibmqbackend import IBMQBackend, IBMQRetiredBackend from .job import IBMQJob from .utils import to_python_identifier, validate_job_tags logger = logging.getLogger(__name__) class IBMQBackendService(SimpleNamespace): """Backend namespace for an IBM Quantum Experience account provider.""" def __init__(self, provider: 'accountprovider.AccountProvider') -> None: """Creates a new IBMQBackendService instance. Args: provider: IBM Q Experience account provider """ super().__init__() self._provider = provider self._discover_backends() def _discover_backends(self) -> None: """Discovers the remote backends if not already known.""" for backend in self._provider._backends.values(): backend_name = to_python_identifier(backend.name()) # Append _ if duplicate while backend_name in self.__dict__: backend_name += '_' setattr(self, backend_name, backend) def __call__( self, name: Optional[str] = None, filters: Optional[Callable[[List[IBMQBackend]], bool]] = None, timeout: Optional[float] = None, **kwargs: Any ) -> List[IBMQBackend]: """Return all backends accessible via this provider, subject to optional filtering. Args: name: backend name to filter by filters: more complex filters, such as lambda functions e.g. AccountProvider.backends( filters=lambda b: b.configuration().n_qubits > 5) timeout: number of seconds to wait for backend discovery. kwargs: simple filters specifying a true/false criteria in the backend configuration or backend status or provider credentials e.g. AccountProvider.backends(n_qubits=5, operational=True) Returns: list of backends available that match the filter """ backends = self._provider._backends.values() # Special handling of the `name` parameter, to support alias # resolution. if name: aliases = self._aliased_backend_names() aliases.update(self._deprecated_backend_names()) name = aliases.get(name, name) kwargs['backend_name'] = name return filter_backends(backends, filters=filters, **kwargs) def jobs( self, limit: int = 10, skip: int = 0, backend_name: Optional[str] = None, status: Optional[Union[JobStatus, str, List[Union[JobStatus, str]]]] = None, job_name: Optional[str] = None, start_datetime: Optional[datetime] = None, end_datetime: Optional[datetime] = None, job_tags: Optional[List[str]] = None, job_tags_operator: Optional[str] = "OR", db_filter: Optional[Dict[str, Any]] = None ) -> List[IBMQJob]: """Return a list of jobs from the API. Return a list of jobs, with optional filtering and pagination. Note that the API has a limit for the number of jobs returned in a single call, and this function might involve making several calls to the API. See also the `skip` parameter for more control over pagination. Note that jobs submitted with earlier versions of Qiskit (in particular, those that predate the Qobj format) are not included in the returned list. Args: limit: number of jobs to retrieve. Default: 10. skip: starting index for the job retrieval. Default: 0. backend_name: name of the backend. Default: None. status: only get jobs with this status or one of the statuses. Default: None. For example, you can specify `status=JobStatus.RUNNING` or `status="RUNNING"` or `status=["RUNNING", "ERROR"] job_name: filter by job name. The `job_name` is matched partially and `regular expressions `_ can be used. Default: None. start_datetime: filter by start date. This is used to find jobs whose creation dates are after (greater than or equal to) this date/time. Default: None. end_datetime: filter by end date. This is used to find jobs whose creation dates are before (less than or equal to) this date/time. Default: None. job_tags: filter by tags assigned to jobs. Default: None. job_tags_operator: logical operator to use when filtering by job tags. Valid values are "AND" and "OR": * If "AND" is specified, then a job must have all of the tags specified in ``job_tags`` to be included. * If "OR" is specified, then a job only needs to have any of the tags specified in ``job_tags`` to be included. Default: OR. db_filter: `loopback-based filter `_. This is an interface to a database ``where`` filter. Default: None. Some examples of its usage are: Filter last five jobs with errors:: job_list = backend.jobs(limit=5, status=JobStatus.ERROR) Filter last five jobs with hub name ``ibm-q``:: filter = {'hubInfo.hub.name': 'ibm-q'} job_list = backend.jobs(limit=5, db_filter=filter) Returns: list of IBMQJob instances Raises: IBMQBackendValueError: if a keyword value is not recognized. """ # Build the filter for the query. api_filter = {} # type: Dict[str, Any] if backend_name: api_filter['backend.name'] = backend_name if status: status_filter = self._get_status_db_filter(status) api_filter.update(status_filter) if job_name: api_filter['name'] = {"regexp": job_name} if start_datetime and end_datetime: api_filter['creationDate'] = { 'between': [start_datetime.isoformat(), end_datetime.isoformat()] } elif start_datetime: api_filter['creationDate'] = {'gte': start_datetime.isoformat()} elif end_datetime: api_filter['creationDate'] = {'lte': end_datetime.isoformat()} if job_tags: validate_job_tags(job_tags, IBMQBackendValueError) job_tags_operator = job_tags_operator.upper() if job_tags_operator == "OR": api_filter['tags'] = {'inq': job_tags} elif job_tags_operator == "AND": and_tags = [] for tag in job_tags: and_tags.append({'tags': tag}) api_filter['and'] = and_tags else: raise IBMQBackendValueError( '"{}" is not a valid job_tags_operator value. ' 'Valid values are "AND" and "OR"'.format(job_tags_operator)) if db_filter: # Rather than overriding the logical operators `and`/`or`, first # check to see if the `api_filter` query should be extended with the # `api_filter` query for the same keys instead. logical_operators_to_expand = ['or', 'and'] for key in db_filter: key = key.lower() if key in logical_operators_to_expand and key in api_filter: api_filter[key].extend(db_filter[key]) # Argument filters takes precedence over db_filter for same keys api_filter = {**db_filter, **api_filter} # Retrieve the requested number of jobs, using pagination. The API # might limit the number of jobs per request. job_responses = [] # type: List[Dict[str, Any]] current_page_limit = limit while True: job_page = self._provider._api.list_jobs_statuses( limit=current_page_limit, skip=skip, extra_filter=api_filter) job_responses += job_page skip = skip + len(job_page) if not job_page: # Stop if there are no more jobs returned by the API. break if limit: if len(job_responses) >= limit: # Stop if we have reached the limit. break current_page_limit = limit - len(job_responses) else: current_page_limit = 0 job_list = [] for job_info in job_responses: job_id = job_info.get('id', "") # Recreate the backend used for this job. backend_name = job_info.get('backend', {}).get('name', 'unknown') try: backend = self._provider.get_backend(backend_name) except QiskitBackendNotFoundError: backend = IBMQRetiredBackend.from_name(backend_name, self._provider, self._provider.credentials, self._provider._api) job_info.update({ '_backend': backend, 'api': self._provider._api, }) try: job = IBMQJob.from_dict(job_info) except ModelValidationError: logger.warning('Discarding job "%s" because it contains invalid data.', job_id) continue job_list.append(job) return job_list def _get_status_db_filter( self, status_arg: Union[JobStatus, str, List[Union[JobStatus, str]]] ) -> Dict[str, Any]: """Return the db filter to use when searching for jobs based on status or statuses. Returns: The status db filter used to query the api when searching for jobs that match a given status or list of statuses. Raises: IBMQBackendError: If a status value is not recognized. """ _final_status_filter = None if isinstance(status_arg, list): _final_status_filter = {'or': []} for status in status_arg: status_filter = self._get_status_filter(status) _final_status_filter['or'].append(status_filter) else: status_filter = self._get_status_filter(status_arg) _final_status_filter = status_filter return _final_status_filter def _get_status_filter(self, status: Union[JobStatus, str]) -> Dict[str, Any]: """Return the db filter to use when searching for jobs based on a status. Returns: The status db filter used to query the api when searching for jobs that match a given status. Raises: IBMQBackendValueError: If the status value is not recognized. """ if isinstance(status, str): try: status = JobStatus[status.upper()] except KeyError: raise IBMQBackendValueError( '{} is not a valid status value. Valid values are {}'.format( status, ", ".join(job_status.name for job_status in JobStatus))) \ from None _status_filter = {} # type: Dict[str, Any] if status == JobStatus.INITIALIZING: _status_filter = {'status': { 'inq': [ApiJobStatus.CREATING.value, ApiJobStatus.CREATED.value] }} elif status == JobStatus.VALIDATING: _status_filter = {'status': { 'inq': [ApiJobStatus.VALIDATING.value, ApiJobStatus.VALIDATED.value] }} elif status == JobStatus.RUNNING: _status_filter = {'status': ApiJobStatus.RUNNING.value} elif status == JobStatus.QUEUED: _status_filter = {'status': ApiJobStatus.QUEUED.value} elif status == JobStatus.CANCELLED: _status_filter = {'status': ApiJobStatus.CANCELLED.value} elif status == JobStatus.DONE: _status_filter = {'status': ApiJobStatus.COMPLETED.value} elif status == JobStatus.ERROR: _status_filter = {'status': {'regexp': '^ERROR'}} # type: ignore[assignment] else: raise IBMQBackendValueError( '{} is not a valid status value. Valid values are {}'.format( status, ", ".join(job_status.name for job_status in JobStatus))) return _status_filter def retrieve_job(self, job_id: str) -> IBMQJob: """Return a single job from the API. Args: job_id: the job id of the job to retrieve Returns: class instance Raises: IBMQBackendApiError: if there was some unexpected failure in the server. IBMQBackendApiProtocolError: if unexpected return value received from the server. """ try: job_info = self._provider._api.job_get(job_id) except ApiError as ex: raise IBMQBackendApiError('Failed to get job "{}": {}' .format(job_id, str(ex))) # Recreate the backend used for this job. backend_name = job_info.get('backend', {}).get('name', 'unknown') try: backend = self._provider.get_backend(backend_name) except QiskitBackendNotFoundError: backend = IBMQRetiredBackend.from_name(backend_name, self._provider, self._provider.credentials, self._provider._api) job_info.update({ '_backend': backend, 'api': self._provider._api }) try: job = IBMQJob.from_dict(job_info) except ModelValidationError as ex: raise IBMQBackendApiProtocolError( 'Failed to get job "{}". Invalid job data received: {}'.format(job_id, str(ex))) return job @staticmethod def _deprecated_backend_names() -> Dict[str, str]: """Returns deprecated backend names.""" return { 'ibmqx_qasm_simulator': 'ibmq_qasm_simulator', 'ibmqx_hpc_qasm_simulator': 'ibmq_qasm_simulator', 'real': 'ibmqx1' } @staticmethod def _aliased_backend_names() -> Dict[str, str]: """Returns aliased backend names.""" return { 'ibmq_5_yorktown': 'ibmqx2', 'ibmq_5_tenerife': 'ibmqx4', 'ibmq_16_rueschlikon': 'ibmqx5', 'ibmq_20_austin': 'QS1_1' } qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/exceptions.py0000664000372000037200000000373013616666011025462 0ustar travistravis00000000000000# -*- 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. """Exception for the IBMQ module.""" from qiskit.exceptions import QiskitError class IBMQError(QiskitError): """Base class for errors raised by the IBMQ provider module.""" pass class IBMQAccountError(IBMQError): """Base class for errors raised by account management.""" pass class IBMQAccountCredentialsNotFound(IBMQAccountError): """Error raised when credentials not found.""" pass class IBMQAccountCredentialsInvalidFormat(IBMQAccountError): """Error raised when credentials format is invalid.""" pass class IBMQAccountCredentialsInvalidToken(IBMQAccountError): """Error raised for an invalid IBM Q Experience API token..""" pass class IBMQAccountCredentialsInvalidUrl(IBMQAccountError): """Error raised for an invalid IBM Q Experience API url..""" pass class IBMQAccountMultipleCredentialsFound(IBMQAccountError): """Error raised when multiple credentials found.""" pass class IBMQProviderError(IBMQAccountError): """Errors related to provider handling.""" pass class IBMQBackendError(IBMQError): """IBM Q Backend Errors.""" pass class IBMQBackendApiError(IBMQBackendError): """Error that occurs unexpectedly when querying the API.""" pass class IBMQBackendApiProtocolError(IBMQBackendApiError): """Error raised when unexpected API return values received.""" pass class IBMQBackendValueError(IBMQBackendError, ValueError): """Value errors thrown within IBMQBackend.""" pass qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/0000775000372000037200000000000013616666025023502 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/0000775000372000037200000000000013616666025024457 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/root.py0000664000372000037200000001324213616666011026011 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2018, 2020. # # 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. """Root REST adapter for the IBM Q Experience API.""" import json from typing import Dict, List, Optional, Any from .base import RestAdapterBase from .backend import Backend from .job import Job class Api(RestAdapterBase): """Rest adapter for general endpoints.""" URL_MAP = { 'backends': '/devices/v/1', 'hubs': '/Network', 'jobs': '/Jobs', 'jobs_status': '/Jobs/status', 'circuit': '/qcircuit', 'version': '/version' } def backend(self, backend_name: str) -> Backend: """Return a adapter for a specific backend. Args: backend_name: name of the backend. Returns: the backend adapter. """ return Backend(self.session, backend_name) def job(self, job_id: str) -> Job: """Return a adapter for a specific job. Args: job_id: id of the job. Returns: the backend adapter. """ return Job(self.session, job_id) def backends(self, timeout: Optional[float] = None) -> List[Dict[str, Any]]: """Return the list of backends. Args: timeout: number of seconds to wait for the request. Returns: json response. """ url = self.get_url('backends') return self.session.get(url, timeout=timeout).json() def hubs(self) -> List[Dict[str, Any]]: """Return the list of hubs available to the user.""" url = self.get_url('hubs') return self.session.get(url).json() def jobs( self, limit: int = 10, skip: int = 0, extra_filter: Dict[str, Any] = None ) -> List[Dict[str, Any]]: """Return a list of jobs statuses. Args: limit: maximum number of items to return. skip: offset for the items to return. extra_filter: additional filtering passed to the query. Returns: json response. """ url = self.get_url('jobs_status') query = { 'order': 'creationDate DESC', 'limit': limit, 'skip': skip, } if extra_filter: query['where'] = extra_filter return self.session.get( url, params={'filter': json.dumps(query)}).json() def job_submit( self, backend_name: str, qobj_dict: Dict[str, Any], job_name: Optional[str] = None, job_share_level: Optional[str] = None, job_tags: Optional[List[str]] = None ) -> Dict[str, Any]: """Submit a job for executing. Args: backend_name: the name of the backend. qobj_dict: the Qobj to be executed, as a dictionary. job_name: custom name to be assigned to the job. job_share_level: level the job should be shared at. job_tags: tags to be assigned to the job. Returns: json response. """ url = self.get_url('jobs') payload = { 'qObject': qobj_dict, 'backend': {'name': backend_name}, 'shots': qobj_dict.get('config', {}).get('shots', 1) } if job_name: payload['name'] = job_name if job_share_level: payload['shareLevel'] = job_share_level if job_tags: payload['tags'] = job_tags return self.session.post(url, json=payload).json() def submit_job_object_storage( self, backend_name: str, shots: int = 1, job_name: Optional[str] = None, job_share_level: Optional[str] = None, job_tags: Optional[List[str]] = None ) -> Dict[str, Any]: """Submit a job for executing, using object storage. Args: backend_name: the name of the backend. shots: number of shots. job_name: custom name to be assigned to the job. job_share_level: level the job should be shared at. job_tags: tags to be assigned to the job. Returns: json response. """ url = self.get_url('jobs') # TODO: "shots" is currently required by the API. payload = { 'backend': {'name': backend_name}, 'shots': shots, 'allowObjectStorage': True } if job_name: payload['name'] = job_name if job_share_level: payload['shareLevel'] = job_share_level if job_tags: payload['tags'] = job_tags return self.session.post(url, json=payload).json() def circuit(self, name: str, **kwargs: Any) -> Dict[str, Any]: """Execute a Circuit. Args: name: name of the Circuit. **kwargs: arguments for the Circuit. Returns: json response. """ url = self.get_url('circuit') payload = { 'name': name, 'params': kwargs } return self.session.post(url, json=payload).json() def version(self) -> Dict[str, Any]: """Return the API versions.""" url = self.get_url('version') return self.session.get(url).json() qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/job.py0000664000372000037200000001151113616666011025575 0ustar travistravis00000000000000# -*- 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. """Job REST adapter for the IBM Q Experience API.""" import pprint from json.decoder import JSONDecodeError from typing import Dict, Any from marshmallow.exceptions import ValidationError from .base import RestAdapterBase from .validation import StatusResponseSchema from ..session import RetrySession from ..exceptions import ApiIBMQProtocolError class Job(RestAdapterBase): """Rest adapter for job related endpoints.""" URL_MAP = { 'callback_upload': '/jobDataUploaded', 'callback_download': '/resultDownloaded', 'cancel': '/cancel', 'download_url': '/jobDownloadUrl', 'self': '', 'status': '/status', 'properties': '/properties', 'result_url': '/resultDownloadUrl', 'upload_url': '/jobUploadUrl' } def __init__(self, session: RetrySession, job_id: str) -> None: """Job constructor. Args: session: session to be used in the adaptor. job_id: id of the job. """ self.job_id = job_id super().__init__(session, '/Jobs/{}'.format(job_id)) def get(self) -> Dict[str, Any]: """Return a job. Returns: json response. """ url = self.get_url('self') response = self.session.get(url).json() if 'calibration' in response: response['properties'] = response.pop('calibration') return response def callback_upload(self) -> Dict[str, Any]: """Notify the API after uploading a Qobj via object storage.""" url = self.get_url('callback_upload') return self.session.post(url).json() def callback_download(self) -> Dict[str, Any]: """Notify the API after downloading a Qobj via object storage.""" url = self.get_url('callback_download') return self.session.post(url).json() def cancel(self) -> Dict[str, Any]: """Cancel a job.""" url = self.get_url('cancel') return self.session.post(url).json() def download_url(self) -> Dict[str, Any]: """Return an object storage URL for downloading the Qobj.""" url = self.get_url('download_url') return self.session.get(url).json() def properties(self) -> Dict[str, Any]: """Return the backend properties of a job.""" url = self.get_url('properties') return self.session.get(url).json() def result_url(self) -> Dict[str, Any]: """Return an object storage URL for downloading results.""" url = self.get_url('result_url') return self.session.get(url).json() def status(self) -> Dict[str, Any]: """Return the status of a job. Returns: status of a job Raises: ApiIBMQProtocolError: if an unexpected result is received from the server. """ url = self.get_url('status') raw_response = self.session.get(url) try: api_response = raw_response.json() except JSONDecodeError as err: raise ApiIBMQProtocolError( 'Unrecognized answer from server: {}. ' 'This could be caused by too many requests.'.format(raw_response)) from err try: # Validate the response. StatusResponseSchema().validate(api_response) except ValidationError as err: raise ApiIBMQProtocolError('Unrecognized answer from server: \n{}'.format( pprint.pformat(api_response))) from err return api_response def upload_url(self) -> Dict[str, Any]: """Return an object storage URL for uploading the Qobj.""" url = self.get_url('upload_url') return self.session.get(url).json() def put_object_storage(self, url: str, qobj_dict: Dict[str, Any]) -> str: """Upload a Qobj via object storage. Args: url: object storage URL. qobj_dict: the qobj to be uploaded, in dict form. Returns: text response, that will be empty if the request was successful. """ response = self.session.put(url, json=qobj_dict, bare=True) return response.text def get_object_storage(self, url: str) -> Dict[str, Any]: """Get via object_storage. Args: url: object storage URL. Returns: json response. """ return self.session.get(url, bare=True).json() qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/backend.py0000664000372000037200000000654313616666011026423 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2018, 2020. # # 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. """Backend REST adapter for the IBM Q Experience API.""" import json from typing import Dict, Optional, Any from datetime import datetime # pylint: disable=unused-import from .base import RestAdapterBase from ..session import RetrySession class Backend(RestAdapterBase): """Rest adapter for backend related endpoints.""" URL_MAP = { 'properties': '/properties', 'pulse_defaults': '/defaults', 'status': '/queue/status', 'jobs_limit': '/jobsLimit' } def __init__(self, session: RetrySession, backend_name: str) -> None: """Backend constructor. Args: session: session to be used in the adaptor. backend_name: name of the backend. """ self.backend_name = backend_name super().__init__(session, '/devices/{}'.format(backend_name)) def properties(self, datetime: Optional[datetime] = None) -> Dict[str, Any]: """Return backend properties. Args: datetime: datetime used for additional filtering passed to the query. Returns: json response of backend properties. """ # pylint: disable=redefined-outer-name url = self.get_url('properties') params = { 'version': 1 } query = {} if datetime: extra_filter = {'last_update_date': {'lt': datetime.isoformat()}} query['where'] = extra_filter params['filter'] = json.dumps(query) # type: ignore[assignment] response = self.session.get(url, params=params).json() # Adjust name of the backend. if response: response['backend_name'] = self.backend_name return response def pulse_defaults(self) -> Dict[str, Any]: """Return backend pulse defaults.""" url = self.get_url('pulse_defaults') return self.session.get(url).json() def status(self) -> Dict[str, Any]: """Return backend status.""" url = self.get_url('status') response = self.session.get(url).json() # Adjust fields according to the specs (BackendStatus). ret = { 'backend_name': self.backend_name, 'backend_version': response.get('backend_version', '0.0.0'), 'status_msg': response.get('status', ''), 'operational': bool(response.get('state', False)) } # 'pending_jobs' is required, and should be >= 0. if 'lengthQueue' in response: ret['pending_jobs'] = max(response['lengthQueue'], 0) else: ret['pending_jobs'] = 0 # Not part of the schema. if 'busy' in response: ret['dedicated'] = response['busy'] return ret def job_limit(self) -> Dict[str, Any]: """Return backend job limit.""" url = self.get_url('jobs_limit') return self.session.get(url).json() qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/version_finder.py0000664000372000037200000000302513616666011030040 0ustar travistravis00000000000000# -*- 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. """Version finder for the IBM Q Experience API.""" from json import JSONDecodeError from typing import Dict, Union from .base import RestAdapterBase class VersionFinder(RestAdapterBase): """Rest adapter for the version finder.""" URL_MAP = { 'version': '/version' } def version(self) -> Dict[str, Union[str, bool]]: """Return the version info. Returns: a dict with information about the API version, with the following keys: * `new_api` (bool): whether the new API is being used And the following optional keys: * `api-*` (str): the versions of each individual API component """ url = self.get_url('version') response = self.session.get(url) try: version_info = response.json() version_info['new_api'] = True except JSONDecodeError: return { 'new_api': False, 'api': response.text } return version_info qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/auth.py0000664000372000037200000000244613616666011025773 0ustar travistravis00000000000000# -*- 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. """Authentication REST adapter for the IBM Q Experience API.""" from typing import Dict, Any from .base import RestAdapterBase class Auth(RestAdapterBase): """Rest adapter for authentication endpoints.""" URL_MAP = { 'login': '/users/loginWithToken', 'user_info': '/users/me', } def login(self, api_token: str) -> Dict[str, Any]: """Login with token. Args: api_token: API token. Returns: json response. """ url = self.get_url('login') return self.session.post(url, json={'apiToken': api_token}).json() def user_info(self) -> Dict[str, Any]: """Return user information.""" url = self.get_url('user_info') response = self.session.get(url).json() return response qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/validation.py0000664000372000037200000000554713616666011027171 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """Schemas for validation.""" # TODO The schemas defined here should be merged with others under rest/schemas # when they are ready from marshmallow import pre_load from marshmallow.validate import OneOf from qiskit.providers.ibmq.apiconstants import ApiJobStatus from qiskit.validation import BaseSchema from qiskit.validation.fields import String, Nested, Integer, DateTime, Float from qiskit.providers.ibmq.utils.fields import map_field_names # Helper schemas. class InfoQueueResponseSchema(BaseSchema): """Queue information schema, nested in StatusResponseSchema""" # Optional properties position = Integer(required=False, missing=None) _status = String(required=False, missing=None) estimated_start_time = DateTime(required=False, missing=None) estimated_complete_time = DateTime(required=False, missing=None) hub_priority = Float(required=False, missing=None) group_priority = Float(required=False, missing=None) project_priority = Float(required=False, missing=None) @pre_load def preprocess_field_names(self, data, **_): # type: ignore """Pre-process the info queue response fields.""" FIELDS_MAP = { # pylint: disable=invalid-name 'status': '_status', 'estimatedStartTime': 'estimated_start_time', 'estimatedCompleteTime': 'estimated_complete_time', 'hubPriority': 'hub_priority', 'groupPriority': 'group_priority', 'projectPriority': 'project_priority' } return map_field_names(FIELDS_MAP, data) # Endpoint schemas. class StatusResponseSchema(BaseSchema): """Schema for StatusResponse""" # Optional properties infoQueue = Nested(InfoQueueResponseSchema, required=False) # Required properties status = String(required=True, validate=OneOf([status.value for status in ApiJobStatus])) class BackendJobLimitResponseSchema(BaseSchema): """Schema for BackendJobLimit""" # Optional properties maximum_jobs = Integer(required=True) running_jobs = Integer(required=True) @pre_load def preprocess_field_names(self, data, **_): # type: ignore """Pre-process the jobs limit response fields.""" FIELDS_MAP = { # pylint: disable=invalid-name 'maximumJobs': 'maximum_jobs', 'runningJobs': 'running_jobs' } return map_field_names(FIELDS_MAP, data) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/base.py0000664000372000037200000000273013616666011025740 0ustar travistravis00000000000000# -*- 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. """REST clients for accessing the IBM Q Experience API.""" from ..session import RetrySession class RestAdapterBase: """Base class for REST adaptors.""" URL_MAP = {} # type: ignore[var-annotated] """Mapping between the internal name of an endpoint and the actual URL""" def __init__(self, session: RetrySession, prefix_url: str = '') -> None: """RestAdapterBase constructor. Args: session: session to be used in the adaptor. prefix_url: string to be prefixed to all urls. """ self.session = session self.prefix_url = prefix_url def get_url(self, identifier: str) -> str: """Return the resolved URL for the specified identifier. Args: identifier: internal identifier of the endpoint. Returns: the resolved URL of the endpoint (relative to the session base url). """ return '{}{}'.format(self.prefix_url, self.URL_MAP[identifier]) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/rest/__init__.py0000664000372000037200000000113513616666011026563 0ustar travistravis00000000000000# -*- 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. """REST adaptors for the IBM Q Experience API.""" from .auth import Auth from .root import Api qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/clients/0000775000372000037200000000000013616666025025143 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/clients/websocket.py0000664000372000037200000003340013616666011027476 0ustar travistravis00000000000000# -*- 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. """Client for websocket communication with the IBM Q Experience API.""" import asyncio import json import logging import time from abc import ABC, abstractmethod from typing import Dict, Union, Generator, Optional, Any from concurrent import futures from ssl import SSLError import warnings from collections import deque import nest_asyncio from websockets import connect, ConnectionClosed from websockets.client import WebSocketClientProtocol from websockets.exceptions import InvalidURI from qiskit.providers.ibmq.apiconstants import ApiJobStatus, API_JOB_FINAL_STATES from ..exceptions import (WebsocketError, WebsocketTimeoutError, WebsocketIBMQProtocolError, WebsocketAuthenticationError) from .base import BaseClient logger = logging.getLogger(__name__) # `asyncio` by design does not allow event loops to be nested. Jupyter (really # tornado) has its own event loop already so we need to patch it. # Patch asyncio to allow nested use of `loop.run_until_complete()`. nest_asyncio.apply() # TODO Replace coroutine with async def once Python 3.5 is dropped. # Also can upgrade to websocket 8 to avoid other deprecation warning. warnings.filterwarnings("ignore", category=DeprecationWarning, message="\"@coroutine\" decorator is deprecated") class WebsocketMessage(ABC): """Container for a message sent or received via websockets. Args: type_: message type. """ def __init__(self, type_: str) -> None: self.type_ = type_ @abstractmethod def get_data(self) -> Union[str, Dict[str, str]]: """Getter for "abstract" attribute subclasses define, `data`.""" pass def as_json(self) -> str: """Return a json representation of the message.""" return json.dumps({'type': self.type_, 'data': self.get_data()}) class WebsocketAuthenticationMessage(WebsocketMessage): """Container for an authentication message sent via websockets. Args: type_: message type. data: data type. """ def __init__(self, type_: str, data: str) -> None: super().__init__(type_) self.data = data def get_data(self) -> str: return self.data class WebsocketResponseMethod(WebsocketMessage): """Container for a message received via websockets. Args: type_: message type. data: data type. """ def __init__(self, type_: str, data: Dict[str, str]) -> None: super().__init__(type_) self.data = data def get_data(self) -> Dict[str, str]: return self.data @classmethod def from_bytes(cls, json_string: bytes) -> 'WebsocketResponseMethod': """Instantiate a message from a bytes response.""" try: parsed_dict = json.loads(json_string.decode('utf8')) except (ValueError, AttributeError) as ex: raise WebsocketIBMQProtocolError('Unable to parse message') from ex return cls(parsed_dict['type'], parsed_dict.get('data', None)) class WebsocketClient(BaseClient): """Client for websocket communication with the IBM Q Experience API. Args: websocket_url: URL for websocket communication with IBM Q. access_token: access token for IBM Q. """ BACKOFF_MAX = 8 # Maximum time to wait between retries. def __init__(self, websocket_url: str, access_token: str) -> None: self.websocket_url = websocket_url.rstrip('/') self.access_token = access_token @asyncio.coroutine def _connect(self, url: str) -> Generator[Any, None, WebSocketClientProtocol]: """Authenticate against the websocket server, returning the connection. Returns: an open websocket connection. Raises: WebsocketError: if the connection to the websocket server could not be established. WebsocketAuthenticationError: if the connection to the websocket was established, but the authentication failed. WebsocketIBMQProtocolError: if the connection to the websocket server was established, but the answer was unexpected. """ try: logger.debug('Starting new websocket connection: %s', url) with warnings.catch_warnings(): # Suppress websockets deprecation warnings until the fix is available warnings.filterwarnings("ignore", category=DeprecationWarning) websocket = yield from connect(url) # Isolate specific exceptions, so they are not retried in `get_job_status`. except (SSLError, InvalidURI) as ex: raise ex # pylint: disable=broad-except except Exception as ex: raise WebsocketError('Could not connect to server') from ex try: # Authenticate against the server. auth_request = self._authentication_message() with warnings.catch_warnings(): # Suppress websockets deprecation warnings until the fix is available warnings.filterwarnings("ignore", category=DeprecationWarning) yield from websocket.send(auth_request.as_json()) # Verify that the server acknowledged our authentication. auth_response_raw = yield from websocket.recv() auth_response = WebsocketResponseMethod.from_bytes(auth_response_raw) if auth_response.type_ != 'authenticated': raise WebsocketIBMQProtocolError(auth_response.as_json()) except ConnectionClosed as ex: yield from websocket.close() raise WebsocketAuthenticationError( 'Error during websocket authentication') from ex return websocket @asyncio.coroutine def get_job_status( self, job_id: str, timeout: Optional[float] = None, retries: int = 5, backoff_factor: float = 0.5, status_deque: Optional[deque] = None ) -> Generator[Any, None, Dict[str, str]]: """Return the status of a job. Reads status messages from the API, which are issued at regular intervals. When a final state is reached, the server closes the socket. If the websocket connection is closed without a reason, the exponential backoff algorithm is used as a basis to reestablish connections. The algorithm takes effect when a connection closes, it is given by: 1. When a connection closes, sleep for a calculated backoff time. 2. Try to retrieve another socket and increment a retry counter. 3. Attempt to get the job status. - If the connection is closed, go back to step 1. - If the job status is read successfully, reset the retry counter. 4. Continue until the job status is complete or the maximum number of retries is met. Args: job_id: id of the job. timeout: timeout, in seconds. retries: max number of retries. backoff_factor: backoff factor used to calculate the time to wait between retries. status_deque: deque used to share the latest status. Returns: the API response for the status of a job, as a dict that contains at least the keys ``status`` and ``id``. Raises: WebsocketError: if the websocket connection ended unexpectedly. WebsocketTimeoutError: if the timeout has been reached. """ url = '{}/jobs/{}/status'.format(self.websocket_url, job_id) original_timeout = timeout start_time = time.time() attempt_retry = True # By default, attempt to retry if the websocket connection closes. current_retry_attempt = 0 last_status = None websocket = None while current_retry_attempt <= retries: try: websocket = yield from self._connect(url) # Read messages from the server until the connection is closed or # a timeout has been reached. while True: try: with warnings.catch_warnings(): # Suppress websockets deprecation warnings until the fix is available warnings.filterwarnings("ignore", category=DeprecationWarning) if timeout: response_raw = yield from asyncio.wait_for( websocket.recv(), timeout=timeout) # Decrease the timeout. timeout = original_timeout - (time.time() - start_time) else: response_raw = yield from websocket.recv() logger.debug('Received message from websocket: %s', response_raw) response = WebsocketResponseMethod.from_bytes(response_raw) last_status = response.data # Successfully received and parsed a message, reset retry counter. current_retry_attempt = 0 job_status = response.data.get('status') if (job_status and ApiJobStatus(job_status) in API_JOB_FINAL_STATES): return last_status if timeout and timeout <= 0: raise WebsocketTimeoutError('Timeout reached') # Share the new status. if status_deque is not None: status_deque.append(last_status) except (futures.TimeoutError, asyncio.TimeoutError): # Timeout during our wait. raise WebsocketTimeoutError('Timeout reached') from None except ConnectionClosed as ex: # From the API: # 4001: closed due to an internal errors # 4002: closed on purpose (no more updates to send) # 4003: closed due to job not found. message = 'Unexpected error' if ex.code == 4001: message = 'Internal server error' elif ex.code == 4002: return last_status # type: ignore[return-value] elif ex.code == 4003: attempt_retry = False # No point in retrying. message = 'Job id not found' raise WebsocketError('Connection with websocket closed ' 'unexpectedly: {}(status_code={})' .format(message, ex.code)) from ex except WebsocketError as ex: logger.info('A websocket error occurred: %s', ex) # Specific `WebsocketError` exceptions that are not worth retrying. if isinstance(ex, (WebsocketTimeoutError, WebsocketIBMQProtocolError)): raise ex current_retry_attempt = current_retry_attempt + 1 if (current_retry_attempt > retries) or (not attempt_retry): raise ex # Sleep, and then `continue` with retrying. backoff_time = self._backoff_time(backoff_factor, current_retry_attempt) logger.info('Retrying get_job_status via websocket after %s seconds: ' 'Attempt #%s.', backoff_time, current_retry_attempt) yield from asyncio.sleep(backoff_time) # Block asyncio loop for given backoff time. continue # Continues next iteration after `finally` block. finally: with warnings.catch_warnings(): # Suppress websockets deprecation warnings until the fix is available warnings.filterwarnings("ignore", category=DeprecationWarning) if websocket is not None: yield from websocket.close() # Execution should not reach here, sanity check. raise WebsocketError('Failed to establish a websocket ' 'connection after {} retries.'.format(retries)) def _backoff_time(self, backoff_factor: float, current_retry_attempt: int) -> float: """Calculate the backoff time to sleep for. Exponential backoff time formula: {backoff_factor} * (2 ** (current_retry_attempt - 1)) Args: backoff_factor: backoff factor, in seconds. current_retry_attempt: current number of retry attempts. Returns: The number of seconds to sleep for, before a retry attempt is made. """ backoff_time = backoff_factor * (2 ** (current_retry_attempt - 1)) return min(self.BACKOFF_MAX, backoff_time) def _authentication_message(self) -> 'WebsocketAuthenticationMessage': """Return the message used for authenticating against the server.""" return WebsocketAuthenticationMessage(type_='authentication', data=self.access_token) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/clients/account.py0000664000372000037200000004241713616666011027154 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2018, 2020. # # 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. """Client for accessing an individual IBM Q Experience account.""" import asyncio import logging import time from collections import deque from typing import List, Dict, Any, Optional # Disabled unused-import because datetime is used only for type hints. from datetime import datetime # pylint: disable=unused-import from qiskit.providers.ibmq.apiconstants import (API_JOB_FINAL_STATES, ApiJobStatus, ApiJobShareLevel) from ..exceptions import (RequestsApiError, WebsocketError, WebsocketTimeoutError, UserTimeoutExceededError) from ..rest import Api from ..session import RetrySession from ..exceptions import ApiIBMQProtocolError from .base import BaseClient from .websocket import WebsocketClient logger = logging.getLogger(__name__) class AccountClient(BaseClient): """Client for accessing an individual IBM Q Experience account. This client provides access to an individual IBM Q hub/group/project. """ def __init__( self, access_token: str, project_url: str, websockets_url: str, use_websockets: bool, **request_kwargs: Any ) -> None: """AccountClient constructor. Args: access_token: IBM Q Experience access token. project_url: IBM Q Experience URL for a specific h/g/p. websockets_url: URL for the websockets server. use_websockets: whether to use webscokets **request_kwargs: arguments for the `requests` Session. """ self.client_api = Api(RetrySession(project_url, access_token, **request_kwargs)) self.client_ws = WebsocketClient(websockets_url, access_token) self._use_websockets = use_websockets # Backend-related public functions. def list_backends(self, timeout: Optional[float] = None) -> List[Dict[str, Any]]: """Return the list of backends. Args: timeout: number of seconds to wait for the request. Returns: a list of backends. """ return self.client_api.backends(timeout=timeout) def backend_status(self, backend_name: str) -> Dict[str, Any]: """Return the status of a backend. Args: backend_name: the name of the backend. Returns: backend status. """ return self.client_api.backend(backend_name).status() def backend_properties( self, backend_name: str, datetime: Optional[datetime] = None ) -> Dict[str, Any]: """Return the properties of a backend. Args: backend_name: the name of the backend. datetime: datetime for additional filtering of backend properties. Returns: backend properties. """ # pylint: disable=redefined-outer-name return self.client_api.backend(backend_name).properties(datetime=datetime) def backend_pulse_defaults(self, backend_name: str) -> Dict: """Return the pulse defaults of a backend. Args: backend_name: the name of the backend. Returns: backend pulse defaults. """ return self.client_api.backend(backend_name).pulse_defaults() def backend_job_limit(self, backend_name: str) -> Dict[str, Any]: """Return the job limit for the backend. Args: backend_name: the name of the backend. Returns: backend job limit. """ return self.client_api.backend(backend_name).job_limit() # Jobs-related public functions. def list_jobs_statuses( self, limit: int = 10, skip: int = 0, extra_filter: Optional[Dict[str, Any]] = None ) -> List[Dict[str, Any]]: """Return a list of statuses of jobs, with filtering and pagination. Args: limit: maximum number of items to return. skip: offset for the items to return. extra_filter: additional filtering passed to the query. Returns: a list of job statuses. """ return self.client_api.jobs(limit=limit, skip=skip, extra_filter=extra_filter) def job_submit( self, backend_name: str, qobj_dict: Dict[str, Any], use_object_storage: bool, job_name: Optional[str] = None, job_share_level: Optional[ApiJobShareLevel] = None, job_tags: Optional[List[str]] = None ) -> Dict[str, Any]: """Submit a Qobj to a device. Args: backend_name: the name of the backend. qobj_dict: the Qobj to be executed, as a dictionary. use_object_storage: `True` if object storage should be used. job_name: custom name to be assigned to the job. job_share_level: level the job should be shared at. job_tags: tags to be assigned to the job. Returns: job status. """ submit_info = None if use_object_storage: # Attempt to use object storage. try: submit_info = self._job_submit_object_storage( backend_name=backend_name, qobj_dict=qobj_dict, job_name=job_name, job_share_level=job_share_level, job_tags=job_tags) except Exception as ex: # pylint: disable=broad-except # Fall back to submitting the Qobj via POST if object storage # failed. logger.info('Submitting the job via object storage failed: ' 'retrying via regular POST upload: %s', str(ex)) logger.debug('Submitting via object storage extra info:', exc_info=True) if not submit_info: # Submit Qobj via HTTP. submit_info = self._job_submit_post( backend_name=backend_name, qobj_dict=qobj_dict, job_name=job_name, job_share_level=job_share_level, job_tags=job_tags) return submit_info def _job_submit_post( self, backend_name: str, qobj_dict: Dict[str, Any], job_name: Optional[str] = None, job_share_level: Optional[ApiJobShareLevel] = None, job_tags: Optional[List[str]] = None ) -> Dict[str, Any]: """Submit a Qobj to a device using HTTP POST. Args: backend_name: the name of the backend. qobj_dict: the Qobj to be executed, as a dictionary. job_name: custom name to be assigned to the job. job_share_level: level the job should be shared at. job_tags: tags to be assigned to the job. Returns: job status. """ # Check for the job share level. _job_share_level = job_share_level.value if job_share_level else None return self.client_api.job_submit( backend_name, qobj_dict, job_name, job_share_level=_job_share_level, job_tags=job_tags) def _job_submit_object_storage( self, backend_name: str, qobj_dict: Dict[str, Any], job_name: Optional[str] = None, job_share_level: Optional[ApiJobShareLevel] = None, job_tags: Optional[List[str]] = None ) -> Dict: """Submit a Qobj to a device using object storage. Args: backend_name: the name of the backend. qobj_dict: the Qobj to be executed, as a dictionary. job_name: custom name to be assigned to the job. job_share_level: level the job should be shared at. job_tags: tags to be assigned to the job. Returns: job status. """ # Check for the job share level. _job_share_level = job_share_level.value if job_share_level else None # Get the job via object storage. job_info = self.client_api.submit_job_object_storage( backend_name, job_name=job_name, job_share_level=_job_share_level, job_tags=job_tags) # Get the upload URL. job_id = job_info['id'] job_api = self.client_api.job(job_id) upload_url = job_api.upload_url()['url'] # Upload the Qobj to object storage. _ = job_api.put_object_storage(upload_url, qobj_dict) # Notify the API via the callback. response = job_api.callback_upload() return response['job'] def job_download_qobj(self, job_id: str, use_object_storage: bool) -> Dict: """Retrieve and return a Qobj. Args: job_id: the id of the job. use_object_storage: `True` if object storage should be used. Returns: Qobj, in dict form. """ if use_object_storage: return self._job_download_qobj_object_storage(job_id) else: return self.job_get(job_id).get('qObject', {}) def _job_download_qobj_object_storage(self, job_id: str) -> Dict: """Retrieve and return a Qobj using object storage. Args: job_id: the id of the job. Returns: Qobj, in dict form. """ job_api = self.client_api.job(job_id) # Get the download URL. download_url = job_api.download_url()['url'] # Download the result from object storage. return job_api.get_object_storage(download_url) def job_result(self, job_id: str, use_object_storage: bool) -> Dict: """Retrieve and return a job result. Args: job_id: the id of the job. use_object_storage: `True` if object storage should be used. Returns: job information. Raises: ApiIBMQProtocolError: if an unexpected result is received from the server. """ if use_object_storage: return self._job_result_object_storage(job_id) try: return self.job_get(job_id)['qObjectResult'] except KeyError as err: raise ApiIBMQProtocolError(str(err)) def _job_result_object_storage(self, job_id: str) -> Dict: """Retrieve and return a result using object storage. Args: job_id: the id of the job. Returns: job information. """ job_api = self.client_api.job(job_id) # Get the download URL. download_url = job_api.result_url()['url'] # Download the result from object storage. result_response = job_api.get_object_storage(download_url) # Notify the API via the callback try: _ = job_api.callback_download() except (RequestsApiError, ValueError) as ex: logger.warning("An error occurred while sending download completion acknowledgement: " "%s", ex) return result_response def job_get( self, job_id: str ) -> Dict[str, Any]: """Return information about a job. Args: job_id: the id of the job. Returns: job information. """ return self.client_api.job(job_id).get() def job_status(self, job_id: str) -> Dict[str, Any]: """Return the status of a job. Args: job_id: the id of the job. Returns: job status. Raises: ApiIBMQProtocolError: if an unexpected result is received from the server. """ return self.client_api.job(job_id).status() def job_final_status( self, job_id: str, timeout: Optional[float] = None, wait: float = 5, status_deque: Optional[deque] = None ) -> Dict[str, Any]: """Wait until the job progress to a final state. Args: job_id: the id of the job timeout: seconds to wait for job. If None, wait indefinitely. wait: seconds between queries. status_deque: deque used to share the latest status. Returns: job status. Raises: UserTimeoutExceededError: if the job does not return results before a specified timeout. ApiIBMQProtocolError: if an unexpected result is received from the server. """ status_response = None # Attempt to use websocket if available. if self._use_websockets: start_time = time.time() try: status_response = self._job_final_status_websocket( job_id=job_id, timeout=timeout, status_deque=status_deque) except WebsocketTimeoutError as ex: logger.info('Timeout checking job status using websocket, ' 'retrying using HTTP: %s', ex) except (RuntimeError, WebsocketError) as ex: logger.info('Error checking job status using websocket, ' 'retrying using HTTP: %s', ex) # Adjust timeout for HTTP retry. if timeout is not None: timeout -= (time.time() - start_time) if not status_response: # Use traditional http requests if websocket not available or failed. status_response = self._job_final_status_polling( job_id, timeout, wait, status_deque) return status_response def _job_final_status_websocket( self, job_id: str, timeout: Optional[float] = None, status_deque: Optional[deque] = None ) -> Dict[str, Any]: """Return the final status of a job via websocket. Args: job_id: the id of the job. timeout: seconds to wait for job. If None, wait indefinitely. status_deque: deque used to share the latest status. Returns: job status. Raises: RuntimeError: if an unexpected error occurred while getting the event loop. WebsocketError: if the websocket connection ended unexpectedly. WebsocketTimeoutError: if the timeout has been reached. """ # As mentioned in `websocket.py`, in jupyter we need to use # `nest_asyncio` to allow nested event loops. try: loop = asyncio.get_event_loop() except RuntimeError as ex: # Event loop may not be set in a child thread. if 'There is no current event loop' in str(ex): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) else: raise return loop.run_until_complete( self.client_ws.get_job_status(job_id, timeout=timeout, status_deque=status_deque)) def _job_final_status_polling( self, job_id: str, timeout: Optional[float] = None, wait: float = 5, status_deque: Optional[deque] = None ) -> Dict[str, Any]: """Return the final status of a job via polling. Args: job_id: the id of the job. timeout: seconds to wait for job. If None, wait indefinitely. wait: seconds between queries. status_deque: deque used to share the latest status. Returns: job status. Raises: UserTimeoutExceededError: if the user specified timeout has been exceeded. """ start_time = time.time() status_response = self.job_status(job_id) while ApiJobStatus(status_response['status']) not in API_JOB_FINAL_STATES: # Share the new status. if status_deque is not None: status_deque.append(status_response) elapsed_time = time.time() - start_time if timeout is not None and elapsed_time >= timeout: raise UserTimeoutExceededError( 'Timeout while waiting for job {}'.format(job_id)) logger.info('API job status = %s (%d seconds)', status_response['status'], elapsed_time) time.sleep(wait) status_response = self.job_status(job_id) return status_response def job_properties(self, job_id: str) -> Dict: """Return the backend properties of a job. Args: job_id: the id of the job. Returns: backend properties. """ return self.client_api.job(job_id).properties() def job_cancel(self, job_id: str) -> Dict[str, Any]: """Submit a request for cancelling a job. Args: job_id: the id of the job. Returns: job cancellation response. """ return self.client_api.job(job_id).cancel() qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/clients/version.py0000664000372000037200000000315613616666011027202 0ustar travistravis00000000000000# -*- 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. """Client for determining the version of an IBM Q Experience service.""" from typing import Dict, Union, Any from ..session import RetrySession from ..rest.version_finder import VersionFinder from .base import BaseClient class VersionClient(BaseClient): """Client for determining the version of an IBM Q Experience service.""" def __init__(self, url: str, **request_kwargs: Any) -> None: """VersionClient constructor. Args: url: URL for the service. **request_kwargs: arguments for the `requests` Session. """ self.client_version_finder = VersionFinder( RetrySession(url, **request_kwargs)) def version(self) -> Dict[str, Union[bool, str]]: """Return the version info. Returns: a dict with information about the API version, with the following keys: * `new_api` (bool): whether the new API is being used And the following optional keys: * `api-*` (str): the versions of each individual API component """ return self.client_version_finder.version() qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/clients/auth.py0000664000372000037200000001363113616666011026455 0ustar travistravis00000000000000# -*- 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. """Client for accessing authentication features of IBM Q Experience.""" from typing import Dict, List, Optional, Any from requests.exceptions import RequestException from ..exceptions import AuthenticationLicenseError, RequestsApiError from ..rest import Api, Auth from ..session import RetrySession from .base import BaseClient class AuthClient(BaseClient): """Client for accessing authentication features of IBM Q Experience.""" def __init__(self, api_token: str, auth_url: str, **request_kwargs: Any) -> None: """AuthClient constructor. Args: api_token: IBM Q Experience API token. auth_url: URL for the authentication service. **request_kwargs: arguments for the `requests` Session. """ self.api_token = api_token self.auth_url = auth_url self._service_urls = {} # type: ignore[var-annotated] self.client_auth = Auth(RetrySession(auth_url, **request_kwargs)) self.client_api = self._init_service_clients(**request_kwargs) def _init_service_clients(self, **request_kwargs: Any) -> Api: """Initialize the clients used for communicating with the API and ws. Args: **request_kwargs: arguments for the `requests` Session. Returns: client for the api server. """ # Request an access token. access_token = self._request_access_token() # Use the token for the next auth server requests. self.client_auth.session.access_token = access_token self._service_urls = self.user_urls() # Create the api server client, using the access token. client_api = Api(RetrySession(self._service_urls['http'], access_token, **request_kwargs)) return client_api def _request_access_token(self) -> str: """Request a new access token from the API authentication server. Returns: access token. Raises: AuthenticationLicenseError: if the user hasn't accepted the license agreement. RequestsApiError: if the request failed. """ try: response = self.client_auth.login(self.api_token) return response['id'] except RequestsApiError as ex: # Get the original exception that raised. original_exception = ex.__cause__ if isinstance(original_exception, RequestException): # Get the response from the original request exception. error_response = original_exception.response # pylint: disable=no-member if error_response is not None and error_response.status_code == 401: try: error_code = error_response.json()['error']['name'] if error_code == 'ACCEPT_LICENSE_REQUIRED': message = error_response.json()['error']['message'] raise AuthenticationLicenseError(message) except (ValueError, KeyError): # the response did not contain the expected json. pass raise # User account-related public functions. def user_urls(self) -> Dict[str, str]: """Retrieve the API URLs from the authentication server. Returns: a dict with the base URLs for the services. Currently supported keys: * ``http``: the API URL for http communication. * ``ws``: the API URL for websocket communication. """ response = self.client_auth.user_info() return response['urls'] def user_hubs(self) -> List[Dict[str, str]]: """Retrieve the hubs available to the user. The first entry in the list will be the default one, as indicated by the API (by having `isDefault` in all hub, group, project fields). Returns: a list of dicts with the hubs, which contains the keys `hub`, `group`, `project`. """ response = self.client_api.hubs() hubs = [] # type: ignore[var-annotated] for hub in response: hub_name = hub['name'] for group_name, group in hub['groups'].items(): for project_name, project in group['projects'].items(): entry = {'hub': hub_name, 'group': group_name, 'project': project_name} # Move to the top if it is the default h/g/p. if project.get('isDefault'): hubs.insert(0, entry) else: hubs.append(entry) return hubs # Miscellaneous public functions. def api_version(self) -> Dict[str, str]: """Return the version of the API. Returns: versions of the API components. """ return self.client_api.version() def current_access_token(self) -> Optional[str]: """Return the current access token. Returns: the access token in use. """ return self.client_auth.session.access_token def current_service_urls(self) -> Dict[str, str]: """Return the current service URLs. Returns: a dict with the base URLs for the services, in the same format as `.user_urls()`. """ return self._service_urls qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/clients/base.py0000664000372000037200000000116213616666011026422 0ustar travistravis00000000000000# -*- 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. """Base Client for accessing IBM Q Experience.""" class BaseClient: """Abstract class for clients.""" pass qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/clients/__init__.py0000664000372000037200000000131113616666011027243 0ustar travistravis00000000000000# -*- 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. """IBM Q Experience API clients.""" from .base import BaseClient from .account import AccountClient from .auth import AuthClient from .version import VersionClient from .websocket import WebsocketClient qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/exceptions.py0000664000372000037200000000302513616666011026230 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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 the IBM Q Experience API.""" from ..exceptions import IBMQError class ApiError(IBMQError): """Generic IBM Q API error.""" pass class RequestsApiError(ApiError): """Exception re-raising a RequestException.""" pass class WebsocketError(ApiError): """Exceptions related to websockets.""" pass class WebsocketIBMQProtocolError(WebsocketError): """Exceptions related to IBM Q protocol error.""" pass class WebsocketAuthenticationError(WebsocketError): """Exception caused during websocket authentication.""" pass class WebsocketTimeoutError(WebsocketError): """Timeout during websocket communication.""" pass class AuthenticationLicenseError(ApiError): """Exception due to user not accepting latest license agreement via web.""" pass class ApiIBMQProtocolError(ApiError): """Exception related to IBM Q API protocol error.""" pass class UserTimeoutExceededError(ApiError): """Exceptions related to exceeding user defined timeout.""" pass qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/__init__.py0000664000372000037200000000106113616666011025604 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """IBM Q Experience API connector and utilities.""" qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/api/session.py0000664000372000037200000002062013616666011025532 0ustar travistravis00000000000000# -*- 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. """Session customized for IBM Q Experience access.""" import os from typing import Dict, Optional, Any, Tuple, Union from requests import Session, RequestException, Response from requests.adapters import HTTPAdapter from requests.auth import AuthBase from urllib3.util.retry import Retry from .exceptions import RequestsApiError from ..version import __version__ as ibmq_provider_version STATUS_FORCELIST = ( 502, # Bad Gateway 503, # Service Unavailable 504, # Gateway Timeout ) CLIENT_APPLICATION = 'ibmqprovider/' + ibmq_provider_version CUSTOM_HEADER_ENV_VAR = 'QE_CUSTOM_CLIENT_APP_HEADER' class PostForcelistRetry(Retry): """Custom Retry that performs retry on POST errors in the forcelist. Custom `urllib3.Retry` that allows retrying in `POST` requests *only* when the status code returned belongs to the `STATUS_FORCELIST`. While `POST` requests are recommended not to be retried due to not being idempotent, the IBM Q API guarantees that retrying on specific 5xx errors is safe. """ def is_retry( self, method: str, status_code: int, has_retry_after: bool = False ) -> bool: if method.upper() == 'POST' and status_code in self.status_forcelist: return True return super().is_retry(method, status_code, has_retry_after) class RetrySession(Session): """Session with retry and handling of IBM Q parameters. Custom session for use with IBM Q, that includes a retry mechanism based on urllib3 and handling of specific parameters based on ``requests.Session``. """ def __init__( self, base_url: str, access_token: Optional[str] = None, retries_total: int = 5, retries_connect: int = 3, backoff_factor: float = 0.5, verify: bool = True, proxies: Optional[Dict[str, str]] = None, auth: Optional[AuthBase] = None, timeout: Tuple[float, Union[float, None]] = (5.0, None) ) -> None: """RetrySession constructor. Args: base_url: base URL for the session's requests. access_token: access token. retries_total: number of total retries for the requests. retries_connect: number of connect retries for the requests. backoff_factor: backoff factor between retry attempts. verify: enable SSL verification. proxies: proxy URLs mapped by protocol. auth: authentication handler. timeout: timeout for the requests, in the form (connection_timeout, total_timeout). """ super().__init__() self.base_url = base_url self._access_token = access_token self.access_token = access_token self._initialize_retry(retries_total, retries_connect, backoff_factor) self._initialize_session_parameters(verify, proxies or {}, auth) self._timeout = timeout def __del__(self) -> None: """RetrySession destructor. Closes the session.""" self.close() @property def access_token(self) -> Optional[str]: """Return the session access token.""" return self._access_token @access_token.setter def access_token(self, value: Optional[str]) -> None: """Set the session access token.""" self._access_token = value if value: self.headers.update({'X-Access-Token': value}) # type: ignore[attr-defined] else: self.headers.pop('X-Access-Token', None) # type: ignore[attr-defined] def _initialize_retry( self, retries_total: int, retries_connect: int, backoff_factor: float ) -> None: """Set the Session retry policy. Args: retries_total: number of total retries for the requests. retries_connect: number of connect retries for the requests. backoff_factor: backoff factor between retry attempts. """ retry = PostForcelistRetry( total=retries_total, connect=retries_connect, backoff_factor=backoff_factor, status_forcelist=STATUS_FORCELIST, ) retry_adapter = HTTPAdapter(max_retries=retry) self.mount('http://', retry_adapter) self.mount('https://', retry_adapter) def _initialize_session_parameters( self, verify: bool, proxies: Dict[str, str], auth: Optional[AuthBase] = None ) -> None: """Set the Session parameters and attributes. Args: verify: enable SSL verification. proxies: proxy URLs mapped by protocol. auth: authentication handler. """ client_app_header = CLIENT_APPLICATION # Append custom header to the end if specified custom_header = os.getenv(CUSTOM_HEADER_ENV_VAR) if custom_header: client_app_header += "/" + custom_header self.headers.update({'X-Qx-Client-Application': client_app_header}) self.auth = auth self.proxies = proxies or {} self.verify = verify def request( # type: ignore[override] self, method: str, url: str, bare: bool = False, **kwargs: Any ) -> Response: """Constructs a Request, prepending the base url. Args: method: method for the new `Request` object. url: URL for the new `Request` object. bare: if `True`, do not send IBM Q specific information (access token) in the request or modify the `url`. kwargs: additional arguments for the request. Returns: Response object. Raises: RequestsApiError: if the request failed. """ # pylint: disable=arguments-differ if bare: final_url = url # Explicitly pass `None` as the `access_token` param, disabling it. params = kwargs.get('params', {}) params.update({'access_token': None}) kwargs.update({'params': params}) else: final_url = self.base_url + url # Add a timeout to the connection for non-proxy connections. if not self.proxies: kwargs.update({'timeout': self._timeout}) try: response = super().request(method, final_url, **kwargs) response.raise_for_status() except RequestException as ex: # Wrap the requests exceptions into a IBM Q custom one, for # compatibility. message = str(ex) if ex.response is not None: try: error_json = ex.response.json()['error'] message += ". {}, Error code: {}.".format( error_json['message'], error_json['code']) except (ValueError, KeyError): # the response did not contain the expected json. pass if self.access_token: message = message.replace(self.access_token, '...') # Modify the original message on the chained exceptions. self._modify_chained_exception_messages(ex) raise RequestsApiError(message) from ex return response def _modify_chained_exception_messages(self, exc: BaseException) -> None: if exc.__cause__: self._modify_chained_exception_messages(exc.__cause__) elif exc.__context__: self._modify_chained_exception_messages(exc.__context__) # Loop through args, attempt to replace access token if string. modified_args = [] for arg in exc.args: exc_message = arg if isinstance(exc_message, str): exc_message = exc_message.replace(self.access_token, '...') modified_args.append(exc_message) exc.args = tuple(modified_args) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/version.py0000664000372000037200000000133113616666011024761 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Contains the package version.""" import os ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(ROOT_DIR, "VERSION.txt"), "r") as version_file: __version__ = version_file.read().strip() qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/utils/0000775000372000037200000000000013616666025024071 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/utils/qobj_utils.py0000664000372000037200000000437513616666011026622 0ustar travistravis00000000000000# -*- 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. """Utilities related to Qobj.""" from typing import Dict, Any, Optional from qiskit.qobj import QobjHeader, Qobj def _serialize_noise_model(config: Dict[str, Any]) -> Dict[str, Any]: """Traverse the dictionary looking for noise_model keys and apply a transformation so it can be serialized. Args: config: The dictionary to traverse Returns: The transformed dictionary """ for k, v in config.items(): if isinstance(config[k], dict): _serialize_noise_model(config[k]) else: if k == 'noise_model': try: config[k] = v.to_dict(serializable=True) except AttributeError: # if .to_dict() fails is probably because the noise_model # has been already transformed elsewhere pass return config def update_qobj_config( qobj: Qobj, backend_options: Optional[Dict] = None, noise_model: Any = None ) -> Qobj: """Update a Qobj configuration from options and noise model. Args: qobj: description of job backend_options: backend options noise_model: noise model Returns: qobj. """ config = qobj.config.to_dict() # Append backend options to configuration. if backend_options: for key, val in backend_options.items(): config[key] = val # Append noise model to configuration. Overwrites backend option if noise_model: config['noise_model'] = noise_model # Look for noise_models in the config, and try to transform them config = _serialize_noise_model(config) # Update the Qobj configuration. qobj.config = QobjHeader.from_dict(config) return qobj qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/utils/fields.py0000664000372000037200000000461413616666011025711 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 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. """Custom fields for validation.""" import enum from typing import Dict, Any from qiskit.validation import ModelTypeValidator, BaseModel from .utils import to_python_identifier class Enum(ModelTypeValidator): """Field for enums.""" default_error_messages = { 'invalid': '"{input}" cannot be parsed as a {enum_cls}.', 'format': '"{input}" cannot be formatted as a {enum_cls}.', } def __init__(self, enum_cls: enum.EnumMeta, *args: Any, **kwargs: Any) -> None: self.valid_types = (enum_cls,) self.valid_strs = [elem.value for elem in enum_cls] # type: ignore[var-annotated] self.enum_cls = enum_cls super().__init__(*args, **kwargs) def _serialize( # type: ignore[return] self, value: Any, attr: str, obj: BaseModel, **_: Any ) -> str: try: return value.value except AttributeError: # TODO: change to self.make_error_serialize after #3228 self.make_error('format', input=value, enum_cls=self.enum_cls) def _deserialize( # type: ignore[return] self, value: Any, attr: str, data: Dict[str, Any], **_: Any ) -> enum.EnumMeta: try: return self.enum_cls(value) except ValueError: self.fail('invalid', input=value, enum_cls=self.enum_cls) def map_field_names(mapper: dict, data: dict) -> dict: """Rename selected fields due to name clashes, and convert from camel-case the rest of the fields. """ rename_map = {} for field_name in data: if field_name in mapper: rename_map[field_name] = mapper[field_name] else: rename_map[field_name] = to_python_identifier(field_name) for old_name, new_name in rename_map.items(): data[new_name] = data.pop(old_name) return data qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/utils/utils.py0000664000372000037200000000333713616666011025604 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 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. """General utility functions.""" import re import keyword from typing import List, Optional, Type def to_python_identifier(name: str) -> str: """Convert a name to a valid Python identifier. Args: name: Name to be converted. Returns: Name that is a valid Python identifier. """ # Python identifiers can only contain alphanumeric characters # and underscores and cannot start with a digit. pattern = re.compile(r"\W|^(?=\d)", re.ASCII) if not name.isidentifier(): name = re.sub(pattern, '_', name) # Convert to snake case name = re.sub('((?<=[a-z0-9])[A-Z]|(?!^)(? None: """Validates input job tags. Args: job_tags: Job tags to be validated. exception: Exception to raise if the tags are invalid. Raises: Exception: If the job tags are invalid. """ if job_tags and (not isinstance(job_tags, list) or not all(isinstance(tag, str) for tag in job_tags)): raise exception("job_tags needs to be a list or strings.") qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/utils/__init__.py0000664000372000037200000000122213616666011026172 0ustar travistravis00000000000000# -*- 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. """Utilities related to the IBMQ Provider.""" from .qobj_utils import update_qobj_config from .utils import to_python_identifier, validate_job_tags qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/backendjoblimit.py0000664000372000037200000000263513616666011026425 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2020. # # 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. """Job limit information related to a backend.""" from typing import Any from qiskit.validation import BaseModel, bind_schema from .api.rest.validation import BackendJobLimitResponseSchema @bind_schema(BackendJobLimitResponseSchema) class BackendJobLimit(BaseModel): """Jobs limit for a backend.""" def __init__(self, maximum_jobs: int, running_jobs: int, **kwargs: Any) -> None: """Creates a new BackendJobLimit instance. Args: maximum_jobs: maximum number of concurrent jobs this account is allowed to submit to this backend with this provider at a time. running_jobs: current number of active jobs on this backend with this provider. kwargs: additional attributes that will be added as instance members. """ self.maximum_jobs = maximum_jobs self.active_jobs = running_jobs super().__init__(**kwargs) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/__init__.py0000664000372000037200000000324013616666011025034 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Backends provided by IBM Quantum Experience.""" from typing import List from qiskit.exceptions import QiskitError from .ibmqfactory import IBMQFactory from .ibmqbackend import IBMQBackend, BaseBackend from .job import IBMQJob from .managed import IBMQJobManager from .version import __version__ # Global instance to be used as the entry point for convenience. IBMQ = IBMQFactory() def least_busy(backends: List[BaseBackend]) -> BaseBackend: """Return the least busy backend from a list. Return the least busy available backend for those that have a `pending_jobs` in their `status`. Backends such as local backends that do not have this are not considered. Args: backends: backends to choose from Returns: the least busy backend Raises: QiskitError: if passing a list of backend names that is either empty or none have attribute ``pending_jobs`` """ try: return min([b for b in backends if b.status().operational], key=lambda b: b.status().pending_jobs) except (ValueError, TypeError): raise QiskitError("Can only find least_busy backend from a non-empty list.") qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/0000775000372000037200000000000013616666025025226 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/environ.py0000664000372000037200000000325613616666011027261 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Utilities for reading credentials from environment variables.""" import os from collections import OrderedDict from typing import Dict from .credentials import Credentials, HubGroupProject # Dictionary that maps `ENV_VARIABLE_NAME` to credential parameter. VARIABLES_MAP = { 'QE_TOKEN': 'token', 'QE_URL': 'url', 'QE_HUB': 'hub', 'QE_GROUP': 'group', 'QE_PROJECT': 'project' } def read_credentials_from_environ() -> Dict[HubGroupProject, Credentials]: """Read the environment variables and return its credentials. Returns: dictionary with the credentials, in the form:: {credentials_unique_id: Credentials} """ # The token is the only required parameter. if not (os.getenv('QE_TOKEN') and os.getenv('QE_URL')): return OrderedDict() # Build the credentials based on environment variables. credentials_dict = {} for envar_name, credential_key in VARIABLES_MAP.items(): if os.getenv(envar_name): credentials_dict[credential_key] = os.getenv(envar_name) credentials = Credentials(**credentials_dict) # type: ignore[arg-type] return OrderedDict({credentials.unique_id(): credentials}) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/credentials.py0000664000372000037200000001521413616666011030073 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Model for representing IBM Q credentials.""" import re import warnings from typing import Dict, Tuple, Optional, Any from requests_ntlm import HttpNtlmAuth from .hubgroupproject import HubGroupProject # Regex that matches a IBMQ URL with hub information REGEX_IBMQ_HUBS = ( '(?Phttp[s]://.+/api)' '/Hubs/(?P[^/]+)/Groups/(?P[^/]+)/Projects/(?P[^/]+)' ) # Template for creating an IBMQ URL with hub information TEMPLATE_IBMQ_HUBS = '{prefix}/Network/{hub}/Groups/{group}/Projects/{project}' class Credentials: """IBM Q account credentials. Note that, by convention, two credentials that have the same hub, group and project (regardless of other attributes) are considered equivalent. The `unique_id()` returns the unique identifier. """ def __init__( self, token: str, url: str, websockets_url: Optional[str] = None, hub: Optional[str] = None, group: Optional[str] = None, project: Optional[str] = None, proxies: Optional[Dict] = None, verify: bool = True ) -> None: """Return new set of credentials. Args: token: Quantum Experience or IBMQ API token. url: URL for Quantum Experience or IBMQ. websockets_url: URL for websocket server. hub: the hub used for IBMQ. group: the group used for IBMQ. project: the project used for IBMQ. proxies: proxy configuration for the API. verify: if False, ignores SSL certificates errors Note: `hub`, `group` and `project` are stored as attributes for convenience, but their usage in the API is deprecated. The new-style URLs (that includes these parameters as part of the url string, and is automatically set during instantiation) should be used when communicating with the API. """ self.token = token (self.url, self.base_url, self.hub, self.group, self.project) = _unify_ibmq_url( url, hub, group, project) self.websockets_url = websockets_url self.proxies = proxies or {} self.verify = verify # Normalize proxy urls. self._prepend_protocol_if_needed() def is_ibmq(self) -> bool: """Return whether the credentials represent a IBMQ account.""" return all([self.hub, self.group, self.project]) def __eq__(self, other: object) -> bool: return self.__dict__ == other.__dict__ def unique_id(self) -> HubGroupProject: """Return a value that uniquely identifies these credentials. By convention, we assume (hub, group, project) is always unique. Returns: the (hub, group, project) tuple. """ return HubGroupProject(self.hub, self.group, self.project) def connection_parameters(self) -> Dict[str, Any]: """Return a dict of kwargs in the format expected by `requests`. Returns: a dict with connection-related arguments in the format expected by `requests`. The following keys can be present: `proxies`, `verify`, `auth`. """ request_kwargs = { 'verify': self.verify } if self.proxies: if 'urls' in self.proxies: request_kwargs['proxies'] = self.proxies['urls'] if 'username_ntlm' in self.proxies and 'password_ntlm' in self.proxies: request_kwargs['auth'] = HttpNtlmAuth( self.proxies['username_ntlm'], self.proxies['password_ntlm'] ) return request_kwargs def _prepend_protocol_if_needed(self) -> None: """Prepend the proxy URLs with protocol if needed.""" if 'urls' not in self.proxies: return warning_issued = False for key, url in self.proxies['urls'].items(): colon_pos = url.find(':') if colon_pos < 0 or re.match(r'[0-9]*$', url[colon_pos+1:]): # If colon not found OR everything after colon are digits (i.e. port number). if not warning_issued: warnings.warn("Proxy URLs without protocols (e.g. 'http://') " "will no longer be supported in the future release.", DeprecationWarning, stacklevel=4) warning_issued = True prepend_str = 'http:' if url[:2] != '//': prepend_str += '//' self.proxies['urls'][key] = prepend_str + url def _unify_ibmq_url( url: str, hub: Optional[str] = None, group: Optional[str] = None, project: Optional[str] = None ) -> Tuple[str, str, Optional[str], Optional[str], Optional[str]]: """Return a new-style set of credential values (url and hub parameters). Args: url: URL for Quantum Experience or IBM Q. hub: the hub used for IBM Q. group: the group used for IBM Q. project: the project used for IBM Q. Returns: tuple[url, base_url, hub, group, token]: * url: new-style Quantum Experience or IBM Q URL (the hub, group and project included in the URL). * base_url: base URL for the API, without hub/group/project. * hub: the hub used for IBM Q. * group: the group used for IBM Q. * project: the project used for IBM Q. """ # Check if the URL is "new style", and retrieve embedded parameters from it. regex_match = re.match(REGEX_IBMQ_HUBS, url, re.IGNORECASE) base_url = url if regex_match: base_url, hub, group, project = regex_match.groups() else: if hub and group and project: # Assume it is an IBMQ URL, and update the url. url = TEMPLATE_IBMQ_HUBS.format(prefix=url, hub=hub, group=group, project=project) else: # Cleanup the hub, group and project, without modifying the url. hub = group = project = None return url, base_url, hub, group, project qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/updater.py0000664000372000037200000001464113616666011027245 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """Helper for updating credentials from IBM Q Experience v1 to v2.""" from typing import Optional from .credentials import Credentials from .configrc import (read_credentials_from_qiskitrc, remove_credentials, store_credentials) QE_URL = 'https://quantumexperience.ng.bluemix.net/api' QCONSOLE_URL = 'https://q-console-api.mybluemix.net/api' QE2_URL = 'https://api.quantum-computing.ibm.com/api' QCONSOLE2_URL = 'https://api-qcon.quantum-computing.ibm.com/api' QE2_AUTH_URL = 'https://auth.quantum-computing.ibm.com/api' def update_credentials(force: bool = False) -> Optional[Credentials]: """Update or provide information about updating stored credentials. This function is an interactive helper to update credentials stored in disk from IBM Q Experience v1 to v2. Upon invocation, the function will inspect the credentials stored in disk and attempt to convert them to the new version, displaying the changes and asking for confirmation before overwriting the credentials. The function attempts to preserve the existing advanced parameters (such as proxies), but has limited conflict resolution handling. For complex configurations with multiple credentials, an alternative is to directly clear the existing credentials via `IBMQ.delete_accounts()` and recreate the configuration from the instructions at the IBM Q Experience site. Args: force: if `True`, disable interactive prompts and perform the changes. Returns: if the updating is possible, credentials for IBM Q Experience version 2; and `None` otherwise. """ # Get the list of stored credentials. credentials_list = list(read_credentials_from_qiskitrc().values()) new_credentials = [] hub_lines = [] warnings = [] provider_number = 1 # Parse the credentials found. for credentials in credentials_list: if is_directly_updatable(credentials): # Credentials can be updated to the new URL directly. new_credentials.append(Credentials(credentials.token, QE2_AUTH_URL, proxies=credentials.proxies, verify=credentials.verify)) else: if credentials.url == QE2_AUTH_URL: # Credential is already for auth url. warnings.append('The stored account with url "{}" is already ' 'an IBM Q Experience v2 account.'.format(credentials.url)) elif credentials.is_ibmq(): new_credentials.append(Credentials(credentials.token, QE2_AUTH_URL, proxies=credentials.proxies, verify=credentials.verify)) hub_lines.append( " provider{} = IBMQ.get_provider(hub='{}', group='{}', " "project='{}')".format(provider_number, credentials.hub, credentials.group, credentials.project)) provider_number += 1 else: # Unknown URL - do not act on it. warnings.append('The stored account with url "{}" could not be ' 'parsed.'.format(credentials.url)) # Check that the conversion can be performed. print('Found {} credentials.'.format(len(credentials_list))) if not new_credentials: print('No credentials available for updating could be found. No ' 'action will be performed.') if warnings: print('Warnings:') print('\n'.join(warnings)) return None # Check if any of the meaningful fields differ. final_credentials = new_credentials[0] tuples = [(credentials.token, credentials.proxies, credentials.verify) for credentials in new_credentials] if not all(field_tuple == tuples[0] for field_tuple in tuples): warnings.append('Multiple credentials found with different settings. The ' 'conversion will use the settings from the first ' 'IBM Q Experience v1 account found.') # Print a summary of the changes. print('The credentials stored will be replaced with a single entry with ' 'token "{}" and the new IBM Q Experience v2 URL ({}).'.format( final_credentials.token, QE2_AUTH_URL)) if final_credentials.proxies: print('The existing proxy configuration will be preserved.') if warnings: print('\nWarnings:') print('\n'.join(warnings)) print('\nIn order to access the provider, please use the new ' '"IBMQ.get_provider()" methods:') print('\n provider0 = IBMQ.load_account()') if hub_lines: print('\n'.join(hub_lines)) print('\nNote you need to update your programs in order to retrieve ' 'backends from a specific provider directly:') print('\n backends = provider0.backends()') print(" backend = provider0.get_backend('ibmq_qasm_simulator')") # Ask for confirmation from the user. if not force: confirmation = input('\nUpdate the credentials? [y/N]: ') if confirmation not in ('y', 'Y'): return None # Proceed with overwriting the credentials. for credentials in credentials_list: remove_credentials(credentials) store_credentials(final_credentials) return final_credentials def is_directly_updatable(credentials: Credentials) -> bool: """Returns `True` if credentials can be updated directly.""" if credentials.base_url == QE_URL: return True if credentials.base_url in (QCONSOLE_URL, QE2_URL, QCONSOLE2_URL): if credentials.base_url == credentials.url: return True return False qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/exceptions.py0000664000372000037200000000170013616666011027752 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """Exception for the Credentials module.""" from ..exceptions import IBMQError class CredentialsError(IBMQError): """Base class for errors raised during credential management.""" pass class InvalidCredentialsFormatError(CredentialsError): """Error raised when the credentials are in an invalid format.""" pass class CredentialsNotFoundError(CredentialsError): """Error raised when the credentials are not found.""" pass qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/hubgroupproject.py0000664000372000037200000000130313616666011031012 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """Model for representing hub/group/project tuples.""" from collections import namedtuple HubGroupProject = namedtuple('HubGroupProject', ['hub', 'group', 'project']) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/__init__.py0000664000372000037200000000527513616666011027343 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Utilities for working with credentials for the IBMQ package.""" from collections import OrderedDict from typing import Dict, Optional import logging from .credentials import Credentials, HubGroupProject from .exceptions import CredentialsError from .configrc import read_credentials_from_qiskitrc, store_credentials from .environ import read_credentials_from_environ from .qconfig import read_credentials_from_qconfig logger = logging.getLogger(__name__) def discover_credentials( qiskitrc_filename: Optional[str] = None ) -> Dict[HubGroupProject, Credentials]: """Automatically discover credentials for IBM Q. This method looks for credentials in the following locations, in order, and returning as soon as credentials are found:: 1. in the `Qconfig.py` file in the current working directory. 2. in the environment variables. 3. in the `qiskitrc` configuration file Args: qiskitrc_filename: location for the `qiskitrc` configuration file. If `None`, defaults to `{HOME}/.qiskitrc/qiskitrc`. Returns: dictionary with the contents of the configuration file, with the form:: {credentials_unique_id: Credentials} """ credentials = OrderedDict() # type: ignore[var-annotated] # dict[str:function] that defines the different locations for looking for # credentials, and their precedence order. readers = OrderedDict([ ('qconfig', (read_credentials_from_qconfig, {})), ('environment variables', (read_credentials_from_environ, {})), ('qiskitrc', (read_credentials_from_qiskitrc, {'filename': qiskitrc_filename})) ]) # Attempt to read the credentials from the different sources. for display_name, (reader_function, kwargs) in readers.items(): try: credentials = reader_function(**kwargs) # type: ignore[arg-type] logger.info('Using credentials from %s', display_name) if credentials: break except CredentialsError as ex: logger.warning( 'Automatic discovery of %s credentials failed: %s', display_name, str(ex)) return credentials qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/qconfig.py0000664000372000037200000000515613616666011027230 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """Utilities for reading credentials from the deprecated `Qconfig.py` file.""" import os from collections import OrderedDict from typing import Dict from importlib.util import module_from_spec, spec_from_file_location from .credentials import Credentials, HubGroupProject from .exceptions import InvalidCredentialsFormatError DEFAULT_QCONFIG_FILE = 'Qconfig.py' QE_URL = 'https://quantumexperience.ng.bluemix.net/api' def read_credentials_from_qconfig() -> Dict[HubGroupProject, Credentials]: """Read a `QConfig.py` file and return its credentials. Returns: dictionary with the credentials, in the form:: {credentials_unique_id: Credentials} Raises: InvalidCredentialsFormatError: if the Qconfig.py was not parseable. Please note that this exception is not raised if the file does not exist (instead, an empty dict is returned). """ if not os.path.isfile(DEFAULT_QCONFIG_FILE): return OrderedDict() else: # Note this is nested inside the else to prevent some tools marking # the whole method as deprecated. pass # TODO: reintroduce when we decide on deprecating # warnings.warn( # "Using 'Qconfig.py' for storing the credentials will be deprecated in" # "upcoming versions (>0.6.0). Using .qiskitrc is recommended", # DeprecationWarning) try: spec = spec_from_file_location('Qconfig', DEFAULT_QCONFIG_FILE) q_config = module_from_spec(spec) spec.loader.exec_module(q_config) # type: ignore[attr-defined] if hasattr(q_config, 'config'): credentials = q_config.config.copy() # type: ignore[attr-defined] else: credentials = {} credentials['token'] = q_config.APItoken # type: ignore[attr-defined] credentials['url'] = credentials.get('url', QE_URL) except Exception as ex: # pylint: disable=broad-except raise InvalidCredentialsFormatError('Error loading Qconfig.py: %s' % str(ex)) credentials = Credentials(**credentials) return OrderedDict({credentials.unique_id(): credentials}) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/credentials/configrc.py0000664000372000037200000001447213616666011027375 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """Utilities for reading and writing credentials from and to config files.""" import logging import os from ast import literal_eval from collections import OrderedDict from configparser import ConfigParser, ParsingError from typing import Dict, Optional, Any from .credentials import Credentials, HubGroupProject from .exceptions import InvalidCredentialsFormatError, CredentialsNotFoundError logger = logging.getLogger(__name__) DEFAULT_QISKITRC_FILE = os.path.join(os.path.expanduser("~"), '.qiskit', 'qiskitrc') def read_credentials_from_qiskitrc( filename: Optional[str] = None ) -> Dict[HubGroupProject, Credentials]: """Read a configuration file and return a dict with its sections. Args: filename: full path to the qiskitrc file. If `None`, the default location is used (`HOME/.qiskit/qiskitrc`). Returns: dictionary with the contents of the configuration file, with the form:: {credential_unique_id: Credentials} Raises: InvalidCredentialsFormatError: if the file was not parseable. Please note that this exception is not raised if the file does not exist (instead, an empty dict is returned). """ filename = filename or DEFAULT_QISKITRC_FILE config_parser = ConfigParser() try: config_parser.read(filename) except ParsingError as ex: raise InvalidCredentialsFormatError(str(ex)) # Build the credentials dictionary. credentials_dict = OrderedDict() # type: ignore[var-annotated] for name in config_parser.sections(): single_credentials = dict(config_parser.items(name)) # Individually convert keys to their right types. # TODO: consider generalizing, moving to json configuration or a more # robust alternative. if 'proxies' in single_credentials.keys(): single_credentials['proxies'] = literal_eval( single_credentials['proxies']) if 'verify' in single_credentials.keys(): single_credentials['verify'] = bool( # type: ignore[assignment] single_credentials['verify']) new_credentials = Credentials(**single_credentials) # type: ignore[arg-type] credentials_dict[new_credentials.unique_id()] = new_credentials return credentials_dict def write_qiskit_rc( credentials: Dict[HubGroupProject, Credentials], filename: Optional[str] = None ) -> None: """Write credentials to the configuration file. Args: credentials: dictionary with the credentials, with the form:: {credentials_unique_id: Credentials} filename: full path to the qiskitrc file. If `None`, the default location is used (`HOME/.qiskit/qiskitrc`). """ def _credentials_object_to_dict(obj: Credentials) -> Dict[str, Any]: return {key: getattr(obj, key) for key in ['token', 'url', 'proxies', 'verify'] if getattr(obj, key)} def _section_name(credentials_: Credentials) -> str: """Return a string suitable for use as a unique section name.""" base_name = 'ibmq' if credentials_.is_ibmq(): base_name = '{}_{}_{}_{}'.format(base_name, *credentials_.unique_id()) return base_name filename = filename or DEFAULT_QISKITRC_FILE # Create the directories and the file if not found. os.makedirs(os.path.dirname(filename), exist_ok=True) unrolled_credentials = { _section_name(credentials_object): _credentials_object_to_dict(credentials_object) for _, credentials_object in credentials.items() } # Write the configuration file. with open(filename, 'w') as config_file: config_parser = ConfigParser() config_parser.read_dict(unrolled_credentials) config_parser.write(config_file) def store_credentials( credentials: Credentials, overwrite: bool = False, filename: Optional[str] = None ) -> None: """Store the credentials for a single account in the configuration file. Args: credentials: credentials instance. overwrite: overwrite existing credentials. filename: full path to the qiskitrc file. If `None`, the default location is used (`HOME/.qiskit/qiskitrc`). """ # Read the current providers stored in the configuration file. filename = filename or DEFAULT_QISKITRC_FILE stored_credentials = read_credentials_from_qiskitrc(filename) # Check if duplicated credentials are already stored. By convention, # we assume (hub, group, project) is always unique. if credentials.unique_id() in stored_credentials and not overwrite: logger.warning('Credentials already present. ' 'Set overwrite=True to overwrite.') return # Append and write the credentials to file. stored_credentials[credentials.unique_id()] = credentials write_qiskit_rc(stored_credentials, filename) def remove_credentials( credentials: Credentials, filename: Optional[str] = None ) -> None: """Remove credentials from qiskitrc. Args: credentials: credentials. filename: full path to the qiskitrc file. If `None`, the default location is used (`HOME/.qiskit/qiskitrc`). Raises: CredentialsNotFoundError: If there is no account with that name on the configuration file. """ # Set the name of the Provider from the class. stored_credentials = read_credentials_from_qiskitrc(filename) try: del stored_credentials[credentials.unique_id()] except KeyError: raise CredentialsNotFoundError('The account "%s" does not exist in the ' 'configuration file') write_qiskit_rc(stored_credentials, filename) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/accountprovider.py0000664000372000037200000001141713616666011026511 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """Provider for a single IBM Quantum Experience account.""" import logging from typing import Dict, List, Optional, Any from collections import OrderedDict from qiskit.providers import BaseProvider # type: ignore[attr-defined] from qiskit.providers.models import (QasmBackendConfiguration, PulseBackendConfiguration) from qiskit.validation.exceptions import ModelValidationError from .api.clients import AccountClient from .ibmqbackend import IBMQBackend, IBMQSimulator from .credentials import Credentials from .ibmqbackendservice import IBMQBackendService logger = logging.getLogger(__name__) class AccountProvider(BaseProvider): """Provider for a single IBM Quantum Experience account.""" def __init__(self, credentials: Credentials, access_token: str) -> None: """Return a new AccountProvider. The ``provider_backends`` attribute can be used to autocomplete backend names, by pressing ``tab`` after ``AccountProvider.provider_backends.``. Note that this feature may not be available if an error occurs during backend discovery. Args: credentials: IBM Q Experience credentials. access_token: access token for IBM Q Experience. """ super().__init__() self.credentials = credentials # Set the clients. self._api = AccountClient(access_token, credentials.url, credentials.websockets_url, use_websockets=(not credentials.proxies), **credentials.connection_parameters()) # Initialize the internal list of backends. self._backends = self._discover_remote_backends() self.backends = IBMQBackendService(self) # type: ignore[assignment] def backends(self, name: Optional[str] = None, **kwargs: Any) -> List[IBMQBackend]: # pylint: disable=method-hidden # This method is only for faking the subclassing of `BaseProvider`, as # `.backends()` is an abstract method. Upon initialization, it is # replaced by a `IBMQBackendService` instance. pass def _discover_remote_backends(self, timeout: Optional[float] = None) -> Dict[str, IBMQBackend]: """Return the remote backends available. Args: timeout: number of seconds to wait for the discovery. Returns: a dict of the remote backend instances, keyed by backend name. """ ret = OrderedDict() # type: ignore[var-annotated] configs_list = self._api.list_backends(timeout=timeout) for raw_config in configs_list: # Make sure the raw_config is of proper type if not isinstance(raw_config, dict): logger.warning("An error occurred when retrieving backend " "information. Some backends might not be available.") continue try: if raw_config.get('open_pulse', False): config = PulseBackendConfiguration.from_dict(raw_config) else: config = QasmBackendConfiguration.from_dict(raw_config) backend_cls = IBMQSimulator if config.simulator else IBMQBackend ret[config.backend_name] = backend_cls( configuration=config, provider=self, credentials=self.credentials, api=self._api) except ModelValidationError as ex: logger.warning( 'Remote backend "%s" could not be instantiated due to an ' 'invalid config: %s', raw_config.get('backend_name', raw_config.get('name', 'unknown')), ex) return ret def __eq__( # type: ignore[overide] self, other: 'AccountProvider' ) -> bool: return self.credentials == other.credentials def __repr__(self) -> str: credentials_info = "hub='{}', group='{}', project='{}'".format( self.credentials.hub, self.credentials.group, self.credentials.project) return "<{} for IBMQ({})>".format( self.__class__.__name__, credentials_info) qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/managed/0000775000372000037200000000000013616666025024325 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/managed/exceptions.py0000664000372000037200000000247413616666011027062 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2018, 2020. # # 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. """Exception for the job manager modules.""" from ..exceptions import IBMQError class IBMQJobManagerError(IBMQError): """Base class for errors raise by job manager.""" pass class IBMQJobManagerInvalidStateError(IBMQJobManagerError): """Errors raised when an operation is invoked in an invalid state.""" pass class IBMQJobManagerTimeoutError(IBMQJobManagerError): """Errors raised when a job manager operation times out.""" pass class IBMQJobManagerJobNotFound(IBMQJobManagerError): """Errors raised when a job cannot be found.""" pass class IBMQManagedResultDataNotAvailable(IBMQJobManagerError): """Errors raised when result data is not available.""" pass class IBMQJobManagerUnknownJobSet(IBMQJobManagerError): """Errors raised when the job set ID is unknown.""" pass qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/managed/utils.py0000664000372000037200000001033613616666011026035 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """Utility functions for IBMQJobManager.""" from typing import Callable, Any, List, Union from functools import wraps from collections import Counter from concurrent.futures import wait from qiskit.providers.jobstatus import JobStatus from .managedjob import ManagedJob def requires_submit(func: Callable) -> Callable: """Decorator used by ManagedJobSet to wait for all jobs to be submitted. Args: func (callable): function to be decorated. Returns: callable: the decorated function. Raises: IBMQJobManagerInvalidStateError: If jobs have not been submitted. """ @wraps(func) def _wrapper( job_set: 'ManagedJobSet', # type: ignore[name-defined] *args: Any, **kwargs: Any ) -> Any: """Wrapper function. Args: job_set: ManagedJobSet instance used to manage a set of jobs. args: arguments to be passed to the decorated function. kwargs: keyword arguments to be passed to the decorated function. Returns: return value of the decorated function. """ futures = [managed_job.future for managed_job in job_set._managed_jobs if managed_job.future] wait(futures) return func(job_set, *args, **kwargs) return _wrapper def format_status_counts(statuses: List[Union[JobStatus, None]]) -> List[str]: """Format summary report on job statuses. Args: statuses: Statuses of the jobs. Returns: Formatted job status report. """ counts = Counter(statuses) # type: Counter report = [ " Total jobs: {}".format(len(statuses)), " Successful jobs: {}".format(counts[JobStatus.DONE]), " Failed jobs: {}".format(counts[JobStatus.ERROR]), " Cancelled jobs: {}".format(counts[JobStatus.CANCELLED]), " Running jobs: {}".format(counts[JobStatus.RUNNING]), " Pending jobs: {}".format(counts[JobStatus.INITIALIZING] + counts[JobStatus.VALIDATING] + counts[JobStatus.QUEUED]) ] return report def format_job_details( statuses: List[Union[JobStatus, None]], managed_jobs: List[ManagedJob] ) -> List[str]: """Format detailed report for jobs. Args: statuses: Statuses of the jobs. managed_jobs: Jobs being managed. Returns: Formatted job details. """ report = [] for i, mjob in enumerate(managed_jobs): report.append(" experiments: {}-{}".format(mjob.start_index, mjob.end_index)) report.append(" job index: {}".format(i)) if (mjob.job is None) and mjob.future \ and (not mjob.future.done()): # type: ignore[warn-unreachable] report.append(" status: {}".format( # type: ignore[warn-unreachable] JobStatus.INITIALIZING.value)) continue if mjob.submit_error is not None: report.append(" status: job submit failed: {}".format( str(mjob.submit_error))) continue job = mjob.job report.append(" job ID: {}".format(job.job_id())) report.append(" name: {}".format(job.name())) status_txt = statuses[i].value if statuses[i] else "Unknown" report.append(" status: {}".format(status_txt)) if statuses[i] is JobStatus.QUEUED: report.append(" queue position: {}".format(job.queue_position())) elif statuses[i] is JobStatus.ERROR: report.append(" error_message:") msg_list = job.error_message().split('\n') for msg in msg_list: report.append(msg.rjust(len(msg)+6)) return report qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/managed/managedjobset.py0000664000372000037200000004016113616666011027477 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """A set of jobs being managed by the IBMQJobManager.""" from datetime import datetime from typing import List, Optional, Union, Any, Tuple from concurrent.futures import ThreadPoolExecutor import time import logging import uuid import re from qiskit.circuit import QuantumCircuit from qiskit.pulse import Schedule from qiskit.compiler import assemble from qiskit.qobj import Qobj from qiskit.providers.jobstatus import JobStatus from qiskit.providers.ibmq.apiconstants import ApiJobShareLevel from qiskit.providers.ibmq.accountprovider import AccountProvider from .managedjob import ManagedJob from .managedresults import ManagedResults from .utils import requires_submit, format_status_counts, format_job_details from .exceptions import (IBMQJobManagerInvalidStateError, IBMQJobManagerTimeoutError, IBMQJobManagerJobNotFound, IBMQJobManagerUnknownJobSet) from ..job import IBMQJob from ..job.exceptions import IBMQJobTimeoutError from ..ibmqbackend import IBMQBackend logger = logging.getLogger(__name__) class ManagedJobSet: """A set of managed jobs.""" _id_prefix = "ibmq_jobset_" _id_suffix = "_" def __init__( self, name: Optional[str] = None, short_id: Optional[str] = None ) -> None: """Creates a new ManagedJobSet instance. Args: name: Name for this set of jobs. Default: current datetime. short_id: Short ID for this set of jobs. Default: None. """ self._managed_jobs = [] # type: List[ManagedJob] self._name = name or datetime.utcnow().isoformat() self._backend = None # type: Optional[IBMQBackend] self._id = short_id or uuid.uuid4().hex + '-' + str(time.time()).replace('.', '') self._id_long = self._id_prefix + self._id + self._id_suffix self._tags = [] # type: List[str] # Used for caching self._managed_results = None # type: Optional[ManagedResults] self._error_msg = None # type: Optional[str] def run( self, experiment_list: Union[List[List[QuantumCircuit]], List[List[Schedule]]], backend: IBMQBackend, executor: ThreadPoolExecutor, job_share_level: ApiJobShareLevel, job_tags: Optional[List[str]] = None, **assemble_config: Any ) -> None: """Execute a list of circuits or pulse schedules on a backend. Args: experiment_list : Circuit(s) or pulse schedule(s) to execute. backend: Backend to execute the experiments on. executor: The thread pool to use. job_share_level: Job share level. job_tags: tags to be assigned to the job. assemble_config: Additional arguments used to configure the Qobj assembly. Refer to the ``qiskit.compiler.assemble`` documentation for details on these arguments. Raises: IBMQJobManagerInvalidStateError: If the jobs were already submitted. """ if self._managed_jobs: raise IBMQJobManagerInvalidStateError("Jobs were already submitted.") self._backend = backend if job_tags: self._tags = job_tags.copy() exp_index = 0 for i, experiments in enumerate(experiment_list): qobj = assemble(experiments, backend=backend, **assemble_config) job_name = "{}_{}_".format(self._name, i) mjob = ManagedJob(experiments_count=len(experiments), start_index=exp_index) mjob.submit(qobj=qobj, job_name=job_name, backend=backend, executor=executor, job_share_level=job_share_level, job_tags=self._tags+[self._id_long]) self._managed_jobs.append(mjob) exp_index += len(experiments) def retrieve_jobs(self, provider: AccountProvider, refresh: bool = False) -> None: """Retrieve previously submitted jobs for this set. Args: provider: Provider used for this job set. refresh: If True, re-query the API for the job set. Otherwise return the cached value. Default: False. Raises: IBMQJobManagerUnknownJobSet: If the job set cannot be found. IBMQJobManagerInvalidStateError: If jobs for this job set are found but have unexpected attributes. """ if not refresh and self._managed_jobs: return # IBMQBackend jobs() method does not have a way to pass in unlimited # number of jobs to retrieve. 1000 should be a sufficiently large # enough number. jobs = [] # type: List[IBMQJob] page_limit = 1000 while True: job_page = provider.backends.jobs( # type: ignore[attr-defined] skip=len(jobs), limit=page_limit, job_tags=[self._id_long]) jobs += job_page if len(job_page) < page_limit: break if not jobs: raise IBMQJobManagerUnknownJobSet( "{} is not a known job set within the provider {}.".format( self.job_set_id(), provider)) # Extract common information from the first job. first_job = jobs[0] pattern = re.compile(r'(.*)_([0-9])+_$') matched = pattern.match(first_job.name()) if not matched: raise IBMQJobManagerInvalidStateError( "Job {} is tagged for the job set {} but does not have a proper job name.".format( first_job.job_id(), self.job_set_id())) self._name = matched.group(1) self._backend = first_job.backend() self._tags = first_job.tags() self._tags.remove(self._id_long) jobs_dict = {} for job in jobs: # Verify the job is proper. matched = pattern.match(job.name()) if job.name() else None if not matched or matched.group(1) != self._name or \ job.backend().name() != self._backend.name(): raise IBMQJobManagerInvalidStateError( "Job {} is tagged for the job set {} but does not appear " "to belong to the set".format(job.job_id(), self.job_set_id())) jobs_dict[int(matched.group(2))] = job sorted_indexes = sorted(jobs_dict) # Verify we got all jobs. if sorted_indexes != list(range(len(sorted_indexes))): raise IBMQJobManagerInvalidStateError( "Unable to retrieve all jobs for job set {}".format(self.job_set_id())) self._managed_jobs = [] experiment_index = 0 for job_index in sorted_indexes: job = jobs_dict[job_index] mjob = ManagedJob( start_index=experiment_index, experiments_count=len(job.qobj().experiments), job=job ) self._managed_jobs.append(mjob) experiment_index = mjob.end_index + 1 def statuses(self) -> List[Union[JobStatus, None]]: """Return the status of each job. Returns: A list of job statuses. The entry is ``None`` if the job status cannot be retrieved due to server error. """ return [mjob.status() for mjob in self._managed_jobs] def report(self, detailed: bool = True) -> str: """Return a report on current job statuses. Args: detailed: True if a detailed report is be returned. False if a summary report is to be returned. Returns: A report on job statuses. """ statuses = self.statuses() report = ["Job set name: {}".format(self.name()), " ID: {}".format(self.job_set_id()), " tags: {}".format(self.tags()), "Summary report:"] report.extend(format_status_counts(statuses)) if detailed: report.append("\nDetail report:") report.extend(format_job_details(statuses, self._managed_jobs)) return '\n'.join(report) @requires_submit def results( self, timeout: Optional[float] = None, partial: bool = False ) -> ManagedResults: """Return the results of the jobs. This call will block until all job results become available or the timeout is reached. Note: Some IBMQ job results can be read only once. A second attempt to query the API for the job will fail, as the job is "consumed". The first call to this method in a ``ManagedJobSet`` instance will query the API and consume any available job results. Subsequent calls to that instance's method will also return the results, since they are cached. However, attempting to retrieve the results again in another instance or session might fail due to the job results having been consumed. When `partial=True`, this method will attempt to retrieve partial results of failed jobs if possible. In this case, precaution should be taken when accessing individual experiments, as doing so might cause an exception. The ``success`` attribute of a ``ManagedResults`` instance can be used to verify whether it contains partial results. For example: If one of the experiments failed, trying to get the counts of the unsuccessful experiment would raise an exception since there are no counts to return for it: i.e. try: counts = managed_results.get_counts("failed_experiment") except QiskitError: print("Experiment failed!") Args: timeout: Number of seconds to wait for job results. partial: If true, attempt to retrieve partial job results. Returns: A ``ManagedResults`` instance that can be used to retrieve results for individual experiments. Raises: IBMQJobManagerTimeoutError: if unable to retrieve all job results before the specified timeout. """ if self._managed_results is not None: return self._managed_results start_time = time.time() original_timeout = timeout success = True # TODO We can potentially make this multithreaded for mjob in self._managed_jobs: try: result = mjob.result(timeout=timeout, partial=partial) if result is None or not result.success: success = False except IBMQJobTimeoutError: raise IBMQJobManagerTimeoutError( "Timeout waiting for results for experiments {}-{}.".format( mjob.start_index, self._managed_jobs[-1].end_index)) if timeout: timeout = original_timeout - (time.time() - start_time) if timeout <= 0: raise IBMQJobManagerTimeoutError( "Timeout waiting for results for experiments {}-{}.".format( mjob.start_index, self._managed_jobs[-1].end_index)) self._managed_results = ManagedResults(self, self._backend.name(), success) return self._managed_results @requires_submit def error_messages(self) -> Optional[str]: """Provide details about job failures. This call will block until all job results become available. Returns: An error report if one or more jobs failed or ``None`` otherwise. """ if self._error_msg: return self._error_msg report = [] # type: List[str] for i, mjob in enumerate(self._managed_jobs): msg_list = mjob.error_message() if not msg_list: continue report.append("Experiments {}-{}, job index={}, job ID={}:".format( mjob.start_index, mjob.end_index, i, mjob.job.job_id())) for msg in msg_list.split('\n'): report.append(msg.rjust(len(msg)+2)) if not report: return None return '\n'.join(report) @requires_submit def cancel(self) -> None: """Cancel all managed jobs.""" for mjob in self._managed_jobs: mjob.cancel() @requires_submit def jobs(self) -> List[Union[IBMQJob, None]]: """Return a list of submitted jobs. Returns: A list of IBMQJob instances that represents the submitted jobs. The entry is ``None`` if the job submit failed. """ return [mjob.job for mjob in self._managed_jobs] @requires_submit def job( self, experiment: Union[str, QuantumCircuit, Schedule, int] ) -> Tuple[Optional[IBMQJob], int]: """Returns the job used to submit the experiment and the experiment index. For example, if ``IBMQJobManager`` is used to submit 1000 experiments, and ``IBMQJobManager`` divides them into 2 jobs: job 1 has experiments 0-499, and job 2 has experiments 500-999. In this case ``job_set.job(501)`` will return (job2, 1). Args: experiment: the index of the experiment. Several types are accepted for convenience:: * str: the name of the experiment. * QuantumCircuit: the name of the circuit instance will be used. * Schedule: the name of the schedule instance will be used. * int: the position of the experiment. Returns: A tuple of the job used to submit the experiment, or ``None`` if the job submit failed, and the experiment index. Raises: IBMQJobManagerJobNotFound: If the job for the experiment could not be found. """ if isinstance(experiment, int): for mjob in self._managed_jobs: if mjob.end_index >= experiment >= mjob.start_index: return mjob.job, experiment - mjob.start_index else: if isinstance(experiment, (QuantumCircuit, Schedule)): experiment = experiment.name for job in self.jobs(): for i, exp in enumerate(job.qobj().experiments): if hasattr(exp.header, 'name') and exp.header.name == experiment: return job, i raise IBMQJobManagerJobNotFound("Unable to find the job for experiment {}".format( experiment)) @requires_submit def qobjs(self) -> List[Qobj]: """Return the Qobj for the jobs. Returns: A list of Qobj for the jobs. The entry is ``None`` if the Qobj could not be retrieved. """ return [mjob.qobj() for mjob in self._managed_jobs] def name(self) -> str: """Return the name of this set of jobs. Returns: Name of this set of jobs. """ return self._name def job_set_id(self) -> str: """Return the ID of this set of jobs. Returns: ID of this set of jobs. """ # Return only the short version of the ID to reduce the possibility the # full ID is used for another job. return self._id def managed_jobs(self) -> List[ManagedJob]: """Return a list of managed jobs. Returns: A list of managed jobs. """ return self._managed_jobs def tags(self) -> List[str]: """Return the tags assigned to this set of jobs. Returns: Tags assigned to this set of jobs. """ return self._tags qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/managed/managedresults.py0000664000372000037200000001610413616666011027712 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 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. """Results managed by the job manager.""" from typing import List, Optional, Union, Tuple, Dict from qiskit.result import Result from qiskit.circuit import QuantumCircuit from qiskit.pulse import Schedule from .exceptions import IBMQManagedResultDataNotAvailable from ..job.exceptions import JobError class ManagedResults: """Results managed by job manager. This class is a wrapper around the `Result` class. It provides the same methods as the `Result` class. Please refer to the `Result` class for more information on the methods. """ def __init__( self, job_set: 'ManagedJobSet', # type: ignore[name-defined] backend_name: str, success: bool ): """Creates a new ManagedResults instance. Args: job_set: Managed job set for these results. backend_name: Name of the backend used to run the experiments. success: True if all experiments were successful and results available. False otherwise. """ self._job_set = job_set self.backend_name = backend_name self.success = success def data(self, experiment: Union[str, QuantumCircuit, Schedule, int]) -> Dict: """Get the raw data for an experiment. Args: experiment: the index of the experiment. Several types are accepted for convenience:: * str: the name of the experiment. * QuantumCircuit: the name of the circuit instance will be used. * Schedule: the name of the schedule instance will be used. * int: the position of the experiment. Returns: Refer to the ``Result.data()`` documentation for return information. Raises: IBMQManagedResultDataNotAvailable: If data for the experiment could not be retrieved. IBMQJobManagerJobNotFound: If the job for the experiment could not be found. """ result, exp_index = self._get_result(experiment) return result.data(exp_index) def get_memory( self, experiment: Union[str, QuantumCircuit, Schedule, int] ) -> Union[list, 'numpy.ndarray']: # type: ignore[name-defined] """Get the sequence of memory states (readouts) for each shot. The data from the experiment is a list of format ['00000', '01000', '10100', '10100', '11101', '11100', '00101', ..., '01010'] Args: experiment: the index of the experiment, as specified by ``data()``. Returns: Refer to the ``Result.get_memory()`` documentation for return information. Raises: IBMQManagedResultDataNotAvailable: If data for the experiment could not be retrieved. IBMQJobManagerJobNotFound: If the job for the experiment could not be found. """ result, exp_index = self._get_result(experiment) return result.get_memory(exp_index) def get_counts( self, experiment: Union[str, QuantumCircuit, Schedule, int] ) -> Dict[str, int]: """Get the histogram data of an experiment. Args: experiment: the index of the experiment, as specified by ``data()``. Returns: Refer to the ``Result.get_counts()`` documentation for return information. Raises: IBMQManagedResultDataNotAvailable: If data for the experiment could not be retrieved. IBMQJobManagerJobNotFound: If the job for the experiment could not be found. """ result, exp_index = self._get_result(experiment) return result.get_counts(exp_index) def get_statevector( self, experiment: Union[str, QuantumCircuit, Schedule, int], decimals: Optional[int] = None ) -> List[complex]: """Get the final statevector of an experiment. Args: experiment: the index of the experiment, as specified by ``data()``. decimals: the number of decimals in the statevector. If None, does not round. Returns: Refer to the ``Result.get_statevector()`` documentation for return information. Raises: IBMQManagedResultDataNotAvailable: If data for the experiment could not be retrieved. IBMQJobManagerJobNotFound: If the job for the experiment could not be found. """ result, exp_index = self._get_result(experiment) return result.get_statevector(experiment=exp_index, decimals=decimals) def get_unitary( self, experiment: Union[str, QuantumCircuit, Schedule, int], decimals: Optional[int] = None ) -> List[List[complex]]: """Get the final unitary of an experiment. Args: experiment: the index of the experiment, as specified by ``data()``. decimals: the number of decimals in the unitary. If None, does not round. Returns: Refer to the ``Result.get_unitary()`` documentation for return information. Raises: IBMQManagedResultDataNotAvailable: If data for the experiment could not be retrieved. IBMQJobManagerJobNotFound: If the job for the experiment could not be found. """ result, exp_index = self._get_result(experiment) return result.get_unitary(experiment=exp_index, decimals=decimals) def _get_result( self, experiment: Union[str, QuantumCircuit, Schedule, int] ) -> Tuple[Result, int]: """Get the result of the job used to submit the experiment. Args: experiment: the index of the experiment, as specified by ``data()``. Returns: A tuple of the result of the job used to submit the experiment and the experiment index within the job. Raises: IBMQManagedResultDataNotAvailable: If data for the experiment could not be retrieved. IBMQJobManagerJobNotFound: If the job for the experiment could not be found. """ (job, exp_index) = self._job_set.job(experiment) if job is None: raise IBMQManagedResultDataNotAvailable( "Job for experiment {} was not successfully submitted.".format(experiment)) try: result = job.result() return result, exp_index except JobError as err: raise IBMQManagedResultDataNotAvailable( "Result data for experiment {} is not available.".format(experiment)) from err qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/managed/ibmqjobmanager.py0000664000372000037200000002362613616666011027661 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """Job manager used to manage jobs for IBM Q Experience.""" import logging from typing import List, Optional, Union, Any from concurrent import futures from qiskit.circuit import QuantumCircuit from qiskit.pulse import Schedule from qiskit.providers.ibmq.apiconstants import ApiJobShareLevel from qiskit.providers.ibmq.utils import validate_job_tags from qiskit.providers.ibmq.accountprovider import AccountProvider from .exceptions import IBMQJobManagerInvalidStateError from .utils import format_job_details, format_status_counts from .managedjobset import ManagedJobSet from ..ibmqbackend import IBMQBackend logger = logging.getLogger(__name__) class IBMQJobManager: """Job manager for IBM Quantum Experience. The Job Manager is a higher level mechanism for handling jobs composed of multiple circuits or pulse schedules. It splits the experiments into multiple jobs based on backend restrictions. When the jobs are finished, it collects and presents the results in a unified view. To use the Job Manager to submit multiple experiments, invoking the ``run()`` method:: from qiskit.providers.ibmq.managed import IBMQJobManager job_manager = IBMQJobManager() job_set_foo = job_manager.run(circs, backend=backend, name='foo') The ``run()`` method returns a ``ManagedJobSet`` instance, which represents the set of jobs for the experiments. You can use the ``ManagedJobSet`` methods, such as ``statuses()``, ``results()``, and ``error_messages()`` to get a combined view of the jobs in the set. For example:: results = job_set_foo.results() results.get_counts(5) # Counts for experiment 5. The ``job_set_id()`` method of ``ManagedJobSet`` returns the job set ID, which can be used to retrieve the job set later:: job_set_id = job_set_foo.job_set_id() retrieved_foo = job_manager.retrieve_job_set(job_set_id=job_set_id, provider=provider) """ def __init__(self) -> None: """Creates a new IBMQJobManager instance.""" self._job_sets = [] # type: List[ManagedJobSet] self._executor = futures.ThreadPoolExecutor() def run( self, experiments: Union[List[QuantumCircuit], List[Schedule]], backend: IBMQBackend, name: Optional[str] = None, max_experiments_per_job: Optional[int] = None, job_share_level: Optional[str] = None, job_tags: Optional[List[str]] = None, **run_config: Any ) -> ManagedJobSet: """Execute a set of circuits or pulse schedules on a backend. The circuits or schedules will be split into multiple jobs. Circuits or schedules in a job will be executed together in each shot. Args: experiments: Circuit(s) or pulse schedule(s) to execute. backend: Backend to execute the experiments on. name: Name for this set of jobs. Each job within the set will have a job name that consists of the set name followed by a suffix. Default: current datetime. max_experiments_per_job: Maximum number of experiments to run in each job. If not specified, the default is to use the maximum allowed by the backend. If the specified value is greater the maximum allowed by the backend, the default is used. job_share_level: Allows sharing the jobs at the hub/group/project and global level. The possible job share levels are: "global", "hub", "group", "project", and "none". Default: "none". job_tags: tags to be assigned to the job. The tags can subsequently be used as a filter in the ``jobs()`` function call. Default: None. run_config: Configuration of the runtime environment. Some examples of these configuration parameters include: ``qobj_id``, ``qobj_header``, ``shots``, ``memory``, ``seed_simulator``, ``qubit_lo_freq``, ``meas_lo_freq``, ``qubit_lo_range``, ``meas_lo_range``, ``schedule_los``, ``meas_level``, ``meas_return``, ``meas_map``, ``memory_slot_size``, ``rep_time``, and ``parameter_binds``. Refer to the documentation on ``qiskit.compiler.assemble()`` for details on these arguments. Returns: Managed job set. Raises: IBMQJobManagerInvalidStateError: If an input parameter value is not valid. """ if (any(isinstance(exp, Schedule) for exp in experiments) and not backend.configuration().open_pulse): raise IBMQJobManagerInvalidStateError( "Pulse schedules found, but the backend does not support pulse schedules.") # Validate job share level if job_share_level: try: api_job_share_level = ApiJobShareLevel(job_share_level.lower()) except ValueError: raise IBMQJobManagerInvalidStateError( '"{}" is not a valid job share level. Valid job share levels are: {}'.format( job_share_level, ', '.join(level.value for level in ApiJobShareLevel))) else: api_job_share_level = ApiJobShareLevel.NONE validate_job_tags(job_tags, IBMQJobManagerInvalidStateError) experiment_list = self._split_experiments( experiments, backend=backend, max_experiments_per_job=max_experiments_per_job) job_set = ManagedJobSet(name=name) job_set.run(experiment_list, backend=backend, executor=self._executor, job_share_level=api_job_share_level, job_tags=job_tags, **run_config) self._job_sets.append(job_set) return job_set def _split_experiments( self, experiments: Union[List[QuantumCircuit], List[Schedule]], backend: IBMQBackend, max_experiments_per_job: Optional[int] = None ) -> List[Union[List[QuantumCircuit], List[Schedule]]]: """Split a list of experiments into sublists. Args: experiments: Experiments to be split. backend: Backend to execute the experiments on. max_experiments_per_job: Maximum number of experiments to run in each job. Returns: A list of sublists of experiments. """ if hasattr(backend.configuration(), 'max_experiments'): backend_max = backend.configuration().max_experiments chunk_size = backend_max if max_experiments_per_job is None \ else min(backend_max, max_experiments_per_job) elif max_experiments_per_job: chunk_size = max_experiments_per_job else: return [experiments] return [experiments[x:x + chunk_size] for x in range(0, len(experiments), chunk_size)] def report(self, detailed: bool = True) -> str: """Return a report on the statuses of all jobs managed by this manager. Args: detailed: True if a detailed report is be returned. False if a summary report is to be returned. Returns: A report on job statuses. """ job_set_statuses = [job_set.statuses() for job_set in self._job_sets] flat_status_list = [stat for stat_list in job_set_statuses for stat in stat_list] report = ["Summary report:"] report.extend(format_status_counts(flat_status_list)) if detailed: report.append("\nDetail report:") for i, job_set in enumerate(self._job_sets): report.append((" Job set name: {}, ID: {}".format( job_set.name(), job_set.job_set_id()))) report.extend(format_job_details( job_set_statuses[i], job_set.managed_jobs())) return '\n'.join(report) def job_sets(self, name: Optional[str] = None) -> List[ManagedJobSet]: """Returns a list of managed job sets matching the specified filtering in this session. Args: name: Name of the managed job sets. Returns: A list of managed job sets. """ if name: return [job_set for job_set in self._job_sets if job_set.name() == name] return self._job_sets def retrieve_job_set( self, job_set_id: str, provider: AccountProvider, refresh: bool = False ) -> ManagedJobSet: """Retrieve a previously submitted job set. Args: job_set_id: ID of the job set. provider: Provider used for this job set. refresh: If True, re-query the API for the job set. Otherwise return the cached value. Default: False. Returns: Retrieved job set. Raises: IBMQJobManagerUnknownJobSet: If the job set cannot be found. IBMQJobManagerInvalidStateError: If jobs for this job set are found but have unexpected attributes. """ for index, mjs in enumerate(self._job_sets): if mjs.job_set_id() == job_set_id: if not refresh: return mjs del self._job_sets[index] break new_job_set = ManagedJobSet(short_id=job_set_id) new_job_set.retrieve_jobs(provider=provider) self._job_sets.append(new_job_set) return new_job_set qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/managed/__init__.py0000664000372000037200000000121313616666011026426 0ustar travistravis00000000000000# -*- 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 representing Jobs communicating with IBM Q.""" from .ibmqjobmanager import IBMQJobManager from .managedjobset import ManagedJobSet qiskit-ibmq-provider-0.4.6/qiskit/providers/ibmq/managed/managedjob.py0000664000372000037200000001523113616666011026763 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """Experiments managed by the job manager.""" import warnings import logging from typing import List, Optional from concurrent.futures import ThreadPoolExecutor from qiskit.providers.ibmq import IBMQBackend from qiskit.qobj import Qobj from qiskit.result import Result from qiskit.providers.jobstatus import JobStatus from qiskit.providers.exceptions import JobError from qiskit.providers.ibmq.apiconstants import ApiJobShareLevel from ..job.ibmqjob import IBMQJob from ..job.exceptions import IBMQJobTimeoutError logger = logging.getLogger(__name__) class ManagedJob: """Job managed by job manager.""" def __init__( self, start_index: int, experiments_count: int, job: Optional[IBMQJob] = None ): """Creates a new ManagedJob instance. Args: start_index: Starting index of the experiment set. experiments_count: Number of experiments. job: Job to be managed, or ``None`` if not already known. Default: None. """ self.start_index = start_index self.end_index = start_index + experiments_count - 1 self.future = None # Properties that may be populated by the future. self.job = job # type: Optional[IBMQJob] self.submit_error = None # type: Optional[Exception] def submit( self, qobj: Qobj, job_name: str, backend: IBMQBackend, executor: ThreadPoolExecutor, job_share_level: ApiJobShareLevel, job_tags: Optional[List[str]] = None ) -> None: """Submit the job. Args: qobj: Qobj to run. job_name: Name of the job. backend: Backend to execute the experiments on. executor: The thread pool to use. job_share_level: Job share level. job_tags: tags to be assigned to the job. """ # Submit the job in its own future. self.future = executor.submit( self._async_submit, qobj=qobj, job_name=job_name, backend=backend, job_share_level=job_share_level, job_tags=job_tags) def _async_submit( self, qobj: Qobj, job_name: str, backend: IBMQBackend, job_share_level: ApiJobShareLevel, job_tags: Optional[List[str]] = None ) -> None: """Run a Qobj asynchronously and populate instance attributes. Args: qobj: Qobj to run. job_name: Name of the job. backend: Backend to execute the experiments on. job_share_level: Job share level. job_tags: tags to be assigned to the job. Returns: IBMQJob instance for the job. """ try: self.job = backend.run( qobj=qobj, job_name=job_name, job_share_level=job_share_level.value, job_tags=job_tags) except Exception as err: # pylint: disable=broad-except warnings.warn("Unable to submit job for experiments {}-{}: {}".format( self.start_index, self.end_index, err)) self.submit_error = err def status(self) -> Optional[JobStatus]: """Query the API for job status. Returns: Current job status, or ``None`` if an error occurred. """ if self.submit_error is not None: return None if self.job is None: # Job not yet submitted return JobStatus.INITIALIZING try: return self.job.status() except JobError as err: warnings.warn( "Unable to retrieve job status for experiments {}-{}, job ID={}: {} ".format( self.start_index, self.end_index, self.job.job_id(), err)) return None def result( self, timeout: Optional[float] = None, partial: bool = False ) -> Optional[Result]: """Return the result of the job. Args: timeout: number of seconds to wait for job partial: If true, attempt to retrieve partial job results. Returns: Result object or ``None`` if result could not be retrieved. Raises: IBMQJobTimeoutError: if the job does not return results before a specified timeout. """ result = None if self.job is not None: try: result = self.job.result(timeout=timeout, partial=partial) except IBMQJobTimeoutError: raise except JobError as err: warnings.warn( "Unable to retrieve job result for experiments {}-{}, job ID={}: {} ".format( self.start_index, self.end_index, self.job.job_id(), err)) return result def error_message(self) -> Optional[str]: """Provide details about the reason of failure. Returns: An error report if the job failed or ``None`` otherwise. """ if self.job is None: return None try: return self.job.error_message() except JobError: return "Unknown error." def cancel(self) -> None: """Attempt to cancel a job.""" cancelled = False cancel_error = "Unknown error" try: cancelled = self.job.cancel() except JobError as err: cancel_error = str(err) if not cancelled: logger.warning("Unable to cancel job %s for experiments %d-%d: %s", self.job.job_id(), self.start_index, self.end_index, cancel_error) def qobj(self) -> Optional[Qobj]: """Return the Qobj for this job. Returns: The Qobj for this job or ``None`` if the Qobj could not be retrieved. """ if self.job is None: return None try: return self.job.qobj() except JobError as err: warnings.warn( "Unable to retrieve qobj for experiments {}-{}, job ID={}: {} ".format( self.start_index, self.end_index, self.job.job_id(), err)) return None qiskit-ibmq-provider-0.4.6/PKG-INFO0000664000372000037200000003006013616666025017554 0ustar travistravis00000000000000Metadata-Version: 2.1 Name: qiskit-ibmq-provider Version: 0.4.6 Summary: Qiskit provider for accessing the quantum devices and simulators at IBMQ Home-page: https://github.com/Qiskit/qiskit-ibmq-provider Author: Qiskit Development Team Author-email: qiskit@qiskit.org License: Apache 2.0 Description: # Qiskit IBMQ Provider [![License](https://img.shields.io/github/license/Qiskit/qiskit-ibmq-provider.svg?style=popout-square)](https://opensource.org/licenses/Apache-2.0)[![Build Status](https://img.shields.io/travis/com/Qiskit/qiskit-ibmq-provider/master.svg?style=popout-square)](https://travis-ci.com/Qiskit/qiskit-ibmq-provider)[![](https://img.shields.io/github/release/Qiskit/qiskit-ibmq-provider.svg?style=popout-square)](https://github.com/Qiskit/qiskit-ibmq-provider/releases)[![](https://img.shields.io/pypi/dm/qiskit-ibmq-provider.svg?style=popout-square)](https://pypi.org/project/qiskit-ibmq-provider/) **Qiskit** is an open-source framework for working with noisy quantum computers at the level of pulses, circuits, and algorithms. This module contains a provider that allows accessing the **[IBM Q]** quantum devices and simulators. ## Installation We encourage installing Qiskit via the PIP tool (a python package manager), which installs all Qiskit elements and components, including this one. ```bash pip install qiskit ``` PIP will handle all dependencies automatically for us and you will always install the latest (and well-tested) version. To install from source, follow the instructions in the [contribution guidelines]. ## Setting up the IBMQ provider Once the package is installed, you can access the provider from Qiskit. > **Note**: Since November 2019 (and with version `0.4` of this > `qiskit-ibmq-provider` package / version `0.14` of the `qiskit` package) > legacy Quantum Experience or QConsole (v1) accounts are no longer supported. > If you are still using a v1 account, please follow the steps described in > [update instructions](#updating-to-the-new-IBM-Q-Experience) to update your account. ### Configure your IBMQ credentials 1. Create an IBM Q account or log in to your existing account by visiting the [IBM Q Experience login page]. 2. Copy (and/or optionally regenerate) your API token from your [IBM Q Experience account page]. 3. Take your token from step 2, here called `MY_API_TOKEN`, and run: ```python from qiskit import IBMQ IBMQ.save_account('MY_API_TOKEN') ``` ### Accessing your IBMQ backends After calling `IBMQ.save_account()`, your credentials will be stored on disk. Once they are stored, at any point in the future you can load and use them in your program simply via: ```python from qiskit import IBMQ provider = IBMQ.load_account() backend = provider.get_backend('ibmq_qasm_simulator') ``` Alternatively, if you do not want to save your credentials to disk and only intend to use them during the current session, you can use: ```python from qiskit import IBMQ provider = IBMQ.enable_account('MY_API_TOKEN') backend = provider.get_backend('ibmq_qasm_simulator') ``` By default, all IBM Q accounts have access to the same, open project (hub: `ibm-q`, group: `open`, project: `main`). For convenience, the `IBMQ.load_account()` and `IBMQ.enable_account()` methods will return a provider for that project. If you have access to other projects, you can use: ```python provider_2 = IBMQ.get_provider(hub='MY_HUB', group='MY_GROUP', project='MY_PROJECT') ``` ## Updating to the new IBM Q Experience Since November 2019 (and with version `0.4` of this `qiskit-ibmq-provider` package), the IBMQProvider only supports the new [IBM Q Experience], dropping support for the legacy Quantum Experience and Qconsole accounts. The new IBM Q Experience is also referred as `v2`, whereas the legacy one and Qconsole as `v1`. This section includes instructions for updating your accounts and programs. Please note that: * the IBM Q Experience `v1` credentials and the programs written for pre-0.3 versions will still be working during the `0.3.x` series. From 0.4 onwards, only `v2` credentials are supported, and it is recommended to upgrade in order to take advantage of the new features. * updating your credentials to the IBM Q Experience `v2` implies that you will need to update your programs. The sections below contain instructions on how to perform the transition. ### Updating your IBM Q Experience credentials If you have credentials for the legacy Quantum Experience or Qconsole stored in disk, you can make use of `IBMQ.update_account()` helper. This helper will read your current credentials stored in disk and attempt to convert them: ```python from qiskit import IBMQ IBMQ.update_account() ``` ``` Found 2 credentials. The credentials stored will be replaced with a single entry with token "MYTOKEN" and the new IBM Q Experience v2 URL (https://auth.quantum-computing.ibm.com/api). In order to access the provider, please use the new "IBMQ.get_provider()" methods: provider0 = IBMQ.load_account() provider1 = IBMQ.get_provider(hub='A', group='B', project='C') Note you need to update your programs in order to retrieve backends from a specific provider directly: backends = provider0.backends() backend = provider0.get_backend('ibmq_qasm_simulator') Update the credentials? [y/N] ``` Upon confirmation, your credentials will be overwritten with a valid IBM Q Experience v2 set of credentials. For more complex cases, consider deleting your previous credentials via `IBMQ.delete_accounts()` and follow the instructions in the [IBM Q Experience account page]. ### Updating your programs The new IBM Q Experience support also introduces a more structured approach for accessing backends. Previously, access to all backends was centralized through: ```python IBMQ.backends() IBMQ.get_backend('ibmq_qasm_simulator') ``` In version `0.3` onwards, the preferred way to access the backends is via a `Provider` for one of your projects instead of via the global `IBMQ` instance directly, allowing for more granular control over the project you are using: ```python my_provider = IBMQ.get_provider() my_provider.backends() my_provider.get_backend('ibmq_qasm_simulator') ``` In a similar spirit, you can check the providers that you have access to via: ```python IBMQ.providers() ``` In addition, since the new IBM Q Experience provides only one set of credentials, the account management methods in IBMQ are now in singular form. For example, you should use `IBMQ.load_account()` instead of `IBMQ.load_accounts()`. An `IBMQAccountError` exception is raised if you attempt to use the legacy methods with an IBM Q Experience v2 account. The following tables contains a quick reference for the differences between the two versions. Please refer to the documentation of each method for more in depth details: ### Account management | <0.3 / v1 credentials | >=0.3 and v2 credentials | | --- | --- | | N/A | `IBMQ.update_account()` | | `IBMQ.save_account(token, url)` | `IBMQ.save_account(token)` | `IBMQ.load_accounts()` | `provider = IBMQ.load_account()` | `IBMQ.enable_account()` | `provider = IBMQ.enable_account()` | `IBMQ.disable_accounts()` | `IBMQ.disable_account()` | `IBMQ.active_accounts()` | `IBMQ.active_account()` | `IBMQ.stored_accounts()` | `IBMQ.stored_account()` | `IBMQ.delete_accounts()` | `IBMQ.delete_account()` ### Using backends | <0.3 / v1 credentials | >=0.3 and v2 credentials | | --- | --- | | N/A | `providers = IBMQ.providers()` | | `backend = IBMQ.get_backend(name, hub='HUB')` | `provider = IBMQ.get_provider(hub='HUB')` | | | `backend = provider.get_backend(name)` | | `backends = IBMQ.backends(hub='HUB')` | `provider = IBMQ.get_provider(hub='HUB')` | | | `backends = provider.backends()` | ## Contribution Guidelines If you'd like to contribute to IBM Q provider, please take a look at our [contribution guidelines]. This project adheres to Qiskit's [code of conduct]. By participating, you are expect to uphold to this code. We use [GitHub issues] for tracking requests and bugs. Please use our [slack] for discussion and simple questions. To join our Slack community use the invite link at [Qiskit.org]. For questions that are more suited for a forum we use the `Qiskit` tag in [Stack Exchange]. ## Next Steps Now you're set up and ready to check out some of the other examples from our [Qiskit Tutorial] repository. ## Authors and Citation The Qiskit IBM Q provider is the work of [many people] who contribute to the project at different levels. If you use Qiskit, please cite as per the included [BibTeX file]. ## License [Apache License 2.0]. [IBM Q]: https://www.research.ibm.com/ibm-q/ [IBM Q Experience]: https://quantum-computing.ibm.com [IBM Q Experience login page]: https://quantum-computing.ibm.com/login [IBM Q Experience account page]: https://quantum-computing.ibm.com/account [contribution guidelines]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/CONTRIBUTING.md [code of conduct]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/CODE_OF_CONDUCT.md [GitHub issues]: https://github.com/Qiskit/qiskit-ibmq-provider/issues [slack]: https://qiskit.slack.com [Qiskit.org]: https://qiskit.org [Stack Exchange]: https://quantumcomputing.stackexchange.com/questions/tagged/qiskit [Qiskit Tutorial]: https://github.com/Qiskit/qiskit-tutorial [many people]: https://github.com/Qiskit/qiskit-ibmq-provider/graphs/contributors [BibTeX file]: https://github.com/Qiskit/qiskit/blob/master/Qiskit.bib [Apache License 2.0]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/LICENSE.txt Keywords: qiskit sdk quantum api ibmq Platform: UNKNOWN Classifier: Environment :: Console Classifier: License :: OSI Approved :: Apache Software License Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: MacOS Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Scientific/Engineering Requires-Python: >=3.5 Description-Content-Type: text/markdown qiskit-ibmq-provider-0.4.6/README.md0000664000372000037200000002227513616666011017742 0ustar travistravis00000000000000# Qiskit IBMQ Provider [![License](https://img.shields.io/github/license/Qiskit/qiskit-ibmq-provider.svg?style=popout-square)](https://opensource.org/licenses/Apache-2.0)[![Build Status](https://img.shields.io/travis/com/Qiskit/qiskit-ibmq-provider/master.svg?style=popout-square)](https://travis-ci.com/Qiskit/qiskit-ibmq-provider)[![](https://img.shields.io/github/release/Qiskit/qiskit-ibmq-provider.svg?style=popout-square)](https://github.com/Qiskit/qiskit-ibmq-provider/releases)[![](https://img.shields.io/pypi/dm/qiskit-ibmq-provider.svg?style=popout-square)](https://pypi.org/project/qiskit-ibmq-provider/) **Qiskit** is an open-source framework for working with noisy quantum computers at the level of pulses, circuits, and algorithms. This module contains a provider that allows accessing the **[IBM Q]** quantum devices and simulators. ## Installation We encourage installing Qiskit via the PIP tool (a python package manager), which installs all Qiskit elements and components, including this one. ```bash pip install qiskit ``` PIP will handle all dependencies automatically for us and you will always install the latest (and well-tested) version. To install from source, follow the instructions in the [contribution guidelines]. ## Setting up the IBMQ provider Once the package is installed, you can access the provider from Qiskit. > **Note**: Since November 2019 (and with version `0.4` of this > `qiskit-ibmq-provider` package / version `0.14` of the `qiskit` package) > legacy Quantum Experience or QConsole (v1) accounts are no longer supported. > If you are still using a v1 account, please follow the steps described in > [update instructions](#updating-to-the-new-IBM-Q-Experience) to update your account. ### Configure your IBMQ credentials 1. Create an IBM Q account or log in to your existing account by visiting the [IBM Q Experience login page]. 2. Copy (and/or optionally regenerate) your API token from your [IBM Q Experience account page]. 3. Take your token from step 2, here called `MY_API_TOKEN`, and run: ```python from qiskit import IBMQ IBMQ.save_account('MY_API_TOKEN') ``` ### Accessing your IBMQ backends After calling `IBMQ.save_account()`, your credentials will be stored on disk. Once they are stored, at any point in the future you can load and use them in your program simply via: ```python from qiskit import IBMQ provider = IBMQ.load_account() backend = provider.get_backend('ibmq_qasm_simulator') ``` Alternatively, if you do not want to save your credentials to disk and only intend to use them during the current session, you can use: ```python from qiskit import IBMQ provider = IBMQ.enable_account('MY_API_TOKEN') backend = provider.get_backend('ibmq_qasm_simulator') ``` By default, all IBM Q accounts have access to the same, open project (hub: `ibm-q`, group: `open`, project: `main`). For convenience, the `IBMQ.load_account()` and `IBMQ.enable_account()` methods will return a provider for that project. If you have access to other projects, you can use: ```python provider_2 = IBMQ.get_provider(hub='MY_HUB', group='MY_GROUP', project='MY_PROJECT') ``` ## Updating to the new IBM Q Experience Since November 2019 (and with version `0.4` of this `qiskit-ibmq-provider` package), the IBMQProvider only supports the new [IBM Q Experience], dropping support for the legacy Quantum Experience and Qconsole accounts. The new IBM Q Experience is also referred as `v2`, whereas the legacy one and Qconsole as `v1`. This section includes instructions for updating your accounts and programs. Please note that: * the IBM Q Experience `v1` credentials and the programs written for pre-0.3 versions will still be working during the `0.3.x` series. From 0.4 onwards, only `v2` credentials are supported, and it is recommended to upgrade in order to take advantage of the new features. * updating your credentials to the IBM Q Experience `v2` implies that you will need to update your programs. The sections below contain instructions on how to perform the transition. ### Updating your IBM Q Experience credentials If you have credentials for the legacy Quantum Experience or Qconsole stored in disk, you can make use of `IBMQ.update_account()` helper. This helper will read your current credentials stored in disk and attempt to convert them: ```python from qiskit import IBMQ IBMQ.update_account() ``` ``` Found 2 credentials. The credentials stored will be replaced with a single entry with token "MYTOKEN" and the new IBM Q Experience v2 URL (https://auth.quantum-computing.ibm.com/api). In order to access the provider, please use the new "IBMQ.get_provider()" methods: provider0 = IBMQ.load_account() provider1 = IBMQ.get_provider(hub='A', group='B', project='C') Note you need to update your programs in order to retrieve backends from a specific provider directly: backends = provider0.backends() backend = provider0.get_backend('ibmq_qasm_simulator') Update the credentials? [y/N] ``` Upon confirmation, your credentials will be overwritten with a valid IBM Q Experience v2 set of credentials. For more complex cases, consider deleting your previous credentials via `IBMQ.delete_accounts()` and follow the instructions in the [IBM Q Experience account page]. ### Updating your programs The new IBM Q Experience support also introduces a more structured approach for accessing backends. Previously, access to all backends was centralized through: ```python IBMQ.backends() IBMQ.get_backend('ibmq_qasm_simulator') ``` In version `0.3` onwards, the preferred way to access the backends is via a `Provider` for one of your projects instead of via the global `IBMQ` instance directly, allowing for more granular control over the project you are using: ```python my_provider = IBMQ.get_provider() my_provider.backends() my_provider.get_backend('ibmq_qasm_simulator') ``` In a similar spirit, you can check the providers that you have access to via: ```python IBMQ.providers() ``` In addition, since the new IBM Q Experience provides only one set of credentials, the account management methods in IBMQ are now in singular form. For example, you should use `IBMQ.load_account()` instead of `IBMQ.load_accounts()`. An `IBMQAccountError` exception is raised if you attempt to use the legacy methods with an IBM Q Experience v2 account. The following tables contains a quick reference for the differences between the two versions. Please refer to the documentation of each method for more in depth details: ### Account management | <0.3 / v1 credentials | >=0.3 and v2 credentials | | --- | --- | | N/A | `IBMQ.update_account()` | | `IBMQ.save_account(token, url)` | `IBMQ.save_account(token)` | `IBMQ.load_accounts()` | `provider = IBMQ.load_account()` | `IBMQ.enable_account()` | `provider = IBMQ.enable_account()` | `IBMQ.disable_accounts()` | `IBMQ.disable_account()` | `IBMQ.active_accounts()` | `IBMQ.active_account()` | `IBMQ.stored_accounts()` | `IBMQ.stored_account()` | `IBMQ.delete_accounts()` | `IBMQ.delete_account()` ### Using backends | <0.3 / v1 credentials | >=0.3 and v2 credentials | | --- | --- | | N/A | `providers = IBMQ.providers()` | | `backend = IBMQ.get_backend(name, hub='HUB')` | `provider = IBMQ.get_provider(hub='HUB')` | | | `backend = provider.get_backend(name)` | | `backends = IBMQ.backends(hub='HUB')` | `provider = IBMQ.get_provider(hub='HUB')` | | | `backends = provider.backends()` | ## Contribution Guidelines If you'd like to contribute to IBM Q provider, please take a look at our [contribution guidelines]. This project adheres to Qiskit's [code of conduct]. By participating, you are expect to uphold to this code. We use [GitHub issues] for tracking requests and bugs. Please use our [slack] for discussion and simple questions. To join our Slack community use the invite link at [Qiskit.org]. For questions that are more suited for a forum we use the `Qiskit` tag in [Stack Exchange]. ## Next Steps Now you're set up and ready to check out some of the other examples from our [Qiskit Tutorial] repository. ## Authors and Citation The Qiskit IBM Q provider is the work of [many people] who contribute to the project at different levels. If you use Qiskit, please cite as per the included [BibTeX file]. ## License [Apache License 2.0]. [IBM Q]: https://www.research.ibm.com/ibm-q/ [IBM Q Experience]: https://quantum-computing.ibm.com [IBM Q Experience login page]: https://quantum-computing.ibm.com/login [IBM Q Experience account page]: https://quantum-computing.ibm.com/account [contribution guidelines]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/CONTRIBUTING.md [code of conduct]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/CODE_OF_CONDUCT.md [GitHub issues]: https://github.com/Qiskit/qiskit-ibmq-provider/issues [slack]: https://qiskit.slack.com [Qiskit.org]: https://qiskit.org [Stack Exchange]: https://quantumcomputing.stackexchange.com/questions/tagged/qiskit [Qiskit Tutorial]: https://github.com/Qiskit/qiskit-tutorial [many people]: https://github.com/Qiskit/qiskit-ibmq-provider/graphs/contributors [BibTeX file]: https://github.com/Qiskit/qiskit/blob/master/Qiskit.bib [Apache License 2.0]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/LICENSE.txt qiskit-ibmq-provider-0.4.6/setup.cfg0000664000372000037200000000056713616666025020311 0ustar travistravis00000000000000[pycodestyle] max-line-length = 100 [mypy] python_version = 3.5 namespace_packages = True ignore_missing_imports = True warn_redundant_casts = True warn_unreachable = True strict_equality = True disallow_untyped_calls = True disallow_untyped_defs = True disallow_incomplete_defs = True strict_optional = False show_none_errors = False [egg_info] tag_build = tag_date = 0 qiskit-ibmq-provider-0.4.6/test/0000775000372000037200000000000013616666025017437 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/test/decorators.py0000664000372000037200000001743113616666011022157 0ustar travistravis00000000000000# -*- 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. """Decorators for using with IBMQProvider unit tests.""" import os from functools import wraps from unittest import SkipTest from qiskit.test.testing_options import get_test_options from qiskit.providers.ibmq import least_busy from qiskit.providers.ibmq.ibmqfactory import IBMQFactory from qiskit.providers.ibmq.credentials import (Credentials, discover_credentials) def requires_qe_access(func): """Decorator that signals that the test uses the online API. It involves: * determines if the test should be skipped by checking environment variables. * if the `USE_ALTERNATE_ENV_CREDENTIALS` environment variable is set, it reads the credentials from an alternative set of environment variables. * if the test is not skipped, it reads `qe_token` and `qe_url` from `Qconfig.py`, environment variables or qiskitrc. * if the test is not skipped, it appends `qe_token` and `qe_url` as arguments to the test function. Args: func (callable): test function to be decorated. Returns: callable: the decorated function. """ @wraps(func) def _wrapper(obj, *args, **kwargs): if get_test_options()['skip_online']: raise SkipTest('Skipping online tests') credentials = _get_credentials() obj.using_ibmq_credentials = credentials.is_ibmq() kwargs.update({'qe_token': credentials.token, 'qe_url': credentials.url}) return func(obj, *args, **kwargs) return _wrapper def requires_provider(func): """Decorator that signals the test uses the online API, via a provider. This decorator delegates into the `requires_qe_access` decorator, but instead of the credentials it appends a `provider` argument to the decorated function. Args: func (callable): test function to be decorated. Returns: callable: the decorated function. """ @wraps(func) @requires_qe_access def _wrapper(*args, **kwargs): ibmq_factory = IBMQFactory() qe_token = kwargs.pop('qe_token') qe_url = kwargs.pop('qe_url') provider = ibmq_factory.enable_account(qe_token, qe_url) if os.getenv('QE_HGP', None): hgp = os.getenv('QE_HGP').split('/') provider = ibmq_factory.get_provider(hub=hgp[0], group=hgp[1], project=hgp[2]) kwargs.update({'provider': provider}) return func(*args, **kwargs) return _wrapper def requires_device(func): """Decorator that retrieves the appropriate backend to use for testing. This decorator delegates into the `requires_provider` decorator, but instead of the provider it appends a `backend` argument to the decorated function. It involves: * If the `QE_DEVICE` environment variable is set, the test is to be run against the backend specified by `QE_DEVICE`. * If the `QE_DEVICE` environment variable is not set, the test is to be run against least busy device. Args: func (callable): test function to be decorated. Returns: callable: the decorated function. """ @wraps(func) @requires_provider def _wrapper(*args, **kwargs): provider = kwargs.pop('provider') _backend = None if os.getenv('QE_DEVICE'): backend_name = os.getenv('QE_DEVICE') _backend = provider.get_backend(backend_name) else: _backend = least_busy(provider.backends( simulator=False, filters=lambda b: b.configuration().n_qubits >= 5)) kwargs.update({'backend': _backend}) return func(*args, **kwargs) return _wrapper def slow_test_on_device(func): """Decorator that signals that the test should run on a real or semi-real device. It involves: * skips the test if online tests are to be skipped. * if the `USE_STAGING_CREDENTIALS` environment variable is set, then enable the staging account using credentials specified by the `QE_STAGING_TOKEN` and `QE_STAGING_URL` environment variables. Backend name specified by `QE_STAGING_DEVICE`, if set, will also be used. * else skips the test if slow tests are to be skipped. * else enable the account using credentials returned by `_get_credentials()` and use the backend specified by `QE_DEVICE`, if set. * if backend value is not already set, use the least busy backend. * appends arguments `provider` and `backend` to the decorated function. Args: func (callable): test function to be decorated. Returns: callable: the decorated function. """ @wraps(func) def _wrapper(obj, *args, **kwargs): if get_test_options()['skip_online']: raise SkipTest('Skipping online tests') if os.getenv('USE_STAGING_CREDENTIALS', ''): credentials = Credentials(os.getenv('QE_STAGING_TOKEN'), os.getenv('QE_STAGING_URL')) backend_name = os.getenv('QE_STAGING_DEVICE', None) else: if not get_test_options()['run_slow']: raise SkipTest('Skipping slow tests') credentials = _get_credentials() backend_name = os.getenv('QE_DEVICE', None) obj.using_ibmq_credentials = credentials.is_ibmq() ibmq_factory = IBMQFactory() provider = ibmq_factory.enable_account(credentials.token, credentials.url) _backend = None if backend_name: for provider in ibmq_factory.providers(): backends = provider.backends(name=backend_name) if backends: _backend = backends[0] break else: _backend = least_busy(provider.backends( simulator=False, filters=lambda b: b.configuration().n_qubits >= 5)) if not _backend: raise Exception("Unable to find suitable backend.") kwargs.update({'provider': provider}) kwargs.update({'backend': _backend}) return func(obj, *args, **kwargs) return _wrapper def _get_credentials(): """Finds the credentials for a specific test and options. Returns: Credentials: set of credentials Raises: Exception: when the credential could not be set and they are needed for that set of options """ if os.getenv('USE_ALTERNATE_ENV_CREDENTIALS', ''): # Special case: instead of using the standard credentials mechanism, # load them from different environment variables. This assumes they # will always be in place, as is used by the Travis setup. return Credentials(os.getenv('IBMQ_TOKEN'), os.getenv('IBMQ_URL')) # Attempt to read the standard credentials. discovered_credentials = discover_credentials() if discovered_credentials: # Decide which credentials to use for testing. if len(discovered_credentials) > 1: try: # Attempt to use QE credentials. return discovered_credentials[(None, None, None)] except KeyError: pass # Use the first available credentials. return list(discovered_credentials.values())[0] raise Exception('Could not locate valid credentials.') from None qiskit-ibmq-provider-0.4.6/test/contextmanagers.py0000664000372000037200000000727513616666011023221 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 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. """Context managers for using with IBMQProvider unit tests.""" import os from contextlib import ContextDecorator from tempfile import NamedTemporaryFile from unittest.mock import patch from qiskit.providers.ibmq.credentials import configrc from qiskit.providers.ibmq.credentials.environ import VARIABLES_MAP CREDENTIAL_ENV_VARS = VARIABLES_MAP.keys() class custom_envs(ContextDecorator): """Context manager that modifies environment variables.""" # pylint: disable=invalid-name def __init__(self, new_environ): """custom_envs constructor. Args: new_environ (dict): a dictionary of new environment variables to use. """ self.new_environ = new_environ self.os_environ_original = os.environ.copy() def __enter__(self): # Remove the original variables from `os.environ`. modified_environ = {**os.environ, **self.new_environ} os.environ = modified_environ def __exit__(self, *exc): os.environ = self.os_environ_original class no_envs(ContextDecorator): """Context manager that disables environment variables.""" # pylint: disable=invalid-name def __init__(self, vars_to_remove): """no_envs constructor. Args: vars_to_remove (list): environment variables to remove. """ self.vars_to_remove = vars_to_remove self.os_environ_original = os.environ.copy() def __enter__(self): # Remove the original variables from `os.environ`. modified_environ = {key: value for key, value in os.environ.items() if key not in self.vars_to_remove} os.environ = modified_environ def __exit__(self, *exc): os.environ = self.os_environ_original class custom_qiskitrc(ContextDecorator): """Context manager that uses a temporary qiskitrc.""" # pylint: disable=invalid-name def __init__(self, contents=b''): # Create a temporary file with the contents. self.tmp_file = NamedTemporaryFile() self.tmp_file.write(contents) self.tmp_file.flush() self.default_qiskitrc_file_original = configrc.DEFAULT_QISKITRC_FILE def __enter__(self): # Temporarily modify the default location of the qiskitrc file. configrc.DEFAULT_QISKITRC_FILE = self.tmp_file.name def __exit__(self, *exc): # Delete the temporary file and restore the default location. self.tmp_file.close() configrc.DEFAULT_QISKITRC_FILE = self.default_qiskitrc_file_original class no_file(ContextDecorator): """Context manager that disallows access to a file.""" # pylint: disable=invalid-name def __init__(self, filename): self.filename = filename # Store the original `os.path.isfile` function, for mocking. self.isfile_original = os.path.isfile self.patcher = patch('os.path.isfile', side_effect=self.side_effect) def __enter__(self): self.patcher.start() def __exit__(self, *exc): self.patcher.stop() def side_effect(self, filename_): """Return False for the specified file.""" if filename_ == self.filename: return False return self.isfile_original(filename_) qiskit-ibmq-provider-0.4.6/test/ibmqtestcase.py0000664000372000037200000000205113616666011022466 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """Custom TestCase for IBMQProvider.""" from qiskit.test import QiskitTestCase class IBMQTestCase(QiskitTestCase): """Custom TestCase for use with the IBMQProvider.""" def tearDown(self): # Reset the default providers, as in practice they acts as a singleton # due to importing the wrapper from qiskit. from qiskit.providers.ibmq import IBMQ IBMQ._providers.clear() IBMQ._credentials = None from qiskit.providers.basicaer import BasicAer BasicAer._backends = BasicAer._verify_backends() qiskit-ibmq-provider-0.4.6/test/ibmq/0000775000372000037200000000000013616666025020367 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_job_attributes.py0000664000372000037200000003606713616666011026037 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """IBMQJob Test.""" import time from unittest import mock, skip import re import uuid from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.providers.jobstatus import JobStatus, JOB_FINAL_STATES from qiskit.providers.ibmq.job.exceptions import IBMQJobFailureError, JobError from qiskit.providers.ibmq.api.clients.account import AccountClient from qiskit.providers.ibmq.exceptions import IBMQBackendValueError from qiskit.compiler import assemble, transpile from ..jobtestcase import JobTestCase from ..decorators import requires_provider, slow_test_on_device from ..utils import most_busy_backend class TestIBMQJobAttributes(JobTestCase): """Test ibmqjob module.""" def setUp(self): super().setUp() self._qc = _bell_circuit() @requires_provider def test_job_id(self, provider): """Test getting a job id.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) self.log.info('job_id: %s', job.job_id()) self.assertTrue(job.job_id() is not None) @requires_provider def test_get_backend_name(self, provider): """Test getting a backend name.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) self.assertTrue(job.backend().name() == backend.name()) @slow_test_on_device def test_running_job_properties(self, provider, backend): # pylint: disable=unused-argument """Test fetching properties of a running job.""" qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) while not job.running(): time.sleep(0.5) self.assertIsNotNone(job.properties()) @requires_provider def test_job_name(self, provider): """Test using job names on a simulator.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) # Use a unique job name job_name = str(time.time()).replace('.', '') job = backend.run(qobj, job_name=job_name) job_id = job.job_id() # TODO No need to wait for job to run once api is fixed while job.status() not in JOB_FINAL_STATES + (JobStatus.RUNNING,): time.sleep(0.5) rjob = provider.backends.retrieve_job(job_id) self.assertEqual(rjob.name(), job_name) # Check using partial matching. job_name_partial = job_name[8:] retrieved_jobs = provider.backends.jobs(backend_name=backend.name(), job_name=job_name_partial) self.assertGreaterEqual(len(retrieved_jobs), 1) retrieved_job_ids = {job.job_id() for job in retrieved_jobs} self.assertIn(job_id, retrieved_job_ids) # Check using regular expressions. job_name_regex = '^{}$'.format(job_name) retrieved_jobs = provider.backends.jobs(backend_name=backend.name(), job_name=job_name_regex) self.assertEqual(len(retrieved_jobs), 1) self.assertEqual(job_id, retrieved_jobs[0].job_id()) @requires_provider def test_duplicate_job_name(self, provider): """Test multiple jobs with the same custom job name using a simulator.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) # Use a unique job name job_name = str(time.time()).replace('.', '') job_ids = set() for _ in range(2): job = backend.run(qobj, job_name=job_name) job_ids.add(job.job_id()) # TODO No need to wait for job to run once api is fixed while job.status() not in JOB_FINAL_STATES + (JobStatus.RUNNING,): time.sleep(0.5) retrieved_jobs = provider.backends.jobs(backend_name=backend.name(), job_name=job_name) self.assertEqual(len(retrieved_jobs), 2, "More than 2 jobs retrieved: {}".format(retrieved_jobs)) retrieved_job_ids = {job.job_id() for job in retrieved_jobs} self.assertEqual(job_ids, retrieved_job_ids) for job in retrieved_jobs: self.assertEqual(job.name(), job_name) @skip('Skipping until staging device is fixed.') @slow_test_on_device def test_error_message_device(self, provider, backend): # pylint: disable=unused-argument """Test retrieving job error messages from a device backend.""" qc_new = transpile(self._qc, backend) qobj = assemble([qc_new, qc_new], backend=backend) qobj.experiments[1].instructions[1].name = 'bad_instruction' job = backend.run(qobj) with self.assertRaises(IBMQJobFailureError): job.result(timeout=300, partial=False) message = job.error_message() self.assertTrue(message) self.assertIsNotNone(re.search(r'Error code: [0-9]{4}\.$', message), message) r_message = provider.backends.retrieve_job(job.job_id()).error_message() self.assertTrue(r_message) self.assertIsNotNone(re.search(r'Error code: [0-9]{4}\.$', r_message), r_message) @requires_provider def test_error_message_simulator(self, provider): """Test retrieving job error messages from a simulator backend.""" backend = provider.get_backend('ibmq_qasm_simulator') qc_new = transpile(self._qc, backend) qobj = assemble([qc_new, qc_new], backend=backend) qobj.experiments[1].instructions[1].name = 'bad_instruction' job = backend.run(qobj) with self.assertRaises(IBMQJobFailureError): job.result() message = job.error_message() self.assertIn('Experiment 1: ERROR', message) r_message = provider.backends.retrieve_job(job.job_id()).error_message() self.assertIn('Experiment 1: ERROR', r_message) @requires_provider def test_error_message_validation(self, provider): """Test retrieving job error message for a validation error.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend), shots=10000) job = backend.run(qobj) with self.assertRaises(IBMQJobFailureError): job.result() message = job.error_message() self.assertNotIn("Unknown", message) self.assertIsNotNone(re.search(r'Error code: [0-9]{4}\.$', message), message) r_message = provider.backends.retrieve_job(job.job_id()).error_message() self.assertEqual(message, r_message) @requires_provider def test_refresh(self, provider): """Test refreshing job data.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) job._wait_for_completion() rjob = provider.backends.jobs(db_filter={'id': job.job_id()})[0] self.assertFalse(rjob._time_per_step) rjob.refresh() self.assertEqual(rjob._time_per_step, job._time_per_step) @requires_provider def test_time_per_step(self, provider): """Test retrieving time per step.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) job.result() self.assertTrue(job.time_per_step()) rjob = provider.backends.jobs(db_filter={'id': job.job_id()})[0] self.assertTrue(rjob.time_per_step()) @requires_provider def test_new_job_attributes(self, provider): """Test job with new attributes.""" def _mocked__api_job_submit(*args, **kwargs): submit_info = original_submit(*args, **kwargs) submit_info.update({'batman': 'bruce'}) return submit_info backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) original_submit = backend._api.job_submit with mock.patch.object(AccountClient, 'job_submit', side_effect=_mocked__api_job_submit): job = backend.run(qobj) self.assertEqual(job.batman, 'bruce') @requires_provider def test_queue_info(self, provider): """Test retrieving queue information.""" # Find the most busy backend. backend = most_busy_backend(provider) qobj = assemble(transpile(self._qc, backend=backend), backend=backend) leave_states = list(JOB_FINAL_STATES) + [JobStatus.RUNNING] job = backend.run(qobj) queue_info = None for _ in range(10): queue_info = job.queue_info() # Even if job status is QUEUED, queue information may not be # immediately available. if (job._status is JobStatus.QUEUED and job.queue_position() is not None) or \ job._status in leave_states: break time.sleep(0.5) if job._status is JobStatus.QUEUED and job.queue_position() is not None: self.log.debug("Job id=%s, queue info=%s, queue position=%s", job.job_id(), queue_info, job.queue_position()) msg = "Job {} is queued but has no ".format(job.job_id()) self.assertIsNotNone(queue_info, msg + "queue info.") for attr, value in queue_info.__dict__.items(): self.assertIsNotNone(value, msg + attr) self.assertTrue(all(0 < priority <= 1.0 for priority in [ queue_info.hub_priority, queue_info.group_priority, queue_info.project_priority]), "Unexpected queue info {} for job {}".format(queue_info, job.job_id())) self.assertTrue(queue_info.format()) self.assertTrue(repr(queue_info)) else: self.assertIsNone(job.queue_position()) self.log.warning("Unable to retrieve queue information") # Cancel job so it doesn't consume more resources. try: job.cancel() except JobError: pass @requires_provider def test_invalid_job_share_level(self, provider): """Test setting a non existent share level for a job.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) with self.assertRaises(IBMQBackendValueError) as context_manager: backend.run(qobj, job_share_level='invalid_job_share_level') self.assertIn('not a valid job share', context_manager.exception.message) @requires_provider def test_share_job_in_project(self, provider): """Test successfully sharing a job within a shareable project.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj, job_share_level='project') retrieved_job = backend.retrieve_job(job.job_id()) self.assertEqual(getattr(retrieved_job, 'share_level'), 'project', "Job {} has incorrect share level".format(job.job_id())) @requires_provider def test_job_tags_or(self, provider): """Test using job tags with an or operator.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) # Use a unique tag. job_tags = [uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex] job = backend.run(qobj, job_tags=job_tags) # TODO No need to wait for job to run once api is fixed while job.status() not in JOB_FINAL_STATES + (JobStatus.RUNNING,): time.sleep(0.5) rjobs = backend.jobs(job_tags=['phantom_tag']) self.assertEqual(len(rjobs), 0, "Expected job {}, got {}".format(job.job_id(), rjobs)) # Check all tags, some of the tags, and a mixture of good and bad tags. tags_to_check = [job_tags, job_tags[1:2], job_tags[0:1]+['phantom_tag']] for tags in tags_to_check: with self.subTest(tags=tags): rjobs = backend.jobs(job_tags=tags) self.assertEqual(len(rjobs), 1, "Expected job {}, got {}".format(job.job_id(), rjobs)) self.assertEqual(rjobs[0].job_id(), job.job_id()) self.assertEqual(set(rjobs[0].tags()), set(job_tags)) @requires_provider def test_job_tags_and(self, provider): """Test using job tags with an and operator.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) # Use a unique tag. job_tags = [uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex] job = backend.run(qobj, job_tags=job_tags) # TODO No need to wait for job to run once api is fixed while job.status() not in JOB_FINAL_STATES + (JobStatus.RUNNING,): time.sleep(0.5) no_rjobs_tags = [job_tags[0:1]+['phantom_tags'], ['phantom_tag']] for tags in no_rjobs_tags: rjobs = backend.jobs(job_tags=tags, job_tags_operator="AND") self.assertEqual(len(rjobs), 0, "Expected job {}, got {}".format(job.job_id(), rjobs)) has_rjobs_tags = [job_tags, job_tags[1:3]] for tags in has_rjobs_tags: with self.subTest(tags=tags): rjobs = backend.jobs(job_tags=tags, job_tags_operator="AND") self.assertEqual(len(rjobs), 1, "Expected job {}, got {}".format(job.job_id(), rjobs)) self.assertEqual(rjobs[0].job_id(), job.job_id()) self.assertEqual(set(rjobs[0].tags()), set(job_tags)) @requires_provider def test_invalid_job_tags(self, provider): """Test using job tags with an and operator.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) self.assertRaises(IBMQBackendValueError, backend.run, qobj, job_tags={'foo'}) self.assertRaises(IBMQBackendValueError, backend.jobs, job_tags=[1, 2, 3]) def _bell_circuit(): qr = QuantumRegister(2, 'q') cr = ClassicalRegister(2, 'c') qc = QuantumCircuit(qr, cr) qc.h(qr[0]) qc.cx(qr[0], qr[1]) qc.measure(qr, cr) return qc qiskit-ibmq-provider-0.4.6/test/ibmq/websocket/0000775000372000037200000000000013616666025022355 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/test/ibmq/websocket/test_websocket.py0000664000372000037200000001272413616666011025755 0ustar travistravis00000000000000# -*- 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. """Test for the Websocket client.""" import asyncio from contextlib import suppress import warnings import websockets from qiskit.providers.ibmq.api.exceptions import ( WebsocketError, WebsocketTimeoutError, WebsocketIBMQProtocolError) from qiskit.providers.ibmq.api.clients.websocket import WebsocketClient from ...ibmqtestcase import IBMQTestCase from .websocket_server import ( TOKEN_JOB_COMPLETED, TOKEN_JOB_TRANSITION, TOKEN_WRONG_FORMAT, TOKEN_TIMEOUT, TOKEN_WEBSOCKET_RETRY_SUCCESS, TOKEN_WEBSOCKET_RETRY_FAILURE, TOKEN_WEBSOCKET_JOB_NOT_FOUND, websocket_handler) TEST_IP_ADDRESS = '127.0.0.1' INVALID_PORT = 9876 VALID_PORT = 8765 class TestWebsocketClient(IBMQTestCase): """Tests for the websocket client.""" def test_invalid_url(self): """Test connecting to an invalid URL.""" client = WebsocketClient('wss://{}:{}'.format(TEST_IP_ADDRESS, INVALID_PORT), None) with self.assertRaises(WebsocketError): asyncio.get_event_loop().run_until_complete( client.get_job_status('job_id')) class TestWebsocketClientMock(IBMQTestCase): """Tests for the the websocket client against a mock server.""" @classmethod def setUpClass(cls): super().setUpClass() # Launch the mock server. start_server = websockets.serve(websocket_handler, TEST_IP_ADDRESS, int(VALID_PORT)) cls.server = asyncio.get_event_loop().run_until_complete(start_server) @classmethod def tearDownClass(cls): super().tearDownClass() # Close the mock server. loop = asyncio.get_event_loop() loop.stop() with warnings.catch_warnings(): # Suppress websockets deprecation warning warnings.filterwarnings("ignore", category=PendingDeprecationWarning) # Manually cancel any pending asyncio tasks. pending = asyncio.Task.all_tasks() for task in pending: task.cancel() with suppress(asyncio.CancelledError): loop.run_until_complete(task) def test_job_final_status(self): """Test retrieving a job already in final status.""" client = WebsocketClient('ws://{}:{}'.format( TEST_IP_ADDRESS, VALID_PORT), TOKEN_JOB_COMPLETED) response = asyncio.get_event_loop().run_until_complete( client.get_job_status('job_id')) self.assertIsInstance(response, dict) self.assertIn('status', response) self.assertEqual(response['status'], 'COMPLETED') def test_job_transition(self): """Test retrieving a job that transitions to final status.""" client = WebsocketClient('ws://{}:{}'.format( TEST_IP_ADDRESS, VALID_PORT), TOKEN_JOB_TRANSITION) response = asyncio.get_event_loop().run_until_complete( client.get_job_status('job_id')) self.assertIsInstance(response, dict) self.assertIn('status', response) self.assertEqual(response['status'], 'COMPLETED') def test_timeout(self): """Test timeout during retrieving a job status.""" client = WebsocketClient('ws://{}:{}'.format( TEST_IP_ADDRESS, VALID_PORT), TOKEN_TIMEOUT) with self.assertRaises(WebsocketTimeoutError): _ = asyncio.get_event_loop().run_until_complete( client.get_job_status('job_id', timeout=2)) def test_invalid_response(self): """Test unparseable response from the server.""" client = WebsocketClient('ws://{}:{}'.format( TEST_IP_ADDRESS, VALID_PORT), TOKEN_WRONG_FORMAT) with self.assertRaises(WebsocketIBMQProtocolError): _ = asyncio.get_event_loop().run_until_complete( client.get_job_status('job_id')) def test_websocket_retry_success(self): """Test retrieving a job status during a retry attempt.""" client = WebsocketClient('ws://{}:{}'.format( TEST_IP_ADDRESS, VALID_PORT), TOKEN_WEBSOCKET_RETRY_SUCCESS) response = asyncio.get_event_loop().run_until_complete( client.get_job_status('job_id')) self.assertIsInstance(response, dict) self.assertIn('status', response) self.assertEqual(response['status'], 'COMPLETED') def test_websocket_retry_failure(self): """Test exceeding the retry limit for retrieving a job status.""" client = WebsocketClient('ws://{}:{}'.format( TEST_IP_ADDRESS, VALID_PORT), TOKEN_WEBSOCKET_RETRY_FAILURE) with self.assertRaises(WebsocketError): _ = asyncio.get_event_loop().run_until_complete( client.get_job_status('job_id')) def test_websocket_job_not_found(self): """Test retrieving a job status for an non existent id.""" client = WebsocketClient('ws://{}:{}'.format( TEST_IP_ADDRESS, VALID_PORT), TOKEN_WEBSOCKET_JOB_NOT_FOUND) with self.assertRaises(WebsocketError): _ = asyncio.get_event_loop().run_until_complete( client.get_job_status('job_id')) qiskit-ibmq-provider-0.4.6/test/ibmq/websocket/test_websocket_integration.py0000664000372000037200000001503113616666011030352 0ustar travistravis00000000000000# -*- 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. """Test for the Websocket client integration.""" from unittest import mock from threading import Thread from queue import Queue from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.compiler import assemble, transpile from qiskit.providers import JobTimeoutError from qiskit.providers.ibmq.api.clients.websocket import ( WebsocketClient, WebsocketAuthenticationMessage) from qiskit.providers.ibmq.api.clients import AccountClient from qiskit.providers.jobstatus import JobStatus from ...ibmqtestcase import IBMQTestCase from ...decorators import requires_provider, slow_test_on_device class TestWebsocketIntegration(IBMQTestCase): """Websocket integration tests.""" @requires_provider def setUp(self, provider): # pylint: disable=arguments-differ self.provider = provider self.sim_backend = self.provider.get_backend(simulator=True) # Create a circuit qr = QuantumRegister(1) cr = ClassicalRegister(1) self.qc1 = QuantumCircuit(qr, cr, name='qc1') self.qc1.measure(qr[0], cr[0]) # Create a default Qobj using the simulator. self.circuit = transpile(self.qc1, backend=self.sim_backend) self.qobj = assemble(self.circuit, backend=self.sim_backend, shots=1) def test_websockets_simulator(self): """Test checking status of a job via websockets for a simulator.""" job = self.sim_backend.run(self.qobj) # Manually disable the non-websocket polling. job._api._job_final_status_polling = None result = job.result() self.assertEqual(result.status, 'COMPLETED') @slow_test_on_device def test_websockets_device(self, provider, backend): # pylint: disable=unused-argument """Test checking status of a job via websockets for a device.""" qc = transpile(self.qc1, backend=backend) qobj = assemble(qc, backend=backend) job = backend.run(qobj) # Manually disable the non-websocket polling. job._api._job_final_status_polling = None result = job.result(timeout=180) self.assertTrue(result.success) def test_websockets_job_final_state(self): """Test checking status of a job in a final state via websockets.""" job = self.sim_backend.run(self.qobj) job._wait_for_completion() # Manually disable the non-websocket polling. job._api._job_final_status_polling = None # Pretend we haven't seen the final status job._status = JobStatus.RUNNING job._wait_for_completion() self.assertIs(job._status, JobStatus.DONE) def test_websockets_retry_bad_url(self): """Test http retry after websocket error due to an invalid URL.""" job = self.sim_backend.run(self.qobj) saved_websocket_url = job._api.client_ws.websocket_url try: # Use fake websocket address. job._api.client_ws.websocket_url = 'wss://wss.localhost' # _wait_for_completion() should retry with http successfully # after getting websockets error. job._wait_for_completion() finally: job._api.client_ws.websocket_url = saved_websocket_url self.assertIs(job._status, JobStatus.DONE) @mock.patch.object(WebsocketClient, '_authentication_message', return_value=WebsocketAuthenticationMessage( type_='authentication', data='phantom_token')) def test_websockets_retry_bad_auth(self, _): """Test http retry after websocket error due to a failed authentication.""" job = self.sim_backend.run(self.qobj) with mock.patch.object(AccountClient, 'job_status', side_effect=job._api.job_status) as mocked_wait: job._wait_for_completion() self.assertIs(job._status, JobStatus.DONE) mocked_wait.assert_called_with(job.job_id()) def test_websockets_retry_connection_closed(self): """Test http retry after websocket error due to closed connection.""" def _job_status_side_effect(*args, **kwargs): """Side effect function to restore job ID""" # pylint: disable=unused-argument job._job_id = saved_job_id return saved_job_status(saved_job_id) job = self.sim_backend.run(self.qobj) # Save the originals. saved_job_id = job._job_id saved_job_status = job._api.job_status # Use bad job ID to fail the status retrieval. job._job_id = '12345' # job.result() should retry with http successfully after getting websockets error. with mock.patch.object(AccountClient, 'job_status', side_effect=_job_status_side_effect): job._wait_for_completion() self.assertIs(job._status, JobStatus.DONE) def test_websockets_timeout(self): """Test timeout checking status of a job via websockets.""" qc = transpile(self.qc1, backend=self.sim_backend) qobj = assemble(qc, backend=self.sim_backend, shots=2048) job = self.sim_backend.run(qobj) with self.assertRaises(JobTimeoutError): job.result(timeout=0.1) def test_websockets_multi_job(self): """Test checking status of multiple jobs in parallel via websockets.""" def _run_job_get_result(q): job = self.sim_backend.run(self.qobj) # Manually disable the non-websocket polling. job._api._job_final_status_polling = None job._wait_for_completion() if job._status is not JobStatus.DONE: q.put(False) max_threads = 2 result_q = Queue() job_threads = [] for i in range(max_threads): job_thread = Thread(target=_run_job_get_result, args=(result_q,), name="job_result_{}".format(i), daemon=True) job_thread.start() job_threads.append(job_thread) for job_thread in job_threads: job_thread.join() self.assertTrue(result_q.empty()) qiskit-ibmq-provider-0.4.6/test/ibmq/websocket/__init__.py0000664000372000037200000000104713616666011024463 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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 Websocket related tests.""" qiskit-ibmq-provider-0.4.6/test/ibmq/websocket/websocket_server.py0000664000372000037200000001124013616666011026274 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Websocket server for testing purposes.""" import asyncio import json import warnings from qiskit.providers.ibmq.api.clients.websocket import WebsocketResponseMethod TOKEN_JOB_COMPLETED = 'token_job_completed' TOKEN_JOB_TRANSITION = 'token_job_transition' TOKEN_TIMEOUT = 'token_timeout' TOKEN_WRONG_FORMAT = 'token_wrong_format' TOKEN_WEBSOCKET_RETRY_SUCCESS = 'token_websocket_retry_success' TOKEN_WEBSOCKET_RETRY_FAILURE = 'token_websocket_retry_failure' TOKEN_WEBSOCKET_JOB_NOT_FOUND = 'token_websocket_job_not_found' @asyncio.coroutine def websocket_handler(websocket, path): """Entry point for the websocket mock server.""" # pylint: disable=unused-argument # Receive the authentication message. msg_in = yield from websocket.recv() auth_message = json.loads(msg_in) # Check for valid access tokens. token = auth_message['data'] if token in (TOKEN_JOB_COMPLETED, TOKEN_JOB_TRANSITION, TOKEN_TIMEOUT, TOKEN_WRONG_FORMAT, TOKEN_WEBSOCKET_RETRY_SUCCESS, TOKEN_WEBSOCKET_RETRY_FAILURE, TOKEN_WEBSOCKET_JOB_NOT_FOUND): msg_out = json.dumps({'type': 'authenticated'}) yield from websocket.send(msg_out.encode('utf8')) else: # Close the connection. yield from websocket.close() # Depending on the access token, perform different actions: if token == TOKEN_JOB_COMPLETED: yield from handle_token_job_completed(websocket) elif token == TOKEN_JOB_TRANSITION: yield from handle_token_job_transition(websocket) elif token == TOKEN_TIMEOUT: yield from handle_token_timeout(websocket) elif token == TOKEN_WRONG_FORMAT: yield from handle_token_wrong_format(websocket) elif token == TOKEN_WEBSOCKET_RETRY_SUCCESS: yield from handle_token_retry_success(websocket) elif token == TOKEN_WEBSOCKET_RETRY_FAILURE: yield from handle_token_retry_failure(websocket) elif token == TOKEN_WEBSOCKET_JOB_NOT_FOUND: yield from handle_token_job_not_found(websocket) @asyncio.coroutine def handle_token_job_completed(websocket): """Return a final job status, and close with 4002.""" msg_out = WebsocketResponseMethod(type_='job-status', data={'status': 'COMPLETED'}) yield from websocket.send(msg_out.as_json().encode('utf8')) yield from websocket.close(code=4002) @asyncio.coroutine def handle_token_job_transition(websocket): """Send several job status, and close with 4002.""" msg_out = WebsocketResponseMethod(type_='job-status', data={'status': 'RUNNING'}) yield from websocket.send(msg_out.as_json().encode('utf8')) yield from asyncio.sleep(1) msg_out = WebsocketResponseMethod(type_='job-status', data={'status': 'COMPLETED'}) yield from websocket.send(msg_out.as_json().encode('utf8')) yield from websocket.close(code=4002) @asyncio.coroutine def handle_token_timeout(websocket): """Close the socket after 10 seconds, without replying.""" yield from asyncio.sleep(10) yield from websocket.close() @asyncio.coroutine def handle_token_wrong_format(websocket): """Return a status in an invalid format.""" yield from websocket.send('INVALID'.encode('utf8')) yield from websocket.close() @asyncio.coroutine def handle_token_retry_success(websocket): """Close the socket once and force a retry.""" if not hasattr(handle_token_retry_success, 'retry_attempt'): setattr(handle_token_retry_success, 'retry_attempt', True) yield from handle_token_retry_failure(websocket) else: yield from handle_token_job_completed(websocket) @asyncio.coroutine def handle_token_retry_failure(websocket): """Continually close the socket, until both the first attempt and retry fail.""" with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=DeprecationWarning) yield from websocket.close() @asyncio.coroutine def handle_token_job_not_found(websocket): """Close the socket, specifying code for job not found.""" yield from websocket.close(code=4003) qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_provider.py0000664000372000037200000001675013616666011024646 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Tests for all IBMQ backends.""" from datetime import datetime from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.providers.exceptions import QiskitBackendNotFoundError from qiskit.providers.ibmq.accountprovider import AccountProvider from qiskit.providers.ibmq.ibmqbackend import IBMQSimulator, IBMQBackend from qiskit.qobj import QobjHeader from qiskit.test import providers from qiskit.compiler import assemble, transpile from qiskit.providers.models.backendproperties import BackendProperties from ..decorators import requires_provider, slow_test_on_device from ..ibmqtestcase import IBMQTestCase class TestAccountProvider(IBMQTestCase, providers.ProviderTestCase): """Tests for all the IBMQ backends through the new API.""" provider_cls = AccountProvider backend_name = 'ibmq_qasm_simulator' def setUp(self): """Required method for testing""" super().setUp() qr = QuantumRegister(1) cr = ClassicalRegister(1) self.qc1 = QuantumCircuit(qr, cr, name='circuit0') self.qc1.h(qr[0]) self.qc1.measure(qr, cr) @requires_provider def _get_provider(self, provider): """Return an instance of a Provider.""" # pylint: disable=arguments-differ return provider def test_remote_backends_exist_real_device(self): """Test if there are remote backends that are devices.""" remotes = self.provider.backends(simulator=False) self.assertTrue(remotes) def test_remote_backends_exist_simulator(self): """Test if there are remote backends that are simulators.""" remotes = self.provider.backends(simulator=True) self.assertTrue(remotes) def test_remote_backends_instantiate_simulators(self): """Test if remote backends that are simulators are an IBMQSimulator instance.""" remotes = self.provider.backends(simulator=True) for backend in remotes: with self.subTest(backend=backend): self.assertIsInstance(backend, IBMQSimulator) def test_remote_backend_status(self): """Test backend_status.""" for backend in self.provider.backends(): _ = backend.status() def test_remote_backend_configuration(self): """Test backend configuration.""" remotes = self.provider.backends() for backend in remotes: _ = backend.configuration() def test_remote_backend_properties(self): """Test backend properties.""" remotes = self.provider.backends(simulator=False) for backend in remotes: properties = backend.properties() if backend.configuration().simulator: self.assertEqual(properties, None) def test_remote_backend_pulse_defaults(self): """Test backend pulse defaults.""" remotes = self.provider.backends(simulator=False) for backend in remotes: defaults = backend.defaults() if backend.configuration().open_pulse: self.assertIsNotNone(defaults) else: self.assertIsNone(defaults) def test_qobj_headers_in_result_sims(self): """Test that the qobj headers are passed onto the results for sims.""" backends = self.provider.backends(simulator=True) custom_qobj_header = {'x': 1, 'y': [1, 2, 3], 'z': {'a': 4}} for backend in backends: with self.subTest(backend=backend): circuits = transpile(self.qc1, backend=backend) qobj = assemble(circuits, backend=backend) # Update the Qobj header. qobj.header = QobjHeader.from_dict(custom_qobj_header) qobj.experiments[0].header.some_field = 'extra info' result = backend.run(qobj).result() self.assertEqual(result.header.to_dict(), custom_qobj_header) self.assertEqual(result.results[0].header.some_field, 'extra info') @slow_test_on_device def test_qobj_headers_in_result_devices(self, provider, backend): """Test that the qobj headers are passed onto the results for devices.""" # pylint: disable=unused-argument backends = provider.backends(simulator=False, filters=lambda b: b.status().operational) custom_qobj_header = {'x': 1, 'y': [1, 2, 3], 'z': {'a': 4}} for backend_ in backends: with self.subTest(backend=backend_): circuits = transpile(self.qc1, backend=backend_) qobj = assemble(circuits, backend=backend_) # Update the Qobj header. qobj.header = QobjHeader.from_dict(custom_qobj_header) # Update the Qobj.experiment header. qobj.experiments[0].header.some_field = 'extra info' result = backend_.run(qobj).result() self.assertEqual(result.header.to_dict(), custom_qobj_header) self.assertEqual(result.results[0].header.some_field, 'extra info') def test_aliases(self): """Test that display names of devices map the regular names.""" aliased_names = self.provider.backends._aliased_backend_names() for display_name, backend_name in aliased_names.items(): with self.subTest(display_name=display_name, backend_name=backend_name): try: backend_by_name = self.provider.get_backend(backend_name) except QiskitBackendNotFoundError: # The real name of the backend might not exist pass else: backend_by_display_name = self.provider.get_backend( display_name) self.assertEqual(backend_by_name, backend_by_display_name) self.assertEqual( backend_by_display_name.name(), backend_name) def test_remote_backend_properties_filter_date(self): """Test backend properties filtered by date.""" backends = self.provider.backends(simulator=False) datetime_filter = datetime(2019, 2, 1).replace(tzinfo=None) for backend in backends: with self.subTest(backend=backend): properties = backend.properties(datetime=datetime_filter) if isinstance(properties, BackendProperties): last_update_date = properties.last_update_date.replace(tzinfo=None) self.assertLessEqual(last_update_date, datetime_filter) else: self.assertEqual(properties, None) def test_provider_backends(self): """Test provider_backends have correct attributes.""" provider_backends = {back for back in dir(self.provider.backends) if isinstance(getattr(self.provider.backends, back), IBMQBackend)} backends = {back.name().lower() for back in self.provider._backends.values()} self.assertEqual(provider_backends, backends) qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_integration.py0000664000372000037200000001315113616666011025327 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """IBMQ provider integration tests (compile and run).""" from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.result import Result from qiskit.execute import execute from qiskit.compiler import assemble, transpile from ..ibmqtestcase import IBMQTestCase from ..decorators import requires_provider, requires_device class TestIBMQIntegration(IBMQTestCase): """Qiskit's IBMQ Provider integration tests.""" seed = 42 def setUp(self): qr = QuantumRegister(1) cr = ClassicalRegister(1) self._qc1 = QuantumCircuit(qr, cr, name='qc1') self._qc2 = QuantumCircuit(qr, cr, name='qc2') self._qc1.measure(qr[0], cr[0]) self._qc2.x(qr[0]) self._qc2.measure(qr[0], cr[0]) @requires_provider def test_ibmq_result_fields(self, provider): """Test components of a result from a remote simulator.""" remote_backend = provider.get_backend(local=False, simulator=True) remote_result = execute(self._qc1, remote_backend).result() self.assertEqual(remote_result.backend_name, remote_backend.name()) self.assertIsInstance(remote_result.job_id, str) self.assertEqual(remote_result.status, 'COMPLETED') self.assertEqual(remote_result.results[0].status, 'DONE') @requires_device def test_compile_remote(self, backend): """Test Compiler remote.""" qubit_reg = QuantumRegister(2, name='q') clbit_reg = ClassicalRegister(2, name='c') qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) circuits = transpile(qc, backend=backend) self.assertIsInstance(circuits, QuantumCircuit) @requires_device def test_compile_two_remote(self, backend): """Test Compiler remote on two circuits.""" qubit_reg = QuantumRegister(2, name='q') clbit_reg = ClassicalRegister(2, name='c') qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) qc_extra = QuantumCircuit(qubit_reg, clbit_reg, name="extra") qc_extra.measure(qubit_reg, clbit_reg) circuits = transpile([qc, qc_extra], backend) self.assertIsInstance(circuits[0], QuantumCircuit) self.assertIsInstance(circuits[1], QuantumCircuit) @requires_provider def test_compile_run_remote(self, provider): """Test Compiler and run remote.""" backend = provider.get_backend(local=False, simulator=True) qubit_reg = QuantumRegister(2, name='q') clbit_reg = ClassicalRegister(2, name='c') qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) qobj = assemble(transpile(qc, backend=backend, seed_transpiler=self.seed), backend=backend) job = backend.run(qobj) result = job.result(timeout=20) self.assertIsInstance(result, Result) @requires_provider def test_compile_two_run_remote(self, provider): """Test Compiler and run two circuits.""" backend = provider.get_backend(local=False, simulator=True) qubit_reg = QuantumRegister(2, name='q') clbit_reg = ClassicalRegister(2, name='c') qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell") qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) qc_extra = QuantumCircuit(qubit_reg, clbit_reg, name="extra") qc_extra.measure(qubit_reg, clbit_reg) qobj = assemble(transpile([qc, qc_extra], backend=backend, seed_transpiler=self.seed), backend=backend) job = backend.run(qobj) result = job.result() self.assertIsInstance(result, Result) @requires_provider def test_execute_remote(self, provider): """Test Execute remote.""" backend = provider.get_backend(local=False, simulator=True) qubit_reg = QuantumRegister(2) clbit_reg = ClassicalRegister(2) qc = QuantumCircuit(qubit_reg, clbit_reg) qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) job = execute(qc, backend, seed_transpiler=self.seed) results = job.result() self.assertIsInstance(results, Result) @requires_provider def test_execute_two_remote(self, provider): """Test execute two remote.""" backend = provider.get_backend(local=False, simulator=True) qubit_reg = QuantumRegister(2) clbit_reg = ClassicalRegister(2) qc = QuantumCircuit(qubit_reg, clbit_reg) qc.h(qubit_reg[0]) qc.cx(qubit_reg[0], qubit_reg[1]) qc.measure(qubit_reg, clbit_reg) qc_extra = QuantumCircuit(qubit_reg, clbit_reg) qc_extra.measure(qubit_reg, clbit_reg) job = execute([qc, qc_extra], backend, seed_transpiler=self.seed) results = job.result() self.assertIsInstance(results, Result) qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_factory.py0000664000372000037200000002122213616666011024451 0ustar travistravis00000000000000# -*- 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. """Tests for the IBMQFactory.""" import os from unittest import skipIf from qiskit.providers.ibmq.accountprovider import AccountProvider from qiskit.providers.ibmq.api.exceptions import RequestsApiError from qiskit.providers.ibmq.exceptions import (IBMQAccountError, IBMQAccountCredentialsInvalidUrl, IBMQAccountCredentialsInvalidToken) from qiskit.providers.ibmq.ibmqfactory import IBMQFactory, QX_AUTH_URL from ..ibmqtestcase import IBMQTestCase from ..decorators import requires_qe_access from ..contextmanagers import (custom_qiskitrc, no_file, no_envs, CREDENTIAL_ENV_VARS) API_URL = 'https://api.quantum-computing.ibm.com/api' AUTH_URL = 'https://auth.quantum-computing.ibm.com/api' API1_URL = 'https://quantumexperience.ng.bluemix.net/api' class TestIBMQFactoryEnableAccount(IBMQTestCase): """Tests for IBMQFactory `enable_account()`.""" @requires_qe_access def test_auth_url(self, qe_token, qe_url): """Test login into an auth account.""" ibmq = IBMQFactory() provider = ibmq.enable_account(qe_token, qe_url) self.assertIsInstance(provider, AccountProvider) def test_old_api_url(self): """Test login into an API v1 auth account.""" qe_token = 'invalid' qe_url = API1_URL with self.assertRaises(IBMQAccountCredentialsInvalidUrl) as context_manager: ibmq = IBMQFactory() ibmq.enable_account(qe_token, qe_url) self.assertIn('authentication URL', str(context_manager.exception)) def test_non_auth_url(self): """Test login into a non-auth account.""" qe_token = 'invalid' qe_url = API_URL with self.assertRaises(IBMQAccountCredentialsInvalidUrl) as context_manager: ibmq = IBMQFactory() ibmq.enable_account(qe_token, qe_url) self.assertIn('authentication URL', str(context_manager.exception)) def test_non_auth_url_with_hub(self): """Test login into a non-auth account with h/g/p.""" qe_token = 'invalid' qe_url = API_URL + '/Hubs/X/Groups/Y/Projects/Z' with self.assertRaises(IBMQAccountCredentialsInvalidUrl) as context_manager: ibmq = IBMQFactory() ibmq.enable_account(qe_token, qe_url) self.assertIn('authentication URL', str(context_manager.exception)) @requires_qe_access def test_enable_twice(self, qe_token, qe_url): """Test login into an already logged-in account.""" ibmq = IBMQFactory() ibmq.enable_account(qe_token, qe_url) with self.assertRaises(IBMQAccountError) as context_manager: ibmq.enable_account(qe_token, qe_url) self.assertIn('already', str(context_manager.exception)) @requires_qe_access def test_enable_twice_invalid(self, qe_token, qe_url): """Test login into an invalid account during an already logged-in account.""" ibmq = IBMQFactory() ibmq.enable_account(qe_token, qe_url) with self.assertRaises(IBMQAccountError) as context_manager: qe_token_api1 = 'invalid' qe_url_api1 = API1_URL ibmq.enable_account(qe_token_api1, qe_url_api1) self.assertIn('already', str(context_manager.exception)) @requires_qe_access def test_pass_unreachable_proxy(self, qe_token, qe_url): """Test using an unreachable proxy while enabling an account.""" proxies = { 'urls': { 'http': 'http://user:password@127.0.0.1:5678', 'https': 'https://user:password@127.0.0.1:5678' } } ibmq = IBMQFactory() with self.assertRaises(RequestsApiError) as context_manager: ibmq.enable_account(qe_token, qe_url, proxies=proxies) self.assertIn('ProxyError', str(context_manager.exception)) @skipIf(os.name == 'nt', 'Test not supported in Windows') class TestIBMQFactoryAccounts(IBMQTestCase): """Tests for the IBMQ account handling.""" @classmethod def setUpClass(cls): cls.token = 'API_TOKEN' def setUp(self): super().setUp() # Reference for saving accounts. self.factory = IBMQFactory() def test_save_account(self): """Test saving an account.""" with custom_qiskitrc(): self.factory.save_account(self.token, url=AUTH_URL) stored_cred = self.factory.stored_account() self.assertEqual(stored_cred['token'], self.token) self.assertEqual(stored_cred['url'], AUTH_URL) def test_delete_account(self): """Test deleting an account.""" with custom_qiskitrc(): self.factory.save_account(self.token, url=AUTH_URL) self.factory.delete_account() stored_cred = self.factory.stored_account() self.assertEqual(len(stored_cred), 0) @requires_qe_access def test_load_account(self, qe_token, qe_url): """Test loading an account.""" if qe_url != QX_AUTH_URL: # .save_account() expects an auth production URL. self.skipTest('Test requires production auth URL') with no_file('Qconfig.py'), custom_qiskitrc(), no_envs(CREDENTIAL_ENV_VARS): self.factory.save_account(qe_token, url=qe_url) self.factory.load_account() self.assertEqual(self.factory._credentials.token, qe_token) self.assertEqual(self.factory._credentials.url, qe_url) @requires_qe_access def test_disable_account(self, qe_token, qe_url): """Test disabling an account """ self.factory.enable_account(qe_token, qe_url) self.factory.disable_account() self.assertIsNone(self.factory._credentials) @requires_qe_access def test_active_account(self, qe_token, qe_url): """Test active_account for an account """ self.assertIsNone(self.factory.active_account()) self.factory.enable_account(qe_token, qe_url) active_account = self.factory.active_account() self.assertIsNotNone(active_account) self.assertEqual(active_account['token'], qe_token) self.assertEqual(active_account['url'], qe_url) def test_save_none_token(self): """Test saving an account with token=None. See #391""" with self.assertRaises(IBMQAccountCredentialsInvalidToken) as context_manager: self.factory.save_account(None) self.assertIn('Invalid token found', str(context_manager.exception)) def test_save_empty_token(self): """Test saving an account with token=''. See #391""" with self.assertRaises(IBMQAccountCredentialsInvalidToken) as context_manager: self.factory.save_account('') self.assertIn('Invalid token found', str(context_manager.exception)) def test_save_zero_token(self): """Test saving an account with token=0. See #391""" with self.assertRaises(IBMQAccountCredentialsInvalidToken) as context_manager: self.factory.save_account(0) self.assertIn('Invalid token found', str(context_manager.exception)) class TestIBMQFactoryProvider(IBMQTestCase): """Tests for IBMQFactory provider related methods.""" @requires_qe_access def _get_provider(self, qe_token=None, qe_url=None): return self.ibmq.enable_account(qe_token, qe_url) def setUp(self): super().setUp() self.ibmq = IBMQFactory() self.provider = self._get_provider() self.credentials = self.provider.credentials def test_get_provider(self): """Test get single provider.""" provider = self.ibmq.get_provider( hub=self.credentials.hub, group=self.credentials.group, project=self.credentials.project) self.assertEqual(self.provider, provider) def test_providers_with_filter(self): """Test providers() with a filter.""" provider = self.ibmq.providers( hub=self.credentials.hub, group=self.credentials.group, project=self.credentials.project)[0] self.assertEqual(self.provider, provider) def test_providers_no_filter(self): """Test providers() without a filter.""" providers = self.ibmq.providers() self.assertIn(self.provider, providers) qiskit-ibmq-provider-0.4.6/test/ibmq/test_account_client.py0000664000372000037200000004242413616666011024773 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2018, 2020. # # 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. """Tests for the AccountClient for IBM Q Experience.""" import re import traceback from unittest import mock from collections import deque from urllib3.connectionpool import HTTPConnectionPool from urllib3.exceptions import MaxRetryError from requests.exceptions import RequestException from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.compiler import assemble, transpile from qiskit.providers.ibmq.apiconstants import ApiJobStatus from qiskit.providers.ibmq.api.clients import AccountClient, AuthClient from qiskit.providers.ibmq.api.exceptions import ApiError, RequestsApiError from qiskit.providers.jobstatus import JobStatus from ..ibmqtestcase import IBMQTestCase from ..decorators import requires_qe_access, requires_device, requires_provider from ..contextmanagers import custom_envs, no_envs class TestAccountClient(IBMQTestCase): """Tests for AccountClient.""" def setUp(self): qr = QuantumRegister(2) cr = ClassicalRegister(2) self.qc1 = QuantumCircuit(qr, cr, name='qc1') self.qc2 = QuantumCircuit(qr, cr, name='qc2') self.qc1.h(qr) self.qc2.h(qr[0]) self.qc2.cx(qr[0], qr[1]) self.qc1.measure(qr[0], cr[0]) self.qc1.measure(qr[1], cr[1]) self.qc2.measure(qr[0], cr[0]) self.qc2.measure(qr[1], cr[1]) self.seed = 73846087 @classmethod @requires_provider def setUpClass(cls, provider): # pylint: disable=arguments-differ cls.provider = provider cls.access_token = cls.provider._api.client_api.session.access_token def _get_client(self): """Helper for instantiating an AccountClient.""" return AccountClient(self.access_token, self.provider.credentials.url, self.provider.credentials.websockets_url, use_websockets=True) def test_job_submit(self): """Test job_submit, running a job against a simulator.""" # Create a Qobj. backend_name = 'ibmq_qasm_simulator' backend = self.provider.get_backend(backend_name) circuit = transpile(self.qc1, backend, seed_transpiler=self.seed) qobj = assemble(circuit, backend, shots=1) # Run the job through the AccountClient directly. api = backend._api job = api.job_submit(backend_name, qobj.to_dict(), use_object_storage=False) self.assertIn('status', job) self.assertIsNotNone(job['status']) def test_job_submit_object_storage(self): """Test running a job against a simulator using object storage.""" # Create a Qobj. backend_name = 'ibmq_qasm_simulator' backend = self.provider.get_backend(backend_name) circuit = transpile(self.qc1, backend, seed_transpiler=self.seed) qobj = assemble(circuit, backend, shots=1) # Run the job through the AccountClient directly using object storage. api = backend._api try: job = api._job_submit_object_storage(backend_name, qobj.to_dict()) except RequestsApiError as ex: # Get the original connection that was raised. original_exception = ex.__cause__ if isinstance(original_exception, RequestException): # Get the response from the original request exception. error_response = original_exception.response # pylint: disable=no-member if error_response is not None and error_response.status_code == 400: try: api_code = error_response.json()['error']['code'] # If we reach that point, it means the backend does not # support qobject storage. self.assertEqual(api_code, 'Q_OBJECT_STORAGE_IS_NOT_ALLOWED') return except (ValueError, KeyError): pass raise job_id = job['id'] self.assertEqual(job['kind'], 'q-object-external-storage') # Wait for completion. api.job_final_status(job_id) # Fetch results and qobj via object storage. result = api._job_result_object_storage(job_id) qobj_downloaded = api._job_download_qobj_object_storage(job_id) self.assertEqual(qobj_downloaded, qobj.to_dict()) self.assertEqual(result['status'], 'COMPLETED') def test_job_submit_object_storage_fallback(self): """Test job_submit fallback when object storage fails.""" # Create a Qobj. backend_name = 'ibmq_qasm_simulator' backend = self.provider.get_backend(backend_name) circuit = transpile(self.qc1, backend, seed_transpiler=self.seed) qobj = assemble(circuit, backend, shots=1) # Run via the AccountClient, making object storage fail. api = backend._api with mock.patch.object(api, '_job_submit_object_storage', side_effect=Exception()), \ mock.patch.object(api, '_job_submit_post') as mocked_post: _ = api.job_submit(backend_name, qobj.to_dict(), use_object_storage=True) # Assert the POST has been called. self.assertEqual(mocked_post.called, True) def test_list_jobs_statuses(self): """Check get status jobs by user authenticated.""" api = self._get_client() jobs = api.list_jobs_statuses(limit=2) self.assertEqual(len(jobs), 2) @requires_device def test_backend_status(self, backend): """Check the status of a real chip.""" api = self._get_client() is_available = api.backend_status(backend.name()) self.assertIsNotNone(is_available['operational']) @requires_device def test_backend_properties(self, backend): """Check the properties of calibration of a real chip.""" api = self._get_client() properties = api.backend_properties(backend.name()) self.assertIsNotNone(properties) @requires_device def test_backend_job_limit(self, backend): """Check the backend job limits of a real backend.""" api = self._get_client() job_limit = api.backend_job_limit(backend.name()) self.assertIsNotNone(job_limit) self.assertIsNotNone(job_limit['maximumJobs']) self.assertIsNotNone(job_limit['runningJobs']) self.assertNotEqual(job_limit['maximumJobs'], 0) def test_backend_pulse_defaults(self): """Check the backend pulse defaults of each backend.""" api = self._get_client() api_backends = api.list_backends() # TODO revert to testing all backends when api is fixed test_backend_names = ['ibmq_armonk', 'ibmq_vigo', 'ibmq_qasm_simulator'] for backend_info in api_backends: backend_name = backend_info['backend_name'] if backend_name not in test_backend_names: continue with self.subTest(backend_name=backend_name): defaults = api.backend_pulse_defaults(backend_name=backend_name) is_open_pulse = backend_info['open_pulse'] if is_open_pulse: self.assertTrue(defaults) else: self.assertFalse(defaults) def test_exception_message(self): """Check exception has proper message.""" api = self._get_client() with self.assertRaises(RequestsApiError) as exception_context: api.job_status('foo') raised_exception = exception_context.exception original_error = raised_exception.__cause__.response.json()['error'] self.assertIn(original_error['message'], raised_exception.message, "Original error message not in raised exception") self.assertIn(str(original_error['code']), raised_exception.message, "Original error code not in raised exception") def test_custom_client_app_header(self): """Check custom client application header.""" custom_header = 'batman' with custom_envs({'QE_CUSTOM_CLIENT_APP_HEADER': custom_header}): api = self._get_client() self.assertIn(custom_header, api.client_api.session.headers['X-Qx-Client-Application']) # Make sure the header is re-initialized with no_envs(['QE_CUSTOM_CLIENT_APP_HEADER']): api = self._get_client() self.assertNotIn(custom_header, api.client_api.session.headers['X-Qx-Client-Application']) def test_list_backends(self): """Test listing backends.""" api = self._get_client() provider_backends = {backend.name() for backend in self.provider.backends()} api_backends = {backend_info['backend_name'] for backend_info in api.list_backends()} self.assertEqual(provider_backends, api_backends) def test_job_cancel(self): """Test canceling a job.""" backend_name = 'ibmq_qasm_simulator' backend = self.provider.get_backend(backend_name) circuit = transpile(self.qc1, backend, seed_transpiler=self.seed) qobj = assemble(circuit, backend, shots=1) api = backend._api job = backend.run(qobj) job_id = job.job_id() max_retry = 2 for _ in range(max_retry): try: api.job_cancel(job_id) # TODO Change the warning back to assert once API is fixed # self.assertEqual(job.status(), JobStatus.CANCELLED) status = job.status() if status is not JobStatus.CANCELLED: self.log.warning("cancel() was successful for job %s but its status is %s.", job.job_id(), status) else: break except RequestsApiError as ex: if 'JOB_NOT_RUNNING' in str(ex): self.assertEqual(job.status(), JobStatus.DONE) break # We may hit the JOB_NOT_CANCELLED error if the job is # in a temporary, noncancellable state. In this case we'll # just retry. self.assertIn('JOB_NOT_CANCELLED', str(ex)) def test_access_token_not_in_exception_traceback(self): """Check that access token is replaced within chained request exceptions.""" backend = self.provider.backends.ibmq_qasm_simulator circuit = transpile(self.qc1, backend, seed_transpiler=self.seed) qobj = assemble(circuit, backend, shots=1) api = backend._api exception_message = 'The access token in this exception ' \ 'message should be replaced: {}'.format(self.access_token) exception_traceback_str = '' try: with mock.patch.object( HTTPConnectionPool, 'urlopen', side_effect=MaxRetryError( HTTPConnectionPool('host'), 'url', reason=exception_message)): _ = api.job_submit(backend.name(), qobj.to_dict(), use_object_storage=True) except RequestsApiError: exception_traceback_str = traceback.format_exc() self.assertTrue(exception_traceback_str) if self.access_token in exception_traceback_str: self.fail('Access token not replaced in request exception traceback.') class TestAccountClientJobs(IBMQTestCase): """Tests for AccountClient methods related to jobs. This TestCase submits a Job during class invocation, available at `cls.job`. Tests should inspect that job according to their needs. """ @classmethod @requires_provider def setUpClass(cls, provider): # pylint: disable=arguments-differ cls.provider = provider cls.access_token = cls.provider._api.client_api.session.access_token backend_name = 'ibmq_qasm_simulator' backend = cls.provider.get_backend(backend_name) cls.client = backend._api cls.job = cls.client.job_submit( backend_name, cls._get_qobj(backend).to_dict(), use_object_storage=backend.configuration().allow_object_storage) cls.job_id = cls.job['id'] @staticmethod def _get_qobj(backend): """Return a Qobj.""" # Create a circuit. qr = QuantumRegister(2) cr = ClassicalRegister(2) qc1 = QuantumCircuit(qr, cr, name='qc1') seed = 73846087 # Assemble the Qobj. qobj = assemble(transpile([qc1], backend=backend, seed_transpiler=seed), backend=backend, shots=1) return qobj def test_job_get(self): """Test job_get.""" response = self.client.job_get(self.job_id) self.assertIn('status', response) def test_job_status(self): """Test getting job status.""" response = self.client.job_status(self.job_id) self.assertIn('status', response) def test_job_final_status_websocket(self): """Test getting a job's final status via websocket.""" response = self.client._job_final_status_websocket(self.job_id) self.assertEqual(response.pop('status', None), ApiJobStatus.COMPLETED.value) def test_job_final_status_polling(self): """Test getting a job's final status via polling.""" status_deque = deque(maxlen=1) response = self.client._job_final_status_polling(self.job_id, status_deque=status_deque) self.assertEqual(response.pop('status', None), ApiJobStatus.COMPLETED.value) self.assertNotEqual(len(status_deque), 0) def test_job_properties(self): """Test getting job properties.""" # Force the job to finish. _ = self.client._job_final_status_websocket(self.job_id) response = self.client.job_properties(self.job_id) # Since the job is against a simulator, it will have no properties. self.assertFalse(response) def test_list_jobs_statuses_limit(self): """Test listing job statuses with a limit.""" jobs_raw = self.client.list_jobs_statuses(limit=1) self.assertEqual(len(jobs_raw), 1) def test_list_jobs_statuses_skip(self): """Test listing job statuses with an offset.""" jobs_raw = self.client.list_jobs_statuses(limit=1, skip=1, extra_filter={ 'creationDate': {'lte': self.job['creationDate']}}) # Ensure our job is skipped for job in jobs_raw: self.assertNotEqual(job['id'], self.job_id) def test_list_jobs_statuses_filter(self): """Test listing job statuses with a filter.""" jobs_raw = self.client.list_jobs_statuses(extra_filter={'id': self.job_id}) self.assertEqual(len(jobs_raw), 1) self.assertEqual(jobs_raw[0]['id'], self.job_id) class TestAuthClient(IBMQTestCase): """Tests for the AuthClient.""" @requires_qe_access def test_valid_login(self, qe_token, qe_url): """Test valid authenticating against IBM Q.""" client = AuthClient(qe_token, qe_url) self.assertTrue(client.client_api.session.access_token) @requires_qe_access def test_url_404(self, qe_token, qe_url): """Test login against a 404 URL""" url_404 = re.sub(r'/api.*$', '/api/TEST_404', qe_url) with self.assertRaises(ApiError): _ = AuthClient(qe_token, url_404) @requires_qe_access def test_invalid_token(self, qe_token, qe_url): """Test login using invalid token.""" qe_token = 'INVALID_TOKEN' with self.assertRaises(ApiError): _ = AuthClient(qe_token, qe_url) @requires_qe_access def test_url_unreachable(self, qe_token, qe_url): """Test login against an invalid (malformed) URL.""" qe_url = 'INVALID_URL' with self.assertRaises(ApiError): _ = AuthClient(qe_token, qe_url) @requires_qe_access def test_api_version(self, qe_token, qe_url): """Check the version of the QX API.""" api = AuthClient(qe_token, qe_url) version = api.api_version() self.assertIsNotNone(version) @requires_qe_access def test_user_urls(self, qe_token, qe_url): """Check the user urls of the QX API.""" api = AuthClient(qe_token, qe_url) user_urls = api.user_urls() self.assertIsNotNone(user_urls) self.assertTrue('http' in user_urls and 'ws' in user_urls) @requires_qe_access def test_user_hubs(self, qe_token, qe_url): """Check the user hubs of the QX API.""" api = AuthClient(qe_token, qe_url) user_hubs = api.user_hubs() self.assertIsNotNone(user_hubs) for user_hub in user_hubs: with self.subTest(user_hub=user_hub): self.assertTrue('hub' in user_hub and 'group' in user_hub and 'project' in user_hub) qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_qasm_simulator.py0000664000372000037200000001103313616666011026041 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Test IBMQ online qasm simulator.""" from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.compiler import assemble, transpile from ..ibmqtestcase import IBMQTestCase from ..decorators import requires_provider class TestIbmqQasmSimulator(IBMQTestCase): """Test IBM Q Qasm Simulator.""" @requires_provider def test_execute_one_circuit_simulator_online(self, provider): """Test execute_one_circuit_simulator_online.""" backend = provider.get_backend('ibmq_qasm_simulator') qr = QuantumRegister(1) cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr, name='qc') qc.h(qr[0]) qc.measure(qr[0], cr[0]) qobj = assemble(transpile(qc, backend=backend, seed_transpiler=73846087), backend=backend) shots = qobj.config.shots job = backend.run(qobj) result = job.result() counts = result.get_counts(qc) target = {'0': shots / 2, '1': shots / 2} threshold = 0.05 * shots self.assertDictAlmostEqual(counts, target, threshold) @requires_provider def test_execute_several_circuits_simulator_online(self, provider): """Test execute_several_circuits_simulator_online.""" backend = provider.get_backend('ibmq_qasm_simulator') qr = QuantumRegister(2) cr = ClassicalRegister(2) qcr1 = QuantumCircuit(qr, cr, name='qc1') qcr2 = QuantumCircuit(qr, cr, name='qc2') qcr1.h(qr) qcr2.h(qr[0]) qcr2.cx(qr[0], qr[1]) qcr1.measure(qr[0], cr[0]) qcr1.measure(qr[1], cr[1]) qcr2.measure(qr[0], cr[0]) qcr2.measure(qr[1], cr[1]) shots = 1024 qobj = assemble(transpile([qcr1, qcr2], backend=backend, seed_transpiler=73846087), backend=backend, shots=shots) job = backend.run(qobj) result = job.result() counts1 = result.get_counts(qcr1) counts2 = result.get_counts(qcr2) target1 = {'00': shots / 4, '01': shots / 4, '10': shots / 4, '11': shots / 4} target2 = {'00': shots / 2, '11': shots / 2} threshold = 0.05 * shots self.assertDictAlmostEqual(counts1, target1, threshold) self.assertDictAlmostEqual(counts2, target2, threshold) @requires_provider def test_online_qasm_simulator_two_registers(self, provider): """Test online_qasm_simulator_two_registers.""" backend = provider.get_backend('ibmq_qasm_simulator') qr1 = QuantumRegister(2) cr1 = ClassicalRegister(2) qr2 = QuantumRegister(2) cr2 = ClassicalRegister(2) qcr1 = QuantumCircuit(qr1, qr2, cr1, cr2, name="circuit1") qcr2 = QuantumCircuit(qr1, qr2, cr1, cr2, name="circuit2") qcr1.x(qr1[0]) qcr2.x(qr2[1]) qcr1.measure(qr1[0], cr1[0]) qcr1.measure(qr1[1], cr1[1]) qcr1.measure(qr2[0], cr2[0]) qcr1.measure(qr2[1], cr2[1]) qcr2.measure(qr1[0], cr1[0]) qcr2.measure(qr1[1], cr1[1]) qcr2.measure(qr2[0], cr2[0]) qcr2.measure(qr2[1], cr2[1]) qobj = assemble(transpile([qcr1, qcr2], backend, seed_transpiler=8458), backend=backend, shots=1024) job = backend.run(qobj) result = job.result() result1 = result.get_counts(qcr1) result2 = result.get_counts(qcr2) self.assertEqual(result1, {'00 01': 1024}) self.assertEqual(result2, {'10 00': 1024}) @requires_provider def test_conditional_operation(self, provider): """Test conditional operation.""" backend = provider.get_backend('ibmq_qasm_simulator') qr = QuantumRegister(4) cr = ClassicalRegister(4) circuit = QuantumCircuit(qr, cr) circuit.x(qr[0]) circuit.x(qr[2]) circuit.measure(qr[0], cr[0]) circuit.x(qr[0]).c_if(cr, 1) qobj = assemble(transpile(circuit, backend=backend)) result = backend.run(qobj).result() self.assertEqual(result.get_counts(circuit), {'0001': 1024}) qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_job_states.py0000664000372000037200000004621013616666011025143 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. # pylint: disable=missing-docstring """IBMQJob states test-suite.""" import time import copy from contextlib import suppress from unittest import mock from qiskit.providers.ibmq.apiconstants import API_JOB_FINAL_STATES, ApiJobStatus from qiskit.test.mock import FakeQobj from qiskit.providers import JobTimeoutError from qiskit.providers.ibmq.job.exceptions import IBMQJobApiError, IBMQJobInvalidStateError from qiskit.providers.ibmq.api.exceptions import (ApiError, UserTimeoutExceededError, ApiIBMQProtocolError) from qiskit.providers.ibmq.exceptions import IBMQBackendError from qiskit.providers.jobstatus import JobStatus from qiskit.providers.ibmq.ibmqbackend import IBMQBackend from ..jobtestcase import JobTestCase MOCKED_ERROR_RESULT = { 'qObjectResult': { 'results': [ { 'status': 'DONE', 'success': True }, { 'status': 'Error 1', 'success': False }, { 'status': 'Error 2', 'success': False } ] } } VALID_QOBJ_RESPONSE = { 'status': 'COMPLETED', 'kind': 'q-object', 'creationDate': '2019-01-01T12:57:15.052Z', 'id': '0123456789', 'qObjectResult': { 'backend_name': 'ibmqx2', 'backend_version': '1.1.1', 'job_id': 'XC1323XG2', 'qobj_id': 'Experiment1', 'success': True, 'status': 'COMPLETED', 'results': [ { 'header': { 'name': 'Bell state', 'memory_slots': 2, 'creg_sizes': [['c', 2]], 'clbit_labels': [['c', 0], ['c', 1]], 'qubit_labels': [['q', 0], ['q', 1]] }, 'shots': 1024, 'status': 'DONE', 'success': True, 'data': { 'counts': { '0x0': 480, '0x3': 490, '0x1': 20, '0x2': 34 } } }, { 'header': { 'name': 'Bell state XY', 'memory_slots': 2, 'creg_sizes': [['c', 2]], 'clbit_labels': [['c', 0], ['c', 1]], 'qubit_labels': [['q', 0], ['q', 1]] }, 'shots': 1024, 'status': 'DONE', 'success': True, 'data': { 'counts': { '0x0': 29, '0x3': 15, '0x1': 510, '0x2': 480 } } } ] } } VALID_JOB_RESPONSE = { 'id': 'TEST_ID', 'kind': 'q-object', 'status': 'CREATING', 'creationDate': '2019-01-01T13:15:58.425972' } class TestIBMQJobStates(JobTestCase): """Test the states of an IBMQJob.""" def setUp(self): self._current_api = None self._current_qjob = None def test_unrecognized_status(self): job = self.run_with_api(UnknownStatusAPI()) with self.assertRaises(IBMQJobApiError): self.wait_for_initialization(job) def test_done_status(self): """Test checking whether the job status is done while progressing job states.""" job = self.run_with_api(QueuedAPI()) self.assertFalse(job.done()) self.wait_for_initialization(job) self._current_api.progress() self.assertFalse(job.done()) self._current_api.progress() self.assertTrue(job.done()) def test_running_status(self): """Test checking whether the job status is running while progressing job states.""" job = self.run_with_api(ValidatingAPI()) self.assertFalse(job.running()) self.wait_for_initialization(job) self._current_api.progress() self.assertTrue(job.running()) def test_cancelled_status(self): """Test checking whether the job status is cancelled while progressing job states.""" job = self.run_with_api(CancellableAPI()) self.assertFalse(job.cancelled()) self.wait_for_initialization(job) self._current_api.progress() self.assertTrue(job.cancelled()) def test_validating_job(self): job = self.run_with_api(ValidatingAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.VALIDATING) def test_error_while_creating_job(self): job = self.run_with_api(ErrorWhileCreatingAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.ERROR) def test_error_while_validating_job(self): job = self.run_with_api(ErrorWhileValidatingAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.VALIDATING) self._current_api.progress() self.assertEqual(job.status(), JobStatus.ERROR) def test_status_flow_for_non_queued_job(self): job = self.run_with_api(NonQueuedAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.RUNNING) self._current_api.progress() self.assertEqual(job.status(), JobStatus.DONE) def test_status_flow_for_queued_job(self): job = self.run_with_api(QueuedAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.QUEUED) self._current_api.progress() self.assertEqual(job.status(), JobStatus.RUNNING) self._current_api.progress() self.assertEqual(job.status(), JobStatus.DONE) def test_status_flow_for_cancellable_job(self): job = self.run_with_api(CancellableAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.RUNNING) can_cancel = job.cancel() self.assertTrue(can_cancel) self._current_api.progress() self.assertEqual(job.status(), JobStatus.CANCELLED) def test_status_flow_for_non_cancellable_job(self): job = self.run_with_api(NonCancellableAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.RUNNING) can_cancel = job.cancel() self.assertFalse(can_cancel) self._current_api.progress() self.assertEqual(job.status(), JobStatus.RUNNING) def test_status_flow_for_errored_cancellation(self): job = self.run_with_api(ErroredCancellationAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.RUNNING) can_cancel = job.cancel() self.assertFalse(can_cancel) self.assertEqual(job.status(), JobStatus.RUNNING) def test_status_flow_for_unable_to_run_valid_qobj(self): with self.assertRaises(IBMQBackendError): self.run_with_api(UnavailableRunAPI()) def test_api_throws_temporarily_but_job_is_finished(self): job = self.run_with_api(ThrowingNonJobRelatedErrorAPI(errors_before_success=2)) # First time we query the server... with self.assertRaises(IBMQJobApiError): # The error happens inside wait_for_initialization, the first time # it calls to status() after INITIALIZING. self.wait_for_initialization(job) # Also an explicit second time... with self.assertRaises(IBMQJobApiError): job.status() # Now the API gets fixed and doesn't throw anymore. self.assertEqual(job.status(), JobStatus.DONE) def test_error_while_running_job(self): job = self.run_with_api(ErrorWhileRunningAPI()) self.wait_for_initialization(job) self.assertEqual(job.status(), JobStatus.RUNNING) self._current_api.progress() self.assertEqual(job.status(), JobStatus.ERROR) self.assertIn('Error 1', job.error_message()) self.assertIn('Error 2', job.error_message()) def test_cancelled_result(self): job = self.run_with_api(CancellableAPI()) self.wait_for_initialization(job) job.cancel() self._current_api.progress() with self.assertRaises(IBMQJobInvalidStateError): _ = job.result() self.assertEqual(job.status(), JobStatus.CANCELLED) def test_errored_result(self): job = self.run_with_api(ThrowingGetJobAPI()) self.wait_for_initialization(job) with self.assertRaises(IBMQJobApiError): job.result() def test_completed_result(self): job = self.run_with_api(NonQueuedAPI()) self.wait_for_initialization(job) self._current_api.progress() self.assertEqual(job.result().success, True) self.assertEqual(job.status(), JobStatus.DONE) def test_block_on_result_waiting_until_completed(self): from concurrent import futures job = self.run_with_api(NonQueuedAPI()) with futures.ThreadPoolExecutor() as executor: executor.submit(_auto_progress_api, self._current_api) result = job.result() self.assertEqual(result.success, True) self.assertEqual(job.status(), JobStatus.DONE) def test_block_on_result_waiting_until_cancelled(self): from concurrent.futures import ThreadPoolExecutor job = self.run_with_api(CancellableAPI()) with ThreadPoolExecutor() as executor: executor.submit(_auto_progress_api, self._current_api) with self.assertRaises(IBMQJobInvalidStateError): job.result() self.assertEqual(job.status(), JobStatus.CANCELLED) def test_block_on_result_waiting_until_exception(self): from concurrent.futures import ThreadPoolExecutor job = self.run_with_api(ThrowingAPI()) with ThreadPoolExecutor() as executor: executor.submit(_auto_progress_api, self._current_api) with self.assertRaises(IBMQJobApiError): job.result() def test_never_complete_result_with_timeout(self): job = self.run_with_api(NonQueuedAPI()) self.wait_for_initialization(job) with self.assertRaises(JobTimeoutError): job.result(timeout=0.2) def test_only_final_states_cause_detailed_request(self): # The state ERROR_CREATING_JOB is only handled when running the job, # and not while checking the status, so it is not tested. all_state_apis = {'COMPLETED': NonQueuedAPI, 'CANCELLED': CancellableAPI, 'ERROR_VALIDATING_JOB': ErrorWhileValidatingAPI, 'ERROR_RUNNING_JOB': ErrorWhileRunningAPI} for status, api in all_state_apis.items(): with self.subTest(status=status): job = self.run_with_api(api()) self.wait_for_initialization(job) with suppress(BaseFakeAPI.NoMoreStatesError): self._current_api.progress() with mock.patch.object(self._current_api, 'job_get', wraps=self._current_api.job_get): job.status() if ApiJobStatus(status) in API_JOB_FINAL_STATES: self.assertTrue(self._current_api.job_get.called) else: self.assertFalse(self._current_api.job_get.called) def test_no_kind_job(self): """Test a job without the kind field.""" job = self.run_with_api(NoKindJobAPI()) with self.assertRaises(IBMQJobInvalidStateError): job.result() self.assertIsNone(job.qobj()) def run_with_api(self, api): """Creates a new ``IBMQJob`` running with the provided API object.""" backend = IBMQBackend(mock.Mock(), mock.Mock(), mock.Mock(), api=api) self._current_api = api self._current_qjob = backend.run(qobj=FakeQobj()) self._current_qjob.refresh = mock.Mock() return self._current_qjob def _auto_progress_api(api, interval=0.2): """Progress a `BaseFakeAPI` instance every `interval` seconds until reaching the final state. """ with suppress(BaseFakeAPI.NoMoreStatesError): while True: time.sleep(interval) api.progress() class BaseFakeAPI: """Base class for faking the IBM-Q API.""" class NoMoreStatesError(Exception): """Raised when it is not possible to progress more.""" _job_status = [] _can_cancel = False def __init__(self): self._state = 0 self.config = {'hub': None, 'group': None, 'project': None} if self._can_cancel: self.config.update({ 'hub': 'test-hub', 'group': 'test-group', 'project': 'test-project' }) def job_get(self, job_id): if not job_id: return {'status': 'Error', 'error': 'Job ID not specified'} return self._job_status[self._state] def job_status(self, job_id): summary_fields = ['status', 'error', 'infoQueue'] complete_response = self.job_get(job_id) try: ApiJobStatus(complete_response['status']) except ValueError: raise ApiIBMQProtocolError('Api Error') return {key: value for key, value in complete_response.items() if key in summary_fields} def job_submit(self, *_args, **_kwargs): time.sleep(0.2) return VALID_JOB_RESPONSE def job_cancel(self, job_id, *_args, **_kwargs): if not job_id: return {'status': 'Error', 'error': 'Job ID not specified'} return {'cancelled': True} if self._can_cancel else { 'error': 'testing fake API can not cancel'} def job_final_status(self, job_id, *_args, **_kwargs): start_time = time.time() status_response = self.job_status(job_id) while ApiJobStatus(status_response['status']) not in API_JOB_FINAL_STATES: elapsed_time = time.time() - start_time timeout = _kwargs.get('timeout', None) if timeout is not None and elapsed_time >= timeout: raise UserTimeoutExceededError( 'Timeout while waiting for job {}'.format(job_id)) time.sleep(5) status_response = self.job_status(job_id) return status_response def job_result(self, job_id, *_args, **_kwargs): return self.job_get(job_id)['qObjectResult'] def progress(self): if self._state == len(self._job_status) - 1: raise self.NoMoreStatesError() self._state += 1 class UnknownStatusAPI(BaseFakeAPI): """Class for emulating an API with unknown status codes.""" _job_status = [ {'status': 'UNKNOWN'} ] class ValidatingAPI(BaseFakeAPI): """Class for emulating an API with job validation.""" _job_status = [ {'status': 'VALIDATING'}, {'status': 'RUNNING'} ] class ErrorWhileValidatingAPI(BaseFakeAPI): """Class for emulating an API processing an invalid job.""" _job_status = [ {'status': 'VALIDATING'}, {'status': 'ERROR_VALIDATING_JOB', **MOCKED_ERROR_RESULT} ] class NonQueuedAPI(BaseFakeAPI): """Class for emulating a successfully-completed non-queued API.""" _job_status = [ {'status': 'RUNNING'}, VALID_QOBJ_RESPONSE ] class ErrorWhileCreatingAPI(BaseFakeAPI): """Class emulating an API processing a job that errors while creating the job. """ _job_status = [ {'status': 'ERROR_CREATING_JOB', **MOCKED_ERROR_RESULT} ] class ErrorWhileRunningAPI(BaseFakeAPI): """Class emulating an API processing a job that errors while running.""" _job_status = [ {'status': 'RUNNING'}, {'status': 'ERROR_RUNNING_JOB', **MOCKED_ERROR_RESULT} ] class QueuedAPI(BaseFakeAPI): """Class for emulating a successfully-completed queued API.""" _job_status = [ {'status': 'RUNNING', 'infoQueue': {'status': 'PENDING_IN_QUEUE'}}, {'status': 'RUNNING'}, {'status': 'COMPLETED'} ] class RejectingJobAPI(BaseFakeAPI): """Class for emulating an API unable of initializing.""" def job_submit(self, *_args, **_kwargs): return {'error': 'invalid qobj'} class UnavailableRunAPI(BaseFakeAPI): """Class for emulating an API throwing before even initializing.""" def job_submit(self, *_args, **_kwargs): time.sleep(0.2) raise ApiError('Api Error') class ThrowingAPI(BaseFakeAPI): """Class for emulating an API throwing in the middle of execution.""" _job_status = [ {'status': 'RUNNING'} ] def job_get(self, job_id): raise ApiError('Api Error') class ThrowingNonJobRelatedErrorAPI(BaseFakeAPI): """Class for emulating an scenario where the job is done but the API fails some times for non job-related errors. """ _job_status = [ {'status': 'COMPLETED'} ] def __init__(self, errors_before_success=2): super().__init__() self._number_of_exceptions_to_throw = errors_before_success def job_get(self, job_id): if self._number_of_exceptions_to_throw != 0: self._number_of_exceptions_to_throw -= 1 raise ApiError('Api Error') return super().job_get(job_id) class ThrowingGetJobAPI(BaseFakeAPI): """Class for emulating an API throwing in the middle of execution. But not in get_status_job(), just in job_get(). """ _job_status = [ {'status': 'COMPLETED'} ] def job_status(self, job_id): return self._job_status[self._state] def job_get(self, job_id): raise ApiError('Unexpected error') class CancellableAPI(BaseFakeAPI): """Class for emulating an API with cancellation.""" _job_status = [ {'status': 'RUNNING'}, {'status': 'CANCELLED'} ] _can_cancel = True class NonCancellableAPI(BaseFakeAPI): """Class for emulating an API without cancellation running a long job.""" _job_status = [ {'status': 'RUNNING'}, {'status': 'RUNNING'}, {'status': 'RUNNING'} ] class ErroredCancellationAPI(BaseFakeAPI): """Class for emulating an API with cancellation but throwing while trying. """ _job_status = [ {'status': 'RUNNING'}, {'status': 'RUNNING'}, {'status': 'RUNNING'} ] _can_cancel = True def job_cancel(self, job_id, *_args, **_kwargs): return {'status': 'Error', 'error': 'test-error-while-cancelling'} class NoKindJobAPI(BaseFakeAPI): """Class for emulating an API with qasm jobs.""" _job_status = [ {'status': 'COMPLETED'} ] no_kind_response = copy.deepcopy(VALID_JOB_RESPONSE) del no_kind_response['kind'] def job_submit(self, *_args, **_kwargs): return self.no_kind_response def job_result(self, job_id, *_args, **_kwargs): return self.no_kind_response qiskit-ibmq-provider-0.4.6/test/ibmq/test_proxies.py0000664000372000037200000001615613616666011023475 0ustar travistravis00000000000000# -*- 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. """Tests for the AuthClient and VersionClient proxy support.""" from unittest import skipIf import urllib import subprocess import sys from requests.exceptions import ProxyError from qiskit.providers.ibmq import IBMQFactory from qiskit.providers.ibmq.api.clients import (AuthClient, VersionClient) from qiskit.providers.ibmq.api.exceptions import RequestsApiError from qiskit.providers.ibmq.credentials import Credentials from ..ibmqtestcase import IBMQTestCase from ..decorators import requires_qe_access # Fallback mechanism. Version variable is stored under __doc__ in new pproxy versions try: from pproxy.__doc__ import __version__ as pproxy_version except ImportError: from pproxy import __version__ as pproxy_version ADDRESS = '127.0.0.1' PORT = 8080 VALID_PROXIES = {'https': 'http://{}:{}'.format(ADDRESS, PORT)} INVALID_PORT_PROXIES = {'https': 'http://{}:{}'.format(ADDRESS, '6666')} INVALID_ADDRESS_PROXIES = {'https': 'http://{}:{}'.format('invalid', PORT)} @skipIf((sys.version_info > (3, 5) and pproxy_version == '1.2.2') or (sys.version_info == (3, 5) and pproxy_version > '1.2.2'), 'pproxy version is not supported') class TestProxies(IBMQTestCase): """Tests for proxy capabilities.""" def setUp(self): super().setUp() listen_flag = '-l' if pproxy_version >= '1.7.2' else '-i' # launch a mock server. command = ['pproxy', '-v', listen_flag, 'http://{}:{}'.format(ADDRESS, PORT)] self.proxy_process = subprocess.Popen(command, stdout=subprocess.PIPE) def tearDown(self): super().tearDown() # terminate the mock server. if self.proxy_process.returncode is None: self.proxy_process.stdout.close() # close the IO buffer self.proxy_process.terminate() # initiate process termination # wait for the process to terminate self.proxy_process.wait() @requires_qe_access def test_proxies_factory(self, qe_token, qe_url): """Should reach the proxy using factory.enable_account.""" factory = IBMQFactory() provider = factory.enable_account(qe_token, qe_url, proxies={'urls': VALID_PROXIES}) self.proxy_process.terminate() # kill to be able of reading the output auth_line = pproxy_desired_access_log_line(qe_url) api_line = pproxy_desired_access_log_line(provider.credentials.url) proxy_output = self.proxy_process.stdout.read().decode('utf-8') # Check if the authentication call went through proxy. self.assertIn(auth_line, proxy_output) # Check if the API call (querying providers list) went through proxy. self.assertIn(api_line, proxy_output) @requires_qe_access def test_proxies_authclient(self, qe_token, qe_url): """Should reach the proxy using AuthClient.""" pproxy_desired_access_log_line_ = pproxy_desired_access_log_line(qe_url) _ = AuthClient(qe_token, qe_url, proxies=VALID_PROXIES) self.proxy_process.terminate() # kill to be able of reading the output self.assertIn(pproxy_desired_access_log_line_, self.proxy_process.stdout.read().decode('utf-8')) # pylint: disable=unused-argument @requires_qe_access def test_proxies_versionclient(self, qe_token, qe_url): """Should reach the proxy using IBMQVersionFinder.""" pproxy_desired_access_log_line_ = pproxy_desired_access_log_line(qe_url) version_finder = VersionClient(qe_url, proxies=VALID_PROXIES) version_finder.version() self.proxy_process.terminate() # kill to be able of reading the output self.assertIn(pproxy_desired_access_log_line_, self.proxy_process.stdout.read().decode('utf-8')) @requires_qe_access def test_invalid_proxy_port_authclient(self, qe_token, qe_url): """Should raise RequestApiError with ProxyError using AuthClient.""" with self.assertRaises(RequestsApiError) as context_manager: _ = AuthClient(qe_token, qe_url, proxies=INVALID_PORT_PROXIES) self.assertIsInstance(context_manager.exception.__cause__, ProxyError) # pylint: disable=unused-argument @requires_qe_access def test_invalid_proxy_port_versionclient(self, qe_token, qe_url): """Should raise RequestApiError with ProxyError using VersionClient.""" with self.assertRaises(RequestsApiError) as context_manager: version_finder = VersionClient(qe_url, proxies=INVALID_PORT_PROXIES) version_finder.version() self.assertIsInstance(context_manager.exception.__cause__, ProxyError) @requires_qe_access def test_invalid_proxy_address_authclient(self, qe_token, qe_url): """Should raise RequestApiError with ProxyError using AuthClient.""" with self.assertRaises(RequestsApiError) as context_manager: _ = AuthClient(qe_token, qe_url, proxies=INVALID_ADDRESS_PROXIES) self.assertIsInstance(context_manager.exception.__cause__, ProxyError) @requires_qe_access def test_invalid_proxy_address_versionclient(self, qe_token, qe_url): """Should raise RequestApiError with ProxyError using VersionClient.""" # pylint: disable=unused-argument with self.assertRaises(RequestsApiError) as context_manager: version_finder = VersionClient(qe_url, proxies=INVALID_ADDRESS_PROXIES) version_finder.version() self.assertIsInstance(context_manager.exception.__cause__, ProxyError) @requires_qe_access def test_proxy_urls(self, qe_token, qe_url): """Test different forms of the proxy urls.""" test_urls = [ '{}:{}'.format(ADDRESS, PORT), 'http://{}:{}'.format(ADDRESS, PORT), '//{}:{}'.format(ADDRESS, PORT), 'http:{}:{}'.format(ADDRESS, PORT), 'http://user:123@{}:{}'.format(ADDRESS, PORT) ] for proxy_url in test_urls: with self.subTest(proxy_url=proxy_url): credentials = Credentials( qe_token, qe_url, proxies={'urls': {'https': proxy_url}}) version_finder = VersionClient(credentials.base_url, **credentials.connection_parameters()) version_finder.version() def pproxy_desired_access_log_line(url): """Return a desired pproxy log entry given a url.""" qe_url_parts = urllib.parse.urlparse(url) protocol_port = '443' if qe_url_parts.scheme == 'https' else '80' return '{}:{}'.format(qe_url_parts.hostname, protocol_port) qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_jobmanager.py0000664000372000037200000004317513616666011025122 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2020. # # 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. """Tests for the IBMQJobManager.""" import copy import time from unittest import mock from inspect import getfullargspec, isfunction import uuid from qiskit import QuantumCircuit from qiskit.result import Result from qiskit.providers.ibmq.managed.ibmqjobmanager import IBMQJobManager from qiskit.providers.ibmq.managed.managedresults import ManagedResults from qiskit.providers.ibmq.managed.exceptions import ( IBMQJobManagerJobNotFound, IBMQManagedResultDataNotAvailable, IBMQJobManagerInvalidStateError) from qiskit.providers.jobstatus import JobStatus, JOB_FINAL_STATES from qiskit.providers import JobError from qiskit.providers.ibmq.ibmqbackend import IBMQBackend from qiskit.providers.ibmq.exceptions import IBMQBackendError from qiskit.compiler import transpile, assemble from ..ibmqtestcase import IBMQTestCase from ..decorators import requires_provider from ..fake_account_client import BaseFakeAccountClient class TestIBMQJobManager(IBMQTestCase): """Tests for IBMQJobManager.""" def setUp(self): self._qc = _bell_circuit() self._jm = IBMQJobManager() @requires_provider def test_split_circuits(self, provider): """Test having circuits split into multiple jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(max_circs+2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) job_set.results() statuses = job_set.statuses() self.assertEqual(len(statuses), 2) self.assertTrue(all(s is JobStatus.DONE for s in statuses)) self.assertTrue(len(job_set.jobs()), 2) @requires_provider def test_no_split_circuits(self, provider): """Test running all circuits in a single job.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(int(max_circs/2)): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) self.assertTrue(len(job_set.jobs()), 1) @requires_provider def test_custom_split_circuits(self, provider): """Test having circuits split with custom slices.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) self.assertTrue(len(job_set.jobs()), 2) @requires_provider def test_job_report(self, provider): """Test job report.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() report = self._jm.report() for job in jobs: self.assertIn(job.job_id(), report) @requires_provider def test_skipped_status(self, provider): """Test one of jobs has no status.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() jobs[1]._job_id = 'BAD_ID' statuses = job_set.statuses() self.assertIsNone(statuses[1]) @requires_provider def test_job_qobjs(self, provider): """Test retrieving qobjs for the jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() provider._api = backend._api qc2 = QuantumCircuit(1, 1) qc2.x(0) qc2.measure(0, 0) circs = [self._qc, qc2] job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() job_set.results() for i, qobj in enumerate(job_set.qobjs()): rjob = provider.backends.retrieve_job(jobs[i].job_id()) self.assertDictEqual(qobj.__dict__, rjob.qobj().__dict__) @requires_provider def test_error_message(self, provider): """Test error message report.""" backend = provider.get_backend('ibmq_qasm_simulator') # Create a bad job. qc_new = transpile(self._qc, backend) qobj = assemble([qc_new, qc_new], backend=backend) qobj.experiments[1].instructions[1].name = 'bad_instruction' job = backend.run(qobj) circs = [] for _ in range(4): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=2) job_set.results() job_set.managed_jobs()[1].job = job error_report = job_set.error_messages() self.assertIsNotNone(error_report) self.assertIn(job.job_id(), error_report) @requires_provider def test_async_submit_exception(self, provider): """Test asynchronous job submit failed.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) with mock.patch.object(IBMQBackend, 'run', side_effect=[IBMQBackendError("Kaboom!"), mock.DEFAULT]): job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) self.assertIsNone(job_set.jobs()[0]) self.assertIsNotNone(job_set.jobs()[1]) # Make sure results() and statuses() don't fail job_set.results() job_set.statuses() @requires_provider def test_multiple_job_sets(self, provider): """Test submitting multiple sets of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() qc2 = QuantumCircuit(1, 1) qc2.h(0) qc2.measure([0], [0]) job_set1 = self._jm.run([self._qc, self._qc], backend=backend, max_experiments_per_job=1) job_set2 = self._jm.run([qc2], backend=backend, max_experiments_per_job=1) id1 = {job.job_id() for job in job_set1.jobs()} id2 = {job.job_id() for job in job_set2.jobs()} self.assertTrue(id1.isdisjoint(id2)) @requires_provider def test_retrieve_job_sets_by_name(self, provider): """Test retrieving job sets by name.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() name = str(time.time()).replace('.', '') self._jm.run([self._qc], backend=backend, max_experiments_per_job=1) job_set = self._jm.run([self._qc, self._qc], backend=backend, name=name, max_experiments_per_job=1) rjob_set = self._jm.job_sets(name=name)[0] self.assertEqual(job_set, rjob_set) @requires_provider def test_retrieve_job_set(self, provider): """Test retrieving a set of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') tags = ['test_retrieve_job_set'] circs_counts = [3, 4] for count in circs_counts: with self.subTest(count=count): circs = [] for i in range(count): new_qc = copy.deepcopy(self._qc) new_qc.name = "test_qc_{}".format(i) circs.append(new_qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=2, job_tags=tags) self.assertEqual(job_set.tags(), tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) rjob_set = IBMQJobManager().retrieve_job_set( job_set_id=job_set.job_set_id(), provider=provider) self.assertEqual({job.job_id() for job in job_set.jobs()}, {rjob.job_id() for rjob in rjob_set.jobs()}, "Unexpected jobs retrieved. Job set id used was {}.".format( job_set.job_set_id())) self.assertEqual(rjob_set.tags(), job_set.tags()) self.assertEqual(len(rjob_set.qobjs()), len(job_set.qobjs())) self.log.info("Job set report:\n%s", rjob_set.report()) mjobs = job_set.managed_jobs() for index, rmjob in enumerate(rjob_set.managed_jobs()): mjob = mjobs[index] self.assertEqual(rmjob.start_index, mjob.start_index) self.assertEqual(rmjob.end_index, mjob.end_index) for exp_index, exp in enumerate(rmjob.job.qobj().experiments): self.assertEqual(exp.header.name, mjob.job.qobj().experiments[exp_index].header.name) rjob_set.results() self.assertEqual(rjob_set.statuses(), [JobStatus.DONE]*len(job_set.jobs())) @requires_provider def test_share_job_in_project(self, provider): """Test sharing managed jobs within a project.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1, job_share_level="project") for job in job_set.jobs(): job.refresh() self.assertEqual(getattr(job, 'share_level'), 'project', "Job {} has incorrect share level".format(job.job_id())) @requires_provider def test_invalid_job_share_level(self, provider): """Test setting a non existent share level for managed jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) self.assertRaises(IBMQJobManagerInvalidStateError, self._jm.run, circs, backend=backend, job_share_level="invalid_job_share_level") @requires_provider def test_job_tags(self, provider): """Test job tags for managed jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_tags = [uuid.uuid4().hex] job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1, job_tags=job_tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) # TODO No need to wait for job to run once api is fixed while any(status not in JOB_FINAL_STATES + (JobStatus.RUNNING,) for status in job_set.statuses()): time.sleep(0.5) rjobs = provider.backends.jobs(job_tags=job_tags) self.assertEqual({job.job_id() for job in job_set.jobs()}, {rjob.job_id() for rjob in rjobs}, "Unexpected jobs retrieved. Job tag used was {}".format(job_tags)) self.assertEqual(job_set.tags(), job_tags) class TestResultManager(IBMQTestCase): """Tests for ResultManager.""" def setUp(self): self._qc = _bell_circuit() self._jm = IBMQJobManager() @requires_provider def test_index_by_number(self, provider): """Test indexing results by number.""" backend = provider.get_backend('ibmq_qasm_simulator') max_per_job = 5 circs = [] for _ in range(max_per_job*2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=max_per_job) result_manager = job_set.results() jobs = job_set.jobs() for i in [0, max_per_job-1, max_per_job+1]: with self.subTest(i=i): job_index = int(i / max_per_job) exp_index = i % max_per_job self.assertEqual(result_manager.get_counts(i), jobs[job_index].result().get_counts(exp_index)) @requires_provider def test_index_by_name(self, provider): """Test indexing results by name.""" backend = provider.get_backend('ibmq_qasm_simulator') max_per_job = 5 circs = [] for i in range(max_per_job*2+1): new_qc = copy.deepcopy(self._qc) new_qc.name = "test_qc_{}".format(i) circs.append(new_qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=max_per_job) result_manager = job_set.results() jobs = job_set.jobs() for i in [1, max_per_job, len(circs)-1]: with self.subTest(i=i): job_index = int(i / max_per_job) exp_index = i % max_per_job self.assertEqual(result_manager.get_counts(circs[i].name), jobs[job_index].result().get_counts(exp_index)) @requires_provider def test_index_out_of_range(self, provider): """Test result index out of range.""" backend = provider.get_backend('ibmq_qasm_simulator') job_set = self._jm.run([self._qc], backend=backend) result_manager = job_set.results() with self.assertRaises(IBMQJobManagerJobNotFound): result_manager.get_counts(1) @requires_provider def test_skipped_result(self, provider): """Test one of jobs has no result.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments circs = [] for _ in range(max_circs+2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) jobs = job_set.jobs() cjob = jobs[1] cancelled = False for _ in range(2): # Try twice in case job is not in a cancellable state try: if cjob.cancel(): # TODO skip checking for status when API is fixed. time.sleep(0.5) cjob.refresh() if cjob.cancelled(): cancelled = True break except JobError: pass result_manager = job_set.results() if cancelled: with self.assertRaises(IBMQManagedResultDataNotAvailable, msg="IBMQManagedResultDataNotAvailable not " "raised for job {}".format(cjob.job_id())): result_manager.get_counts(max_circs) else: self.log.warning("Unable to cancel job %s", cjob.job_id()) def test_ibmq_managed_results_signature(self): """Test `ManagedResults` and `Result` contain the same public methods. Note: Aside from ensuring the two classes contain the same public methods, it is also necessary to check that the corresponding methods have the same signature. """ result_methods = self._get_class_methods(Result) self.assertTrue(result_methods) managed_results_methods = self._get_class_methods(ManagedResults) self.assertTrue(managed_results_methods) # Ensure both classes share the *exact* same public methods. self.assertEqual(result_methods.keys(), managed_results_methods.keys()) # Ensure the signature for the public methods from both classes are compatible. for name, method in managed_results_methods.items(): managed_results_params = getattr(getfullargspec(method), 'args', []) result_params = getattr(getfullargspec(result_methods[name]), 'args', []) self.assertTrue(managed_results_params) self.assertTrue(result_params) # pylint: disable=duplicate-string-formatting-argument self.assertEqual(result_params, managed_results_params, "The signatures for method `{}` differ. " "`Result.{}` params = {} " "`ManagedResults.{}` params = {}." .format(name, name, managed_results_params, name, result_params)) def _get_class_methods(self, cls): """Get public class methods from its namespace. Note: Since the methods are found using the class itself and not and instance, the "methods" are categorized as functions. Methods are only bound when they belong to an actual instance. """ cls_methods = {} for name, method in cls.__dict__.items(): if isfunction(method) and not name.startswith('_'): cls_methods[name] = method return cls_methods def _bell_circuit(): qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.measure([0, 1], [0, 1]) return qc qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_backend.py0000664000372000037200000000377713616666011024410 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 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. """IBMQBackend Test.""" from inspect import getfullargspec from qiskit.providers.ibmq.ibmqbackend import IBMQBackend from qiskit.providers.ibmq.ibmqbackendservice import IBMQBackendService from ..ibmqtestcase import IBMQTestCase class TestIBMQBackend(IBMQTestCase): """Test ibmqbackend module.""" def test_backend_jobs_signature(self): """Test `IBMQBackend.jobs` signature is similar to `IBMQBackendService.jobs` The signature of `IBMQBackend.jobs` is similar to the signature of `IBMQBackendService.jobs` if its parameter list is a subset of the parameter list of `IBMQBackendService.jobs`. """ # Acceptable params `IBMQBackendService.jobs` has that `IBMQBackend.jobs` does not. acceptable_differing_params = {'backend_name'} # Retrieve parameter lists for both classes. backend_jobs_params = set( getattr(getfullargspec(IBMQBackend.jobs), 'args', []) ) backend_service_jobs_params = set( getattr(getfullargspec(IBMQBackendService.jobs), 'args', []) ) # Ensure parameter lists not empty self.assertTrue(backend_jobs_params) self.assertTrue(backend_service_jobs_params) # Remove acceptable params from `IBMQBackendService.jobs`. backend_service_jobs_params.difference_update(acceptable_differing_params) # Ensure method signatures are similar, other than the acceptable differences. self.assertEqual(backend_service_jobs_params, backend_jobs_params) qiskit-ibmq-provider-0.4.6/test/ibmq/test_registration.py0000664000372000037200000003424613616666011024516 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Test the registration and credentials features of the IBMQ module.""" import logging import os import warnings from io import StringIO from contextlib import contextmanager from tempfile import NamedTemporaryFile from unittest import skipIf from unittest.mock import patch from requests_ntlm import HttpNtlmAuth from qiskit.providers.ibmq import IBMQ, IBMQFactory from qiskit.providers.ibmq.credentials import ( Credentials, discover_credentials, qconfig, read_credentials_from_qiskitrc, store_credentials) from qiskit.providers.ibmq.credentials.updater import ( update_credentials, QE2_AUTH_URL, QE2_URL, QE_URL) from qiskit.providers.ibmq.exceptions import IBMQAccountError from ..ibmqtestcase import IBMQTestCase from ..contextmanagers import custom_envs, no_envs, custom_qiskitrc, no_file, CREDENTIAL_ENV_VARS IBMQ_TEMPLATE = 'https://localhost/api/Hubs/{}/Groups/{}/Projects/{}' PROXIES = { 'urls': { 'http': 'http://user:password@127.0.0.1:5678', 'https': 'https://user:password@127.0.0.1:5678' } } # TODO: NamedTemporaryFiles do not support name in Windows @skipIf(os.name == 'nt', 'Test not supported in Windows') class TestCredentials(IBMQTestCase): """Tests for the credential subsystem.""" def test_autoregister_no_credentials(self): """Test register() with no credentials available.""" with no_file('Qconfig.py'), custom_qiskitrc(), no_envs(CREDENTIAL_ENV_VARS): with self.assertRaises(IBMQAccountError) as context_manager: IBMQ.load_account() self.assertIn('No IBM Q Experience credentials found', str(context_manager.exception)) def test_store_credentials_overwrite(self): """Test overwriting qiskitrc credentials.""" credentials = Credentials('QISKITRC_TOKEN', url=QE2_AUTH_URL) credentials2 = Credentials('QISKITRC_TOKEN_2', url=QE2_AUTH_URL) factory = IBMQFactory() with custom_qiskitrc(): store_credentials(credentials) # Cause all warnings to always be triggered. warnings.simplefilter("always") # Get the logger for `store_credentials`. config_rc_logger = logging.getLogger(store_credentials.__module__) # Attempt overwriting. with self.assertLogs(logger=config_rc_logger, level='WARNING') as log_records: store_credentials(credentials) self.assertIn('already present', log_records.output[0]) with no_file('Qconfig.py'), no_envs(CREDENTIAL_ENV_VARS), mock_ibmq_provider(): # Attempt overwriting. store_credentials(credentials2, overwrite=True) factory.load_account() # Ensure that the credentials are the overwritten ones. self.assertEqual(factory._credentials, credentials2) def test_environ_over_qiskitrc(self): """Test order, without qconfig""" credentials = Credentials('QISKITRC_TOKEN', url=QE2_AUTH_URL) with custom_qiskitrc(): # Prepare the credentials: both env and qiskitrc present store_credentials(credentials) with no_file('Qconfig.py'), custom_envs({'QE_TOKEN': 'ENVIRON_TOKEN', 'QE_URL': 'ENVIRON_URL'}): credentials = discover_credentials() self.assertEqual(len(credentials), 1) self.assertEqual(list(credentials.values())[0].token, 'ENVIRON_TOKEN') def test_qconfig_over_all(self): """Test order, with qconfig""" credentials = Credentials('QISKITRC_TOKEN', url=QE2_AUTH_URL) with custom_qiskitrc(): # Prepare the credentials: qconfig, env and qiskitrc present store_credentials(credentials) with custom_qconfig(b"APItoken='QCONFIG_TOKEN'"),\ custom_envs({'QE_TOKEN': 'ENVIRON_TOKEN'}): credentials = discover_credentials() self.assertEqual(len(credentials), 1) self.assertEqual(list(credentials.values())[0].token, 'QCONFIG_TOKEN') class TestCredentialsKwargs(IBMQTestCase): """Test for `Credentials.connection_parameters()`.""" def test_no_proxy_params(self): """Test when no proxy parameters are passed.""" no_params_expected_result = {'verify': True} no_params_credentials = Credentials('dummy_token', 'https://dummy_url') result = no_params_credentials.connection_parameters() self.assertDictEqual(no_params_expected_result, result) def test_verify_param(self): """Test 'verify' arg is acknowledged.""" false_verify_expected_result = {'verify': False} false_verify_credentials = Credentials( 'dummy_token', 'https://dummy_url', verify=False) result = false_verify_credentials.connection_parameters() self.assertDictEqual(false_verify_expected_result, result) def test_proxy_param(self): """Test using only proxy urls (no NTLM credentials).""" urls = {'http': 'localhost:8080', 'https': 'localhost:8080'} proxies_only_expected_result = {'verify': True, 'proxies': urls} proxies_only_credentials = Credentials( 'dummy_token', 'https://dummy_url', proxies={'urls': urls}) result = proxies_only_credentials.connection_parameters() self.assertDictEqual(proxies_only_expected_result, result) def test_proxies_param_with_ntlm(self): """Test proxies with NTLM credentials.""" urls = {'http': 'localhost:8080', 'https': 'localhost:8080'} proxies_with_ntlm_dict = { 'urls': urls, 'username_ntlm': 'domain\\username', 'password_ntlm': 'password' } ntlm_expected_result = { 'verify': True, 'proxies': urls, 'auth': HttpNtlmAuth('domain\\username', 'password') } proxies_with_ntlm_credentials = Credentials( 'dummy_token', 'https://dummy_url', proxies=proxies_with_ntlm_dict) result = proxies_with_ntlm_credentials.connection_parameters() # Verify the NTLM credentials. self.assertEqual( ntlm_expected_result['auth'].username, result['auth'].username) self.assertEqual( ntlm_expected_result['auth'].password, result['auth'].password) # Remove the HttpNtlmAuth objects for direct comparison of the dicts. ntlm_expected_result.pop('auth') result.pop('auth') self.assertDictEqual(ntlm_expected_result, result) def test_malformed_proxy_param(self): """Test input with malformed nesting of the proxies dictionary.""" urls = {'http': 'localhost:8080', 'https': 'localhost:8080'} malformed_nested_proxies_dict = {'proxies': urls} malformed_nested_credentials = Credentials( 'dummy_token', 'https://dummy_url', proxies=malformed_nested_proxies_dict) # Malformed proxy entries should be ignored. expected_result = {'verify': True} result = malformed_nested_credentials.connection_parameters() self.assertDictEqual(expected_result, result) def test_malformed_ntlm_params(self): """Test input with malformed NTLM credentials.""" urls = {'http': 'localhost:8080', 'https': 'localhost:8080'} malformed_ntlm_credentials_dict = { 'urls': urls, 'username_ntlm': 1234, 'password_ntlm': 5678 } malformed_ntlm_credentials = Credentials( 'dummy_token', 'https://dummy_url', proxies=malformed_ntlm_credentials_dict) # Should raise when trying to do username.split('\\', ) # in NTLM credentials due to int not facilitating 'split'. with self.assertRaises(AttributeError): _ = malformed_ntlm_credentials.connection_parameters() @skipIf(os.name == 'nt', 'Test not supported in Windows') class TestIBMQAccountUpdater(IBMQTestCase): """Tests for the update_credentials() helper.""" def setUp(self): super().setUp() # Avoid stdout output during tests. self.patcher = patch('sys.stdout', new=StringIO()) self.patcher.start() def tearDown(self): super().tearDown() # Reenable stdout output. self.patcher.stop() def assertCorrectApi2Credentials(self, token, credentials_dict): """Asserts that there is only one credentials belonging to API 2.""" self.assertEqual(len(credentials_dict), 1) credentials = list(credentials_dict.values())[0] self.assertEqual(credentials.url, QE2_AUTH_URL) self.assertIsNone(credentials.hub) self.assertIsNone(credentials.group) self.assertIsNone(credentials.project) if token: self.assertEqual(credentials.token, token) def test_qe_credentials(self): """Test converting QE credentials.""" with custom_qiskitrc(): store_credentials(Credentials('A', url=QE_URL)) _ = update_credentials(force=True) # Assert over the stored (updated) credentials. loaded_accounts = read_credentials_from_qiskitrc() self.assertCorrectApi2Credentials('A', loaded_accounts) def test_qconsole_credentials(self): """Test converting Qconsole credentials.""" with custom_qiskitrc(): store_credentials(Credentials('A', url=IBMQ_TEMPLATE.format('a', 'b', 'c'))) _ = update_credentials(force=True) # Assert over the stored (updated) credentials. loaded_accounts = read_credentials_from_qiskitrc() self.assertCorrectApi2Credentials('A', loaded_accounts) def test_proxy_credentials(self): """Test converting credentials with proxy values.""" with custom_qiskitrc(): store_credentials(Credentials('A', url=IBMQ_TEMPLATE.format('a', 'b', 'c'), proxies=PROXIES)) _ = update_credentials(force=True) # Assert over the stored (updated) credentials. loaded_accounts = read_credentials_from_qiskitrc() self.assertCorrectApi2Credentials('A', loaded_accounts) # Extra assert on preserving proxies. credentials = list(loaded_accounts.values())[0] self.assertEqual(credentials.proxies, PROXIES) def test_multiple_credentials(self): """Test converting multiple credentials.""" with custom_qiskitrc(): store_credentials(Credentials('A', url=QE2_AUTH_URL)) store_credentials(Credentials('B', url=IBMQ_TEMPLATE.format('a', 'b', 'c'))) store_credentials(Credentials('C', url=IBMQ_TEMPLATE.format('d', 'e', 'f'))) _ = update_credentials(force=True) # Assert over the stored (updated) credentials. loaded_accounts = read_credentials_from_qiskitrc() # We don't assert over the token, as it depends on the order of # the qiskitrc, which is not guaranteed. self.assertCorrectApi2Credentials(None, loaded_accounts) def test_api2_non_auth_credentials(self): """Test converting api 2 non auth credentials.""" with custom_qiskitrc(): store_credentials(Credentials('A', url=QE2_URL)) _ = update_credentials(force=True) # Assert over the stored (updated) credentials. loaded_accounts = read_credentials_from_qiskitrc() self.assertCorrectApi2Credentials('A', loaded_accounts) def test_auth2_credentials(self): """Test converting already API 2 auth credentials.""" with custom_qiskitrc(): store_credentials(Credentials('A', url=QE2_AUTH_URL)) credentials = update_credentials(force=True) # No credentials should be returned. self.assertIsNone(credentials) def test_unknown_credentials(self): """Test converting credentials with an unknown URL.""" with custom_qiskitrc(): store_credentials(Credentials('A', url='UNKNOWN_URL')) credentials = update_credentials(force=True) # No credentials should be returned nor updated. self.assertIsNone(credentials) loaded_accounts = read_credentials_from_qiskitrc() self.assertEqual(list(loaded_accounts.values())[0].url, 'UNKNOWN_URL') # Context managers @contextmanager def custom_qconfig(contents=b''): """Context manager that uses a temporary qconfig.py.""" # Create a temporary file with the contents. tmp_file = NamedTemporaryFile(suffix='.py') tmp_file.write(contents) tmp_file.flush() # Temporarily modify the default location of the qiskitrc file. default_qconfig_file_original = qconfig.DEFAULT_QCONFIG_FILE qconfig.DEFAULT_QCONFIG_FILE = tmp_file.name yield # Delete the temporary file and restore the default location. tmp_file.close() qconfig.DEFAULT_QCONFIG_FILE = default_qconfig_file_original def _mocked_initialize_provider(self, credentials: Credentials): """Mock `_initialize_provider()`, just storing the credentials.""" self._credentials = credentials @contextmanager def mock_ibmq_provider(): """Mock the initialization of IBMQFactory, so it does not query the api.""" patcher = patch.object(IBMQFactory, '_initialize_providers', side_effect=_mocked_initialize_provider, autospec=True) patcher2 = patch.object(IBMQFactory, '_check_api_version', return_value={'new_api': True, 'api-auth': '0.1'}) patcher.start() patcher2.start() yield patcher2.stop() patcher.stop() qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_backends.py0000664000372000037200000001705113616666011024561 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """IBMQ Remote Backend Tests.""" from qiskit import (BasicAer, ClassicalRegister, QuantumCircuit, QuantumRegister) from qiskit.test import slow_test from qiskit.compiler import assemble, transpile from ..ibmqtestcase import IBMQTestCase from ..decorators import requires_provider class TestIBMQBackends(IBMQTestCase): """Qiskit test for remote backend validation. Executes a series of circuits of special interest using all available remote backends, comparing results against local simulator 'local_qasm_simulator' as ground truth. """ @slow_test def setUp(self): super().setUp() self._local_backend = BasicAer.get_backend('qasm_simulator') self._remote_backends = self.get_backends() @requires_provider def get_backends(self, provider=None): """Return all available remote backends.""" return provider.backends() def test_one_qubit_no_operation(self): """Test one circuit, one register, in-order readout.""" qr = QuantumRegister(1) cr = ClassicalRegister(1) circuit = QuantumCircuit(qr, cr) circuit.measure(qr[0], cr[0]) qobj = assemble(transpile(circuit, backend=self._local_backend)) result_local = self._local_backend.run(qobj).result() for remote_backend in self._remote_backends: if not remote_backend.status().operational: continue with self.subTest(backend=remote_backend): result_remote = remote_backend.run(qobj).result() self.assertDictAlmostEqual(result_remote.get_counts(circuit), result_local.get_counts(circuit), delta=50) def test_one_qubit_operation(self): """Test one circuit, one register, in-order readout.""" qr = QuantumRegister(1) cr = ClassicalRegister(1) circuit = QuantumCircuit(qr, cr) circuit.x(qr[0]) circuit.measure(qr[0], cr[0]) qobj = assemble(transpile(circuit, backend=self._local_backend)) result_local = self._local_backend.run(qobj).result() for remote_backend in self._remote_backends: with self.subTest(backend=remote_backend): result_remote = remote_backend.run(qobj).result() self.assertDictAlmostEqual(result_remote.get_counts(circuit), result_local.get_counts(circuit), delta=50) def test_simple_circuit(self): """Test one circuit, one register, in-order readout.""" qr = QuantumRegister(4) cr = ClassicalRegister(4) circuit = QuantumCircuit(qr, cr) circuit.x(qr[0]) circuit.x(qr[2]) circuit.measure(qr[0], cr[0]) circuit.measure(qr[1], cr[1]) circuit.measure(qr[2], cr[2]) circuit.measure(qr[3], cr[3]) qobj = assemble(transpile(circuit, backend=self._local_backend)) result_local = self._local_backend.run(qobj).result() for remote_backend in self._remote_backends: with self.subTest(backend=remote_backend): result_remote = remote_backend.run(qobj).result() self.assertDictAlmostEqual(result_remote.get_counts(circuit), result_local.get_counts(circuit), delta=50) def test_readout_order(self): """Test one circuit, one register, out-of-order readout. """ qr = QuantumRegister(4) cr = ClassicalRegister(4) circuit = QuantumCircuit(qr, cr) circuit.x(qr[0]) circuit.x(qr[2]) circuit.measure(qr[0], cr[2]) circuit.measure(qr[1], cr[0]) circuit.measure(qr[2], cr[1]) circuit.measure(qr[3], cr[3]) qobj_local = assemble(transpile(circuit, backend=self._local_backend)) result_local = self._local_backend.run(qobj_local).result() for remote_backend in self._remote_backends: with self.subTest(backend=remote_backend): qobj_remote = assemble(transpile(circuit, backend=remote_backend)) result_remote = remote_backend.run(qobj_remote).result() self.assertDictAlmostEqual(result_remote.get_counts(circuit), result_local.get_counts(circuit), delta=50) def test_multi_register(self): """Test one circuit, two registers, out-of-order readout.""" qr1 = QuantumRegister(2) qr2 = QuantumRegister(2) cr1 = ClassicalRegister(3) cr2 = ClassicalRegister(1) circuit = QuantumCircuit(qr1, qr2, cr1, cr2) circuit.h(qr1[0]) circuit.cx(qr1[0], qr2[1]) circuit.h(qr2[0]) circuit.cx(qr2[0], qr1[1]) circuit.x(qr1[1]) circuit.measure(qr1[0], cr2[0]) circuit.measure(qr1[1], cr1[0]) circuit.measure(qr1[1], cr2[0]) circuit.measure(qr1[1], cr1[2]) circuit.measure(qr2[0], cr1[2]) circuit.measure(qr2[1], cr1[1]) qobj = assemble(transpile(circuit, backend=self._local_backend)) result_local = self._local_backend.run(qobj).result() for remote_backend in self._remote_backends: with self.subTest(backend=remote_backend): result_remote = remote_backend.run(qobj).result() self.assertDictAlmostEqual(result_remote.get_counts(circuit), result_local.get_counts(circuit), delta=50) def test_multi_circuit(self): """Test two circuits, two registers, out-of-order readout.""" qr1 = QuantumRegister(2) qr2 = QuantumRegister(2) cr1 = ClassicalRegister(3) cr2 = ClassicalRegister(1) circuit1 = QuantumCircuit(qr1, qr2, cr1, cr2) circuit1.h(qr1[0]) circuit1.cx(qr1[0], qr2[1]) circuit1.h(qr2[0]) circuit1.cx(qr2[0], qr1[1]) circuit1.x(qr1[1]) circuit1.measure(qr1[0], cr2[0]) circuit1.measure(qr1[1], cr1[0]) circuit1.measure(qr1[0], cr2[0]) circuit1.measure(qr1[1], cr1[2]) circuit1.measure(qr2[0], cr1[2]) circuit1.measure(qr2[1], cr1[1]) circuit2 = QuantumCircuit(qr1, qr2, cr1) circuit2.h(qr1[0]) circuit2.cx(qr1[0], qr1[1]) circuit2.h(qr2[1]) circuit2.cx(qr2[1], qr1[1]) circuit2.measure(qr1[0], cr1[0]) circuit2.measure(qr1[1], cr1[1]) circuit2.measure(qr1[0], cr1[2]) circuit2.measure(qr2[1], cr1[2]) qobj = assemble(transpile([circuit1, circuit2], backend=self._local_backend)) result_local = self._local_backend.run(qobj).result() for remote_backend in self._remote_backends: with self.subTest(backend=remote_backend): result_remote = remote_backend.run(qobj).result() self.assertDictAlmostEqual(result_remote.get_counts(circuit1), result_local.get_counts(circuit1), delta=50) self.assertDictAlmostEqual(result_remote.get_counts(circuit2), result_local.get_counts(circuit2), delta=50) qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_job.py0000664000372000037200000007666113616666011023575 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 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. """IBMQJob Test.""" import time import copy from datetime import datetime, timedelta from unittest import SkipTest import numpy from scipy.stats import chi2_contingency from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.providers.jobstatus import JobStatus, JOB_FINAL_STATES from qiskit.providers.ibmq import least_busy from qiskit.providers.ibmq.apiconstants import ApiJobStatus, API_JOB_FINAL_STATES from qiskit.providers.ibmq.ibmqbackend import IBMQRetiredBackend from qiskit.providers.ibmq.exceptions import IBMQBackendError from qiskit.providers.ibmq.job.utils import api_status_to_job_status from qiskit.providers.ibmq.job.exceptions import (IBMQJobInvalidStateError, JobError, IBMQJobTimeoutError) from qiskit.providers.ibmq.ibmqfactory import IBMQFactory from qiskit.test import slow_test from qiskit.compiler import assemble, transpile from qiskit.result import Result from ..jobtestcase import JobTestCase from ..decorators import (requires_provider, slow_test_on_device, requires_device, requires_qe_access) from ..utils import most_busy_backend class TestIBMQJob(JobTestCase): """Test ibmqjob module.""" def setUp(self): super().setUp() self._qc = _bell_circuit() @requires_provider def test_run_simulator(self, provider): """Test running in a simulator.""" backend = provider.get_backend('ibmq_qasm_simulator') qr = QuantumRegister(2, 'q') cr = ClassicalRegister(2, 'c') qc = QuantumCircuit(qr, cr, name='hadamard') qc.h(qr) qc.measure(qr, cr) qobj = assemble(transpile([self._qc, qc], backend=backend), backend=backend) shots = qobj.config.shots job = backend.run(qobj) result = job.result() counts_qx1 = result.get_counts(0) counts_qx2 = result.get_counts(1) counts_ex1 = {'00': shots / 2, '11': shots / 2} counts_ex2 = {'00': shots / 4, '11': shots / 4, '10': shots / 4, '01': shots / 4} states1 = counts_qx1.keys() | counts_ex1.keys() states2 = counts_qx2.keys() | counts_ex2.keys() # contingency table ctable1 = numpy.array([[counts_qx1.get(key, 0) for key in states1], [counts_ex1.get(key, 0) for key in states1]]) ctable2 = numpy.array([[counts_qx2.get(key, 0) for key in states2], [counts_ex2.get(key, 0) for key in states2]]) self.log.info('states1: %s', str(states1)) self.log.info('states2: %s', str(states2)) self.log.info('ctable1: %s', str(ctable1)) self.log.info('ctable2: %s', str(ctable2)) contingency1 = chi2_contingency(ctable1) contingency2 = chi2_contingency(ctable2) self.log.info('chi2_contingency1: %s', str(contingency1)) self.log.info('chi2_contingency2: %s', str(contingency2)) self.assertGreater(contingency1[1], 0.01) self.assertGreater(contingency2[1], 0.01) @slow_test_on_device def test_run_device(self, provider, backend): # pylint: disable=unused-argument """Test running in a real device.""" qobj = assemble(transpile(self._qc, backend=backend), backend=backend) shots = qobj.config.shots job = backend.run(qobj) result = job.result() counts_qx = result.get_counts(0) counts_ex = {'00': shots / 2, '11': shots / 2} self.assertDictAlmostEqual(counts_qx, counts_ex, shots * 0.1) # Test fetching the job properties, as this is a real backend and is # guaranteed to have them. self.assertIsNotNone(job.properties()) @requires_provider def test_run_multiple_simulator(self, provider): """Test running multiple jobs in a simulator.""" backend = provider.get_backend('ibmq_qasm_simulator') self.log.info('submitting to backend %s', backend.name()) num_qubits = 16 qr = QuantumRegister(num_qubits, 'qr') cr = ClassicalRegister(num_qubits, 'cr') qc = QuantumCircuit(qr, cr) for i in range(num_qubits - 1): qc.cx(qr[i], qr[i + 1]) qc.measure(qr, cr) qobj = assemble(transpile([qc] * 10, backend=backend), backend=backend) num_jobs = 5 job_array = [backend.run(qobj) for _ in range(num_jobs)] timeout = 30 start_time = time.time() while True: check = sum( [job.status() is JobStatus.RUNNING for job in job_array]) if check >= 2: self.log.info('found %d simultaneous jobs', check) break if all([job.status() is JobStatus.DONE for job in job_array]): # done too soon? don't generate error self.log.warning('all jobs completed before simultaneous jobs ' 'could be detected') break for job in job_array: self.log.info('%s %s %s %s', job.status(), job.status() is JobStatus.RUNNING, check, job.job_id()) self.log.info('- %s', str(time.time() - start_time)) if time.time() - start_time > timeout: raise TimeoutError('failed to see multiple running jobs after ' '{0} s'.format(timeout)) time.sleep(0.2) result_array = [job.result() for job in job_array] self.log.info('got back all job results') # Ensure all jobs have finished. self.assertTrue( all([job.status() is JobStatus.DONE for job in job_array])) self.assertTrue(all([result.success for result in result_array])) # Ensure job ids are unique. job_ids = [job.job_id() for job in job_array] self.assertEqual(sorted(job_ids), sorted(list(set(job_ids)))) @slow_test_on_device def test_run_multiple_device(self, provider, backend): # pylint: disable=unused-argument """Test running multiple jobs in a real device.""" self.log.info('submitting to backend %s', backend.name()) num_qubits = 5 qr = QuantumRegister(num_qubits, 'qr') cr = ClassicalRegister(num_qubits, 'cr') qc = QuantumCircuit(qr, cr) for i in range(num_qubits - 1): qc.cx(qr[i], qr[i + 1]) qc.measure(qr, cr) qobj = assemble(transpile(qc, backend=backend), backend=backend) num_jobs = 3 job_array = [backend.run(qobj) for _ in range(num_jobs)] time.sleep(3) # give time for jobs to start (better way?) job_status = [job.status() for job in job_array] num_init = sum( [status is JobStatus.INITIALIZING for status in job_status]) num_queued = sum([status is JobStatus.QUEUED for status in job_status]) num_running = sum( [status is JobStatus.RUNNING for status in job_status]) num_done = sum([status is JobStatus.DONE for status in job_status]) num_error = sum([status is JobStatus.ERROR for status in job_status]) self.log.info('number of currently initializing jobs: %d/%d', num_init, num_jobs) self.log.info('number of currently queued jobs: %d/%d', num_queued, num_jobs) self.log.info('number of currently running jobs: %d/%d', num_running, num_jobs) self.log.info('number of currently done jobs: %d/%d', num_done, num_jobs) self.log.info('number of errored jobs: %d/%d', num_error, num_jobs) self.assertTrue(num_jobs - num_error - num_done > 0) # Wait for all the results. result_array = [job.result(timeout=180) for job in job_array] # Ensure all jobs have finished. self.assertTrue( all([job.status() is JobStatus.DONE for job in job_array])) self.assertTrue(all([result.success for result in result_array])) # Ensure job ids are unique. job_ids = [job.job_id() for job in job_array] self.assertEqual(sorted(job_ids), sorted(list(set(job_ids)))) @requires_provider def test_cancel(self, provider): """Test job cancellation.""" # Find the most busy backend backend = most_busy_backend(provider) qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) for _ in range(2): # Try twice in case job is not in a cancellable state try: if job.cancel(): status = job.status() # TODO Change the warning to assert once API is fixed if status is not JobStatus.CANCELLED: self.log.warning("cancel() was successful for job %s but its status is %s.", job.job_id(), status) except JobError: pass @requires_provider def test_retrieve_jobs(self, provider): """Test retrieving jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, skip=0) for job in job_list: self.assertTrue(isinstance(job.job_id(), str)) @requires_provider def test_retrieve_job(self, provider): """Test retrieving a single job.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) retrieved_job = provider.backends.retrieve_job(job.job_id()) self.assertEqual(job.job_id(), retrieved_job.job_id()) self.assertEqual(job.result().get_counts(), retrieved_job.result().get_counts()) self.assertEqual(job.qobj().to_dict(), qobj.to_dict()) @requires_device @requires_provider def test_retrieve_job_uses_appropriate_backend(self, backend, provider): """Test that retrieved jobs come from their appropriate backend.""" backend_1 = backend # Get a second backend. backend_2 = None for backend_2 in provider.backends(): if backend_2.status().operational and backend_2.name() != backend_1.name(): break if not backend_2: raise SkipTest('Skipping test that requires multiple backends') qobj_1 = assemble( transpile(self._qc, backend=backend_1), backend=backend_1) job_1 = backend_1.run(qobj_1) qobj_2 = assemble( transpile(self._qc, backend=backend_2), backend=backend_2) job_2 = backend_2.run(qobj_2) # test a retrieved job's backend is the same as the queried backend self.assertEqual(backend_1.retrieve_job(job_1.job_id()).backend().name(), backend_1.name()) self.assertEqual(backend_2.retrieve_job(job_2.job_id()).backend().name(), backend_2.name()) # test retrieve requests for jobs that exist on other backends throw errors with self.assertWarns(Warning) as context_manager: self.assertRaises(IBMQBackendError, backend_1.retrieve_job, job_2.job_id()) self.assertIn('belongs to', str(context_manager.warning)) with self.assertWarns(Warning) as context_manager: self.assertRaises(IBMQBackendError, backend_2.retrieve_job, job_1.job_id()) self.assertIn('belongs to', str(context_manager.warning)) # Cleanup for job in [job_1, job_2]: try: job.cancel() except JobError: pass @requires_provider def test_retrieve_job_error(self, provider): """Test retrieving an invalid job.""" self.assertRaises(IBMQBackendError, provider.backends.retrieve_job, 'BAD_JOB_ID') @requires_provider def test_retrieve_jobs_status(self, provider): """Test retrieving jobs filtered by status.""" backend = provider.get_backend('ibmq_qasm_simulator') # Get the most recent jobs that are done. status_args = [JobStatus.DONE, 'DONE', [JobStatus.DONE], ['DONE']] for arg in status_args: with self.subTest(arg=arg): backend_jobs = backend.jobs(limit=10, skip=0, status=arg) self.assertTrue(backend_jobs) for job in backend_jobs: self.assertTrue(job.status() is JobStatus.DONE, "job status for job {} was '{}' but " "it should be: '{}'" .format(job.job_id(), job.status(), JobStatus.DONE)) @requires_provider def test_retrieve_multiple_job_statuses(self, provider): """Test retrieving jobs filtered by multiple job statuses.""" backend = provider.get_backend('ibmq_qasm_simulator') statuses_to_filter = [JobStatus.ERROR, JobStatus.CANCELLED] status_filters = [ {'status': [JobStatus.ERROR, JobStatus.CANCELLED], 'db_filter': None}, {'status': [JobStatus.CANCELLED], 'db_filter': {'or': [{'status': {'regexp': '^ERROR'}}]}}, {'status': [JobStatus.ERROR], 'db_filter': {'or': [{'status': 'CANCELLED'}]}} ] qobj = assemble(transpile(self._qc, backend=backend), backend=backend) # Submit a job, then cancel it. job_to_cancel = backend.run(qobj) for _ in range(2): # Try twice in case job is not in a cancellable state try: if job_to_cancel.cancel(): status = job_to_cancel.status() # TODO Change the warning to assert once API is fixed if status is not JobStatus.CANCELLED: self.log.warning("cancel() was successful for job %s but its status is %s.", job_to_cancel.job_id(), status) except JobError: pass # Submit a job that will fail. qobj.config.shots = 10000 # Modify the number of shots to be an invalid amount. job_to_fail = backend.run(qobj) while job_to_fail.status() not in JOB_FINAL_STATES: time.sleep(0.5) for status_filter in status_filters: with self.subTest(status_filter=status_filter): job_list = backend.jobs(status=status_filter['status'], db_filter=status_filter['db_filter']) if (job_to_cancel.status() is JobStatus.CANCELLED and job_to_fail.status() is JobStatus.ERROR): job_list_ids = [_job.job_id() for _job in job_list] # Assert `job_id` in the list of job id's (instead of the list of jobs), # because retrieved jobs might differ in attributes from the originally # submitted jobs. self.assertIn(job_to_fail.job_id(), job_list_ids) self.assertIn(job_to_cancel.job_id(), job_list_ids) for filtered_job in job_list: self.assertIn(filtered_job._status, statuses_to_filter, "job {} has status '{}' but it should be one " "of the values '{}'" .format(filtered_job.job_id(), filtered_job._status, statuses_to_filter)) @requires_provider def test_retrieve_active_jobs(self, provider): """Test retrieving jobs that are currently unfinished.""" backend = most_busy_backend(provider) active_job_statuses = {api_status_to_job_status(status) for status in ApiJobStatus if status not in API_JOB_FINAL_STATES} qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) active_jobs = backend.active_jobs() if job.status() not in JOB_FINAL_STATES: # Assert `job_id` in the list of job id's (instead of the list of jobs), # because retrieved jobs might differ in attributes from the originally # submitted jobs or they might have changed status. self.assertIn(job.job_id(), [active_job.job_id() for active_job in active_jobs], "job {} is active but not retrieved when filtering for active jobs." .format(job.job_id())) for active_job in active_jobs: self.assertTrue(active_job._status in active_job_statuses, "status for job {} is '{}' but it should be '{}'." .format(active_job.job_id(), active_job._status, active_job_statuses)) # Cancel job so it doesn't consume more resources. try: job.cancel() except JobError: pass @requires_provider def test_retrieve_jobs_queued(self, provider): """Test retrieving jobs that are queued.""" backend = most_busy_backend(provider) qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) # Wait for the job to queue, run, or reach a final state. leave_states = list(JOB_FINAL_STATES) + [JobStatus.QUEUED, JobStatus.RUNNING] while job.status() not in leave_states: time.sleep(0.5) before_status = job._status job_list_queued = backend.jobs(status=JobStatus.QUEUED, limit=5) if before_status is JobStatus.QUEUED and job.status() is JobStatus.QUEUED: # When retrieving jobs, the status of a recent job might be `RUNNING` when it is in fact # `QUEUED`. This is due to queue info not being returned for the job. To ensure the job # was retrieved, check whether the job id is in the list of queued jobs retrieved. self.assertIn(job.job_id(), [queued_job.job_id() for queued_job in job_list_queued], "job {} is queued but not retrieved when filtering for queued jobs." .format(job.job_id())) # TODO: Uncomment when api fixes job statuses. # for queued_job in job_list_queued: # self.assertTrue(queued_job._status == JobStatus.QUEUED, # "status for job {} is '{}' but it should be {}" # .format(queued_job.job_id(), queued_job._status, # JobStatus.QUEUED)) # Cancel job so it doesn't consume more resources. try: job.cancel() except JobError: pass @requires_provider def test_retrieve_jobs_start_datetime(self, provider): """Test retrieving jobs created after a specified datetime.""" backend = provider.get_backend('ibmq_qasm_simulator') past_month = datetime.now() - timedelta(days=30) past_month_str = past_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, skip=0, start_datetime=past_month) self.assertTrue(job_list) for i, job in enumerate(job_list): self.assertTrue(job.creation_date() >= past_month_str, '{}) job creation_date {} is not ' 'greater than or equal to past month: {}' .format(i, job.creation_date(), past_month_str)) @requires_qe_access def test_retrieve_jobs_end_datetime(self, qe_token, qe_url): """Test retrieving jobs created before a specified datetime.""" ibmq_factory = IBMQFactory() provider = ibmq_factory.enable_account(qe_token, qe_url) backend = provider.get_backend('ibmq_qasm_simulator') past_month = datetime.now() - timedelta(days=30) past_month_str = past_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, skip=0, end_datetime=past_month) self.assertTrue(job_list) for i, job in enumerate(job_list): self.assertTrue(job.creation_date() <= past_month_str, '{}) job creation_date {} is not ' 'less than or equal to past month: {}' .format(i, job.creation_date(), past_month_str)) @requires_qe_access def test_retrieve_jobs_between_datetimes(self, qe_token, qe_url): """Test retrieving jobs created between two specified datetimes.""" ibmq_factory = IBMQFactory() provider = ibmq_factory.enable_account(qe_token, qe_url) backend = provider.get_backend('ibmq_qasm_simulator') date_today = datetime.now() past_month = date_today - timedelta(30) past_month_str = past_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') past_two_month = date_today - timedelta(60) past_two_month_str = past_two_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, skip=0, start_datetime=past_two_month, end_datetime=past_month) self.assertTrue(job_list) for i, job in enumerate(job_list): self.assertTrue((past_two_month_str <= job.creation_date() <= past_month_str), '{}) job creation date {} is not ' 'between past two month {} and past month {}' .format(i, past_two_month_str, job.creation_date(), past_month_str)) @requires_qe_access def test_retrieve_jobs_between_datetimes_not_overriden(self, qe_token, qe_url): """Test retrieving jobs created between two specified datetimes and ensure `db_filter` does not override datetime arguments.""" ibmq_factory = IBMQFactory() provider = ibmq_factory.enable_account(qe_token, qe_url) backend = provider.get_backend('ibmq_qasm_simulator') date_today = datetime.now() past_two_month = date_today - timedelta(30) past_two_month_str = past_two_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') past_three_month = date_today - timedelta(60) past_three_month_str = past_three_month.strftime('%Y-%m-%dT%H:%M:%S.%fZ') # Used for `db_filter`, should not override `start_datetime` and `end_datetime` arguments. past_ten_days = date_today - timedelta(10) job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, skip=0, start_datetime=past_three_month, end_datetime=past_two_month, db_filter={'creationDate': {'gt': past_ten_days}}) self.assertTrue(job_list) for i, job in enumerate(job_list): self.assertTrue((past_three_month_str <= job.creation_date() <= past_two_month_str), '{}) job creation date {} is not ' 'between past three month {} and past two month {}' .format(i, past_three_month_str, job.creation_date(), past_two_month_str)) @requires_provider def test_retrieve_jobs_db_filter(self, provider): """Test retrieving jobs using db_filter.""" # TODO: consider generalizing backend name backend = provider.get_backend('ibmq_qasm_simulator') # Submit jobs with desired attributes. qc = QuantumCircuit(3, 3) qc.h(0) qc.measure([0, 1, 2], [0, 1, 2]) qobj = assemble(transpile(qc, backend=backend), backend=backend) for _ in range(2): backend.run(qobj).result() my_filter = {'backend.name': backend.name(), 'summaryData.summary.qobj_config.n_qubits': 3, 'status': 'COMPLETED'} job_list = provider.backends.jobs(backend_name=backend.name(), limit=2, skip=0, db_filter=my_filter) self.assertTrue(job_list) for job in job_list: job.refresh() self.assertEqual( job.summary_data['summary']['qobj_config']['n_qubits'], 3, "Job {} does not have correct data.".format(job.job_id()) ) @requires_provider def test_retrieve_jobs_filter_date(self, provider): """Test retrieving jobs filtered by date.""" backend = provider.get_backend('ibmq_qasm_simulator') date_today = datetime.now() date_today_str = date_today.strftime('%Y-%m-%dT%H:%M:%S.%fZ') my_filter = {'creationDate': {'lt': date_today.isoformat()}} job_list = provider.backends.jobs(backend_name=backend.name(), limit=5, db_filter=my_filter) self.assertTrue(job_list) self.log.info('found %s matching jobs', len(job_list)) for i, job in enumerate(job_list): self.log.info('match #%d: %s', i, job.creation_date()) self.assertTrue(job.creation_date() < date_today_str, '{}) job.creation_date: {}, date_today: {}' .format(i, job.creation_date(), date_today_str)) @requires_provider def test_double_submit_fails(self, provider): """Test submitting a job twice.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) # backend.run() will automatically call job.submit() job = backend.run(qobj) with self.assertRaises(IBMQJobInvalidStateError): job.submit() @requires_provider def test_retrieve_failed_job_simulator_partial(self, provider): """Test retrieving partial results from a simulator backend.""" backend = provider.get_backend('ibmq_qasm_simulator') qc_new = transpile(self._qc, backend) qobj = assemble([qc_new, qc_new], backend=backend) qobj.experiments[1].instructions[1].name = 'bad_instruction' job = backend.run(qobj) result = job.result(partial=True) self.assertIsInstance(result, Result) self.assertTrue(result.results[0].success) self.assertFalse(result.results[1].success) @slow_test @requires_provider def test_pulse_job(self, provider): """Test running a pulse job.""" backends = provider.backends(open_pulse=True, operational=True) if not backends: raise SkipTest('Skipping pulse test since no pulse backend found.') backend = least_busy(backends) config = backend.configuration() defaults = backend.defaults() inst_map = defaults.circuit_instruction_map # Run 2 experiments - 1 with x pulse and 1 without x = inst_map.get('x', 0) measure = inst_map.get('measure', range(config.n_qubits)) << x.duration ground_sched = measure excited_sched = x | measure schedules = [ground_sched, excited_sched] qobj = assemble(schedules, backend, meas_level=1, shots=256) job = backend.run(qobj) _ = job.result() @requires_provider def test_retrieve_from_retired_backend(self, provider): """Test retrieving a job from a retired backend.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) del provider._backends['ibmq_qasm_simulator'] new_job = provider.backends.retrieve_job(job.job_id()) self.assertTrue(isinstance(new_job.backend(), IBMQRetiredBackend)) self.assertNotEqual(new_job.backend().name(), 'unknown') new_job2 = provider.backends.jobs(db_filter={'id': job.job_id()})[0] self.assertTrue(isinstance(new_job2.backend(), IBMQRetiredBackend)) self.assertNotEqual(new_job2.backend().name(), 'unknown') @requires_provider def test_refresh_job_result(self, provider): """Test re-retrieving job result via refresh.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) result = job.result() # Save original cached results. cached_result = copy.deepcopy(result) self.assertTrue(cached_result) # Modify cached results. result.results[0].header.name = 'modified_result' self.assertNotEqual(cached_result, result) self.assertEqual(result.results[0].header.name, 'modified_result') # Re-retrieve result via refresh. result = job.result(refresh=True) self.assertEqual(cached_result, result) self.assertNotEqual(result.results[0].header.name, 'modified_result') @requires_provider def test_wait_for_final_state(self, provider): """Test waiting for job to reach final state.""" def final_state_callback(c_job_id, c_status, c_job, **kwargs): """Job status query callback function.""" self.assertEqual(c_job_id, job.job_id()) self.assertNotIn(c_status, JOB_FINAL_STATES) self.assertEqual(c_job.job_id(), job.job_id()) self.assertIn('queue_info', kwargs) if c_status is JobStatus.QUEUED: self.assertIsNotNone( kwargs.pop('queue_info', None), "queue_info not found for job {}".format(c_job_id)) callback_info[0] = True if callback_info[1]: self.assertAlmostEqual(time.time() - callback_info[1], wait_time, delta=0.1) callback_info[1] = time.time() # The first is whether the callback function is invoked. The second # is last called time. They're put in a list to be mutable. callback_info = [False, None] wait_time = 0.5 backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) try: job.wait_for_final_state(timeout=30, wait=wait_time, callback=final_state_callback) self.assertTrue(job.done()) self.assertTrue(callback_info[0]) finally: # Ensure all threads ended. for thread in job._executor._threads: thread.join(0.1) @requires_provider def test_wait_for_final_state_timeout(self, provider): """Test waiting for job to reach final state times out.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(self._qc, backend=backend), backend=backend) job = backend.run(qobj) try: self.assertRaises(IBMQJobTimeoutError, job.wait_for_final_state, timeout=0.1) finally: # Ensure all threads ended. for thread in job._executor._threads: thread.join(0.1) def _bell_circuit(): qr = QuantumRegister(2, 'q') cr = ClassicalRegister(2, 'c') qc = QuantumCircuit(qr, cr) qc.h(qr[0]) qc.cx(qr[0], qr[1]) qc.measure(qr, cr) return qc qiskit-ibmq-provider-0.4.6/test/ibmq/test_ibmq_job_model.py0000664000372000037200000000577213616666011024750 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 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. """IBMQJob model tests.""" from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.compiler import assemble, transpile from qiskit.validation import ModelValidationError from qiskit.validation.jsonschema import SchemaValidationError from qiskit.providers.ibmq import IBMQJob from ..jobtestcase import JobTestCase from ..decorators import requires_provider VALID_JOB_RESPONSE = { # Attributes needed by the constructor. 'api': None, '_backend': None, # Attributes required by the schema. 'id': 'TEST_ID', 'kind': 'q-object', 'status': 'CREATING', 'creationDate': '2019-01-01T13:15:58.425972' } class TestIBMQJobModel(JobTestCase): """Test model-related functionality of IBMQJob.""" def test_bad_job_schema(self): """Test creating a job with bad job schema.""" bad_job_info = {'id': 'TEST_ID'} with self.assertRaises(ModelValidationError): IBMQJob.from_dict(bad_job_info) @requires_provider def test_invalid_qobj(self, provider): """Test submitting an invalid qobj.""" backend = provider.get_backend('ibmq_qasm_simulator') qobj = assemble(transpile(_bell_circuit(), backend=backend), backend=backend) delattr(qobj, 'qobj_id') with self.assertRaises(SchemaValidationError): backend.run(qobj) def test_valid_job(self): """Test the model can be created from a response.""" job = IBMQJob.from_dict(VALID_JOB_RESPONSE) # Check for a required attribute with correct name. self.assertNotIn('creationDate', job) self.assertIn('_creation_date', job) def test_auto_undefined_fields(self): """Test undefined response fields appear in the model.""" response = VALID_JOB_RESPONSE.copy() response['newField'] = {'foo': 2} job = IBMQJob.from_dict(response) # Check the field appears as an attribute in the model. self.assertIn('new_field', job) self.assertEqual(job.new_field, {'foo': 2}) def test_invalid_enum(self): """Test creating a model with an invalid value for an Enum field.""" response = VALID_JOB_RESPONSE.copy() response['kind'] = 'invalid' with self.assertRaises(ModelValidationError): IBMQJob.from_dict(response) def _bell_circuit(): qr = QuantumRegister(2, 'q') cr = ClassicalRegister(2, 'c') qc = QuantumCircuit(qr, cr) qc.h(qr[0]) qc.cx(qr[0], qr[1]) qc.measure(qr, cr) return qc qiskit-ibmq-provider-0.4.6/test/ibmq/__init__.py0000664000372000037200000000104213616666011022470 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Qiskit IBMQ integration tests.""" qiskit-ibmq-provider-0.4.6/test/ibmq/test_filter_backends.py0000664000372000037200000000562213616666011025117 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Backends Filtering Test.""" from qiskit.providers.ibmq import least_busy from ..ibmqtestcase import IBMQTestCase from ..decorators import requires_provider, requires_device class TestBackendFilters(IBMQTestCase): """Qiskit Backend Filtering Tests.""" @requires_device def test_filter_config_properties(self, backend): """Test filtering by configuration properties""" # Use the default backend as a reference for the filter. provider = backend._provider n_qubits = backend.configuration().n_qubits filtered_backends = provider.backends(n_qubits=n_qubits, local=False) self.assertTrue(filtered_backends) for filtered_backend in filtered_backends: with self.subTest(filtered_backend=filtered_backend): self.assertEqual(n_qubits, filtered_backend.configuration().n_qubits) self.assertFalse(filtered_backend.configuration().local) @requires_provider def test_filter_status_dict(self, provider): """Test filtering by dictionary of mixed status/configuration properties""" filtered_backends = provider.backends( operational=True, # from status local=False, simulator=True) # from configuration self.assertTrue(filtered_backends) for backend in filtered_backends: with self.subTest(backend=backend): self.assertTrue(backend.status().operational) self.assertFalse(backend.configuration().local) self.assertTrue(backend.configuration().simulator) @requires_provider def test_filter_config_callable(self, provider): """Test filtering by lambda function on configuration properties""" filtered_backends = provider.backends( filters=lambda x: (not x.configuration().simulator and x.configuration().n_qubits >= 5)) self.assertTrue(filtered_backends) for backend in filtered_backends: with self.subTest(backend=backend): self.assertFalse(backend.configuration().simulator) self.assertGreaterEqual(backend.configuration().n_qubits, 5) @requires_provider def test_filter_least_busy(self, provider): """Test filtering by least busy function""" backends = provider.backends() filtered_backends = least_busy(backends) self.assertTrue(filtered_backends) qiskit-ibmq-provider-0.4.6/test/utils.py0000664000372000037200000000222013616666011021140 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2020. # # 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. """General utility functions for testing.""" def most_busy_backend(provider): """Return the most busy backend for the provider given. Return the most busy available backend for those that have a `pending_jobs` in their `status`. Backends such as local backends that do not have this are not considered. Args: provider (AccountProvider): IBM Q Experience account provider. Returns: IBMQBackend: the most busy backend. """ backends = provider.backends(simulator=False, operational=True) return max([b for b in backends if b.configuration().n_qubits >= 5], key=lambda b: b.status().pending_jobs) qiskit-ibmq-provider-0.4.6/test/fake_account_client.py0000664000372000037200000001715513616666011023775 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 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. """Fake AccountClient.""" # TODO This can probably be merged with the one in test_ibmq_job_states import time import copy from random import randrange import uuid from concurrent.futures import ThreadPoolExecutor, wait from qiskit.test.mock.backends.poughkeepsie.fake_poughkeepsie import FakePoughkeepsie from qiskit.providers.ibmq.apiconstants import ApiJobStatus, API_JOB_FINAL_STATES, ApiJobShareLevel from qiskit.providers.ibmq.api.exceptions import RequestsApiError VALID_RESULT_RESPONSE = { 'backend_name': 'ibmqx2', 'backend_version': '1.1.1', 'job_id': 'XC1323XG2', 'qobj_id': 'Experiment1', 'success': True, 'results': [ { 'header': { 'name': 'Bell state', 'memory_slots': 2, 'creg_sizes': [['c', 2]], 'clbit_labels': [['c', 0], ['c', 1]], 'qubit_labels': [['q', 0], ['q', 1]] }, 'shots': 1024, 'status': 'DONE', 'success': True, 'data': { 'counts': { '0x0': 484, '0x3': 540 } } } ] } class BaseFakeJob: """Base class for faking a remote job.""" _job_progress = [ ApiJobStatus.CREATING, ApiJobStatus.VALIDATING, ApiJobStatus.RUNNING, ApiJobStatus.COMPLETED ] def __init__(self, executor, job_id, qobj, backend_name, job_tags=None, share_level=None): """Initialize a fake job.""" self._job_id = job_id self._status = ApiJobStatus.CREATING self.qobj = qobj self._future = executor.submit(self._auto_progress) self._result = None self._backend_name = backend_name self._share_level = share_level self._job_tags = job_tags def _auto_progress(self): """Automatically update job status.""" for status in self._job_progress: time.sleep(0.5) self._status = status if self._status == ApiJobStatus.COMPLETED: new_result = copy.deepcopy(VALID_RESULT_RESPONSE) counts = randrange(1024) new_result['results'][0]['data']['counts'] = { '0x0': counts, '0x3': 1024-counts} new_result['job_id'] = self._job_id new_result['backend_name'] = self._backend_name self._result = new_result def data(self): """Return job data.""" data = { 'id': self._job_id, 'kind': 'q-object', 'status': self._status.value, 'creationDate': '2019-01-01T13:15:58.425972', 'backend': {'name': self._backend_name} } if self._share_level: data['share_level'] = self._share_level if self._job_tags: data['tags'] = self._job_tags return data def cancel(self): """Cancel the job.""" self._future.cancel() wait([self._future]) self._status = ApiJobStatus.CANCELLED self._result = None def result(self): """Return job result.""" if not self._result: raise RequestsApiError("Result is not available") return self._result def status(self): """Return job status.""" return self._status class CancelableFakeJob(BaseFakeJob): """Fake job that can be canceled.""" _job_progress = [ ApiJobStatus.CREATING, ApiJobStatus.VALIDATING, ApiJobStatus.RUNNING ] class BaseFakeAccountClient: """Base class for faking the AccountClient.""" def __init__(self, job_limit=-1, job_class=BaseFakeJob): """Initialize a fake account client.""" self._jobs = {} self._results_retrieved = set() self._job_limit = job_limit self._executor = ThreadPoolExecutor() self._job_class = job_class def list_jobs_statuses(self, limit, skip, *_args, **_kwargs): """Return a list of statuses of jobs.""" job_data = [] for job in self._jobs[skip:skip+limit]: job_data.append(job.data()) return job_data def job_submit(self, backend_name, qobj_dict, job_share_level, job_tags, *_args, **_kwargs): """Submit a Qobj to a device.""" if self._job_limit != -1 and self._unfinished_jobs() >= self._job_limit: raise RequestsApiError( '400 Client Error: Bad Request for url: . User reached ' 'the maximum limits of concurrent jobs, Error code: 3458.') new_job_id = uuid.uuid4().hex job_share_level = job_share_level or ApiJobShareLevel.NONE new_job = self._job_class(executor=self._executor, job_id=new_job_id, qobj=qobj_dict, backend_name=backend_name, share_level=job_share_level.value, job_tags=job_tags) self._jobs[new_job_id] = new_job return new_job.data() def job_download_qobj(self, job_id, *_args, **_kwargs): """Retrieve and return a Qobj.""" return self._get_job(job_id).qobj def job_result(self, job_id, *_args, **_kwargs): """Return a random job result.""" if job_id in self._results_retrieved: raise ValueError("Result already retrieved for job {}!".format(job_id)) self._results_retrieved.add(job_id) return self._get_job(job_id).result() def job_get(self, job_id, *_args, **_kwargs): """Return information about a job.""" return self._get_job(job_id).data() def job_status(self, job_id, *_args, **_kwargs): """Return the status of a job.""" return {'status': self._get_job(job_id).status().value} def job_final_status(self, job_id, *_args, **_kwargs): """Wait until the job progress to a final state.""" job = self._get_job(job_id) status = job.status() while status not in API_JOB_FINAL_STATES: time.sleep(0.5) status = job.status() return self.job_status(job_id) def job_properties(self, *_args, **_kwargs): """Return the backend properties of a job.""" return FakePoughkeepsie().properties() def job_cancel(self, job_id, *_args, **_kwargs): """Submit a request for cancelling a job.""" self._get_job(job_id).cancel() return {'cancelled': True} def backend_job_limit(self, *_args, **_kwargs): """Return the job limit for the backend.""" return {'maximumJobs': self._job_limit, 'runningJobs': self._unfinished_jobs()} def _unfinished_jobs(self): """Return the number of unfinished jobs.""" return sum(1 for job in self._jobs.values() if job.status() not in API_JOB_FINAL_STATES) def _get_job(self, job_id): """Return job if found.""" if job_id not in self._jobs: raise RequestsApiError('Job not found., Error code: 3250.') return self._jobs[job_id] class JobSubmitFailClient(BaseFakeAccountClient): """Fake AccountClient used to fail a job submit.""" def job_submit(self, *_args, **_kwargs): # pylint: disable=arguments-differ """Failing job submit.""" raise RequestsApiError("Job submit failed!") qiskit-ibmq-provider-0.4.6/test/__init__.py0000664000372000037200000000105613616666011021545 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Test for the qiskit-ibmq-provider package.""" qiskit-ibmq-provider-0.4.6/test/jobtestcase.py0000664000372000037200000000226013616666011022312 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. """Custom TestCase for Jobs.""" import time from qiskit.providers import JobStatus from .ibmqtestcase import IBMQTestCase class JobTestCase(IBMQTestCase): """Include common functionality when testing jobs.""" def wait_for_initialization(self, job, timeout=1): """Waits until job progresses from `INITIALIZING` to other status.""" waited = 0 wait = 0.1 while job.status() is JobStatus.INITIALIZING: time.sleep(wait) waited += wait if waited > timeout: self.fail( msg="The JOB is still initializing after timeout ({}s)" .format(timeout) ) qiskit-ibmq-provider-0.4.6/setup.py0000664000372000037200000000531413616666011020170 0ustar travistravis00000000000000# -*- coding: utf-8 -*- # This code is part of Qiskit. # # (C) Copyright IBM 2017, 2018. # # 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. import os from setuptools import setup requirements = [ "nest-asyncio>=1.0.0,!=1.1.0", "qiskit-terra>=0.10", "requests>=2.19", "requests-ntlm>=1.1.0", "websockets>=7,<8", "arrow>=0.15.5" ] # Handle version. VERSION_PATH = os.path.join(os.path.dirname(__file__), "qiskit", "providers", "ibmq", "VERSION.txt") with open(VERSION_PATH, "r") as version_file: VERSION = version_file.read().strip() # Read long description from README. README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md') with open(README_PATH) as readme_file: README = readme_file.read() setup( name="qiskit-ibmq-provider", version=VERSION, description="Qiskit provider for accessing the quantum devices and " "simulators at IBMQ", long_description=README, long_description_content_type='text/markdown', url="https://github.com/Qiskit/qiskit-ibmq-provider", author="Qiskit Development Team", author_email="qiskit@qiskit.org", license="Apache 2.0", classifiers=[ "Environment :: Console", "License :: OSI Approved :: Apache Software License", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Topic :: Scientific/Engineering", ], keywords="qiskit sdk quantum api ibmq", packages=['qiskit.providers.ibmq', 'qiskit.providers.ibmq.api', 'qiskit.providers.ibmq.api.clients', 'qiskit.providers.ibmq.api.rest', 'qiskit.providers.ibmq.credentials', 'qiskit.providers.ibmq.job', 'qiskit.providers.ibmq.managed', 'qiskit.providers.ibmq.utils'], install_requires=requirements, include_package_data=True, python_requires=">=3.5", zip_safe=False ) qiskit-ibmq-provider-0.4.6/qiskit_ibmq_provider.egg-info/0000775000372000037200000000000013616666025024400 5ustar travistravis00000000000000qiskit-ibmq-provider-0.4.6/qiskit_ibmq_provider.egg-info/PKG-INFO0000664000372000037200000003006013616666025025474 0ustar travistravis00000000000000Metadata-Version: 2.1 Name: qiskit-ibmq-provider Version: 0.4.6 Summary: Qiskit provider for accessing the quantum devices and simulators at IBMQ Home-page: https://github.com/Qiskit/qiskit-ibmq-provider Author: Qiskit Development Team Author-email: qiskit@qiskit.org License: Apache 2.0 Description: # Qiskit IBMQ Provider [![License](https://img.shields.io/github/license/Qiskit/qiskit-ibmq-provider.svg?style=popout-square)](https://opensource.org/licenses/Apache-2.0)[![Build Status](https://img.shields.io/travis/com/Qiskit/qiskit-ibmq-provider/master.svg?style=popout-square)](https://travis-ci.com/Qiskit/qiskit-ibmq-provider)[![](https://img.shields.io/github/release/Qiskit/qiskit-ibmq-provider.svg?style=popout-square)](https://github.com/Qiskit/qiskit-ibmq-provider/releases)[![](https://img.shields.io/pypi/dm/qiskit-ibmq-provider.svg?style=popout-square)](https://pypi.org/project/qiskit-ibmq-provider/) **Qiskit** is an open-source framework for working with noisy quantum computers at the level of pulses, circuits, and algorithms. This module contains a provider that allows accessing the **[IBM Q]** quantum devices and simulators. ## Installation We encourage installing Qiskit via the PIP tool (a python package manager), which installs all Qiskit elements and components, including this one. ```bash pip install qiskit ``` PIP will handle all dependencies automatically for us and you will always install the latest (and well-tested) version. To install from source, follow the instructions in the [contribution guidelines]. ## Setting up the IBMQ provider Once the package is installed, you can access the provider from Qiskit. > **Note**: Since November 2019 (and with version `0.4` of this > `qiskit-ibmq-provider` package / version `0.14` of the `qiskit` package) > legacy Quantum Experience or QConsole (v1) accounts are no longer supported. > If you are still using a v1 account, please follow the steps described in > [update instructions](#updating-to-the-new-IBM-Q-Experience) to update your account. ### Configure your IBMQ credentials 1. Create an IBM Q account or log in to your existing account by visiting the [IBM Q Experience login page]. 2. Copy (and/or optionally regenerate) your API token from your [IBM Q Experience account page]. 3. Take your token from step 2, here called `MY_API_TOKEN`, and run: ```python from qiskit import IBMQ IBMQ.save_account('MY_API_TOKEN') ``` ### Accessing your IBMQ backends After calling `IBMQ.save_account()`, your credentials will be stored on disk. Once they are stored, at any point in the future you can load and use them in your program simply via: ```python from qiskit import IBMQ provider = IBMQ.load_account() backend = provider.get_backend('ibmq_qasm_simulator') ``` Alternatively, if you do not want to save your credentials to disk and only intend to use them during the current session, you can use: ```python from qiskit import IBMQ provider = IBMQ.enable_account('MY_API_TOKEN') backend = provider.get_backend('ibmq_qasm_simulator') ``` By default, all IBM Q accounts have access to the same, open project (hub: `ibm-q`, group: `open`, project: `main`). For convenience, the `IBMQ.load_account()` and `IBMQ.enable_account()` methods will return a provider for that project. If you have access to other projects, you can use: ```python provider_2 = IBMQ.get_provider(hub='MY_HUB', group='MY_GROUP', project='MY_PROJECT') ``` ## Updating to the new IBM Q Experience Since November 2019 (and with version `0.4` of this `qiskit-ibmq-provider` package), the IBMQProvider only supports the new [IBM Q Experience], dropping support for the legacy Quantum Experience and Qconsole accounts. The new IBM Q Experience is also referred as `v2`, whereas the legacy one and Qconsole as `v1`. This section includes instructions for updating your accounts and programs. Please note that: * the IBM Q Experience `v1` credentials and the programs written for pre-0.3 versions will still be working during the `0.3.x` series. From 0.4 onwards, only `v2` credentials are supported, and it is recommended to upgrade in order to take advantage of the new features. * updating your credentials to the IBM Q Experience `v2` implies that you will need to update your programs. The sections below contain instructions on how to perform the transition. ### Updating your IBM Q Experience credentials If you have credentials for the legacy Quantum Experience or Qconsole stored in disk, you can make use of `IBMQ.update_account()` helper. This helper will read your current credentials stored in disk and attempt to convert them: ```python from qiskit import IBMQ IBMQ.update_account() ``` ``` Found 2 credentials. The credentials stored will be replaced with a single entry with token "MYTOKEN" and the new IBM Q Experience v2 URL (https://auth.quantum-computing.ibm.com/api). In order to access the provider, please use the new "IBMQ.get_provider()" methods: provider0 = IBMQ.load_account() provider1 = IBMQ.get_provider(hub='A', group='B', project='C') Note you need to update your programs in order to retrieve backends from a specific provider directly: backends = provider0.backends() backend = provider0.get_backend('ibmq_qasm_simulator') Update the credentials? [y/N] ``` Upon confirmation, your credentials will be overwritten with a valid IBM Q Experience v2 set of credentials. For more complex cases, consider deleting your previous credentials via `IBMQ.delete_accounts()` and follow the instructions in the [IBM Q Experience account page]. ### Updating your programs The new IBM Q Experience support also introduces a more structured approach for accessing backends. Previously, access to all backends was centralized through: ```python IBMQ.backends() IBMQ.get_backend('ibmq_qasm_simulator') ``` In version `0.3` onwards, the preferred way to access the backends is via a `Provider` for one of your projects instead of via the global `IBMQ` instance directly, allowing for more granular control over the project you are using: ```python my_provider = IBMQ.get_provider() my_provider.backends() my_provider.get_backend('ibmq_qasm_simulator') ``` In a similar spirit, you can check the providers that you have access to via: ```python IBMQ.providers() ``` In addition, since the new IBM Q Experience provides only one set of credentials, the account management methods in IBMQ are now in singular form. For example, you should use `IBMQ.load_account()` instead of `IBMQ.load_accounts()`. An `IBMQAccountError` exception is raised if you attempt to use the legacy methods with an IBM Q Experience v2 account. The following tables contains a quick reference for the differences between the two versions. Please refer to the documentation of each method for more in depth details: ### Account management | <0.3 / v1 credentials | >=0.3 and v2 credentials | | --- | --- | | N/A | `IBMQ.update_account()` | | `IBMQ.save_account(token, url)` | `IBMQ.save_account(token)` | `IBMQ.load_accounts()` | `provider = IBMQ.load_account()` | `IBMQ.enable_account()` | `provider = IBMQ.enable_account()` | `IBMQ.disable_accounts()` | `IBMQ.disable_account()` | `IBMQ.active_accounts()` | `IBMQ.active_account()` | `IBMQ.stored_accounts()` | `IBMQ.stored_account()` | `IBMQ.delete_accounts()` | `IBMQ.delete_account()` ### Using backends | <0.3 / v1 credentials | >=0.3 and v2 credentials | | --- | --- | | N/A | `providers = IBMQ.providers()` | | `backend = IBMQ.get_backend(name, hub='HUB')` | `provider = IBMQ.get_provider(hub='HUB')` | | | `backend = provider.get_backend(name)` | | `backends = IBMQ.backends(hub='HUB')` | `provider = IBMQ.get_provider(hub='HUB')` | | | `backends = provider.backends()` | ## Contribution Guidelines If you'd like to contribute to IBM Q provider, please take a look at our [contribution guidelines]. This project adheres to Qiskit's [code of conduct]. By participating, you are expect to uphold to this code. We use [GitHub issues] for tracking requests and bugs. Please use our [slack] for discussion and simple questions. To join our Slack community use the invite link at [Qiskit.org]. For questions that are more suited for a forum we use the `Qiskit` tag in [Stack Exchange]. ## Next Steps Now you're set up and ready to check out some of the other examples from our [Qiskit Tutorial] repository. ## Authors and Citation The Qiskit IBM Q provider is the work of [many people] who contribute to the project at different levels. If you use Qiskit, please cite as per the included [BibTeX file]. ## License [Apache License 2.0]. [IBM Q]: https://www.research.ibm.com/ibm-q/ [IBM Q Experience]: https://quantum-computing.ibm.com [IBM Q Experience login page]: https://quantum-computing.ibm.com/login [IBM Q Experience account page]: https://quantum-computing.ibm.com/account [contribution guidelines]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/CONTRIBUTING.md [code of conduct]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/CODE_OF_CONDUCT.md [GitHub issues]: https://github.com/Qiskit/qiskit-ibmq-provider/issues [slack]: https://qiskit.slack.com [Qiskit.org]: https://qiskit.org [Stack Exchange]: https://quantumcomputing.stackexchange.com/questions/tagged/qiskit [Qiskit Tutorial]: https://github.com/Qiskit/qiskit-tutorial [many people]: https://github.com/Qiskit/qiskit-ibmq-provider/graphs/contributors [BibTeX file]: https://github.com/Qiskit/qiskit/blob/master/Qiskit.bib [Apache License 2.0]: https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/LICENSE.txt Keywords: qiskit sdk quantum api ibmq Platform: UNKNOWN Classifier: Environment :: Console Classifier: License :: OSI Approved :: Apache Software License Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: MacOS Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Scientific/Engineering Requires-Python: >=3.5 Description-Content-Type: text/markdown qiskit-ibmq-provider-0.4.6/qiskit_ibmq_provider.egg-info/dependency_links.txt0000664000372000037200000000000113616666025030446 0ustar travistravis00000000000000 qiskit-ibmq-provider-0.4.6/qiskit_ibmq_provider.egg-info/top_level.txt0000664000372000037200000000000713616666025027127 0ustar travistravis00000000000000qiskit qiskit-ibmq-provider-0.4.6/qiskit_ibmq_provider.egg-info/not-zip-safe0000664000372000037200000000000113616666025026626 0ustar travistravis00000000000000 qiskit-ibmq-provider-0.4.6/qiskit_ibmq_provider.egg-info/SOURCES.txt0000664000372000037200000000633213616666025026270 0ustar travistravis00000000000000LICENSE.txt MANIFEST.in README.md setup.cfg setup.py qiskit/providers/ibmq/VERSION.txt qiskit/providers/ibmq/__init__.py qiskit/providers/ibmq/accountprovider.py qiskit/providers/ibmq/apiconstants.py qiskit/providers/ibmq/backendjoblimit.py qiskit/providers/ibmq/exceptions.py qiskit/providers/ibmq/ibmqbackend.py qiskit/providers/ibmq/ibmqbackendservice.py qiskit/providers/ibmq/ibmqfactory.py qiskit/providers/ibmq/version.py qiskit/providers/ibmq/api/__init__.py qiskit/providers/ibmq/api/exceptions.py qiskit/providers/ibmq/api/session.py qiskit/providers/ibmq/api/clients/__init__.py qiskit/providers/ibmq/api/clients/account.py qiskit/providers/ibmq/api/clients/auth.py qiskit/providers/ibmq/api/clients/base.py qiskit/providers/ibmq/api/clients/version.py qiskit/providers/ibmq/api/clients/websocket.py qiskit/providers/ibmq/api/rest/__init__.py qiskit/providers/ibmq/api/rest/auth.py qiskit/providers/ibmq/api/rest/backend.py qiskit/providers/ibmq/api/rest/base.py qiskit/providers/ibmq/api/rest/job.py qiskit/providers/ibmq/api/rest/root.py qiskit/providers/ibmq/api/rest/validation.py qiskit/providers/ibmq/api/rest/version_finder.py qiskit/providers/ibmq/credentials/__init__.py qiskit/providers/ibmq/credentials/configrc.py qiskit/providers/ibmq/credentials/credentials.py qiskit/providers/ibmq/credentials/environ.py qiskit/providers/ibmq/credentials/exceptions.py qiskit/providers/ibmq/credentials/hubgroupproject.py qiskit/providers/ibmq/credentials/qconfig.py qiskit/providers/ibmq/credentials/updater.py qiskit/providers/ibmq/job/__init__.py qiskit/providers/ibmq/job/exceptions.py qiskit/providers/ibmq/job/ibmqjob.py qiskit/providers/ibmq/job/queueinfo.py qiskit/providers/ibmq/job/schema.py qiskit/providers/ibmq/job/utils.py qiskit/providers/ibmq/managed/__init__.py qiskit/providers/ibmq/managed/exceptions.py qiskit/providers/ibmq/managed/ibmqjobmanager.py qiskit/providers/ibmq/managed/managedjob.py qiskit/providers/ibmq/managed/managedjobset.py qiskit/providers/ibmq/managed/managedresults.py qiskit/providers/ibmq/managed/utils.py qiskit/providers/ibmq/utils/__init__.py qiskit/providers/ibmq/utils/fields.py qiskit/providers/ibmq/utils/qobj_utils.py qiskit/providers/ibmq/utils/utils.py qiskit_ibmq_provider.egg-info/PKG-INFO qiskit_ibmq_provider.egg-info/SOURCES.txt qiskit_ibmq_provider.egg-info/dependency_links.txt qiskit_ibmq_provider.egg-info/not-zip-safe qiskit_ibmq_provider.egg-info/requires.txt qiskit_ibmq_provider.egg-info/top_level.txt test/__init__.py test/contextmanagers.py test/decorators.py test/fake_account_client.py test/ibmqtestcase.py test/jobtestcase.py test/utils.py test/ibmq/__init__.py test/ibmq/test_account_client.py test/ibmq/test_filter_backends.py test/ibmq/test_ibmq_backend.py test/ibmq/test_ibmq_backends.py test/ibmq/test_ibmq_factory.py test/ibmq/test_ibmq_integration.py test/ibmq/test_ibmq_job.py test/ibmq/test_ibmq_job_attributes.py test/ibmq/test_ibmq_job_model.py test/ibmq/test_ibmq_job_states.py test/ibmq/test_ibmq_jobmanager.py test/ibmq/test_ibmq_provider.py test/ibmq/test_ibmq_qasm_simulator.py test/ibmq/test_proxies.py test/ibmq/test_registration.py test/ibmq/websocket/__init__.py test/ibmq/websocket/test_websocket.py test/ibmq/websocket/test_websocket_integration.py test/ibmq/websocket/websocket_server.pyqiskit-ibmq-provider-0.4.6/qiskit_ibmq_provider.egg-info/requires.txt0000664000372000037200000000016213616666025026777 0ustar travistravis00000000000000nest-asyncio!=1.1.0,>=1.0.0 qiskit-terra>=0.10 requests>=2.19 requests-ntlm>=1.1.0 websockets<8,>=7 arrow>=0.15.5