diff --git a/.travis.yml b/.travis.yml index 60400235e..1a985bac3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ stage_generic: &stage_generic # TODO: until the split is complete, install a terra branch. # - pip install -U -r requirements.txt - git clone https://github.com/Qiskit/qiskit-terra.git + - pip install cython - pip install -e qiskit-terra - pip install "requests>=2.19" - pip install "requests-ntlm>=1.1.0" diff --git a/qiskit/providers/ibmq/api/utils.py b/qiskit/providers/ibmq/api/utils.py index e914b966f..e8f8b9d32 100644 --- a/qiskit/providers/ibmq/api/utils.py +++ b/qiskit/providers/ibmq/api/utils.py @@ -123,8 +123,7 @@ def obtain_token(self, config=None): if error_message: raise CredentialsError('error during login: %s' % error_message) - else: - raise CredentialsError('invalid token') + raise CredentialsError('invalid token') try: response.raise_for_status() self.data_credentials = response.json() @@ -355,8 +354,7 @@ def _response_good(self, response): response.status_code, url, response.text)) - else: - return self._parse_response(response) + return self._parse_response(response) try: if str(response.headers['content-type']).startswith("text/html;"): self.result = response.text diff --git a/qiskit/providers/ibmq/ibmqbackend.py b/qiskit/providers/ibmq/ibmqbackend.py index 06ca8c92b..655b4b901 100644 --- a/qiskit/providers/ibmq/ibmqbackend.py +++ b/qiskit/providers/ibmq/ibmqbackend.py @@ -13,6 +13,7 @@ from marshmallow import ValidationError from qiskit.providers import BaseBackend, JobStatus +from qiskit.providers.ibmq.utils import update_qobj_config from qiskit.providers.models import (BackendStatus, BackendProperties, PulseDefaults) @@ -63,12 +64,8 @@ def properties(self): The return is via QX API call. Returns: - BackendProperties: The properties of the backend. If the backend - is a simulator, it returns ``None``. + BackendProperties: The properties of the backend. """ - if self.configuration().simulator: - return None - api_properties = self._api.backend_properties(self.name()) return BackendProperties.from_dict(api_properties) @@ -241,3 +238,30 @@ def __repr__(self): 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): + """Return the online backend properties. + + Returns: + None + """ + return None + + def run(self, qobj, backend_options=None, noise_model=None): + """Run qobj asynchronously. + + Args: + qobj (Qobj): description of job + backend_options (dict): backend options + noise_model (NoiseModel): noise model + + Returns: + IBMQJob: an instance derived from BaseJob + """ + # pylint: disable=arguments-differ + qobj = update_qobj_config(qobj, backend_options, noise_model) + return super(IBMQSimulator, self).run(qobj) diff --git a/qiskit/providers/ibmq/ibmqsingleprovider.py b/qiskit/providers/ibmq/ibmqsingleprovider.py index 69be54043..ed5c9ad27 100644 --- a/qiskit/providers/ibmq/ibmqsingleprovider.py +++ b/qiskit/providers/ibmq/ibmqsingleprovider.py @@ -16,7 +16,7 @@ from qiskit.validation.exceptions import ModelValidationError from .api import IBMQConnector -from .ibmqbackend import IBMQBackend +from .ibmqbackend import IBMQBackend, IBMQSimulator logger = logging.getLogger(__name__) @@ -28,6 +28,7 @@ class IBMQSingleProvider(BaseProvider): Note: this class is not part of the public API and is not guaranteed to be present in future releases. """ + def __init__(self, credentials, ibmq_provider): """Return a new IBMQSingleProvider. @@ -95,7 +96,8 @@ def _discover_remote_backends(self): for raw_config in configs_list: try: config = BackendConfiguration.from_dict(raw_config) - ret[config.backend_name] = IBMQBackend( + backend_cls = IBMQSimulator if config.simulator else IBMQBackend + ret[config.backend_name] = backend_cls( configuration=config, provider=self._ibm_provider, credentials=self.credentials, diff --git a/qiskit/providers/ibmq/utils.py b/qiskit/providers/ibmq/utils.py new file mode 100644 index 000000000..75e5f446f --- /dev/null +++ b/qiskit/providers/ibmq/utils.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + +"""Utilities related to the IBMQ Provider.""" + +import json + +from numpy import ndarray + +from qiskit.qobj import QobjHeader + + +class AerJSONEncoder(json.JSONEncoder): + """JSON encoder for NumPy arrays and complex numbers. + + This functions as the standard JSON Encoder but adds support + for encoding: + complex numbers z as lists [z.real, z.imag] + ndarrays as nested lists. + """ + + # pylint: disable=method-hidden,arguments-differ + def default(self, obj): + if isinstance(obj, ndarray): + return obj.tolist() + if isinstance(obj, complex): + return [obj.real, obj.imag] + if hasattr(obj, "as_dict"): + return obj.as_dict() + return super().default(obj) + + +def update_qobj_config(qobj, backend_options=None, noise_model=None): + """Update a Qobj configuration from options and noise model. + + Args: + qobj (Qobj): description of job + backend_options (dict): backend options + noise_model (NoiseModel): noise model + + Returns: + Qobj: qobj. + """ + config = qobj.config.as_dict() + + # Append backend options to configuration. + if backend_options: + for key, val in backend_options.items(): + config[key] = val + + # Append noise model to configuration. + if noise_model: + config['noise_model'] = json.loads(json.dumps(noise_model, + cls=AerJSONEncoder)) + + # Update the Qobj configuration. + qobj.config = QobjHeader.from_dict(config) + + return qobj diff --git a/requirements-dev.txt b/requirements-dev.txt index 28bab150e..9f2d301cd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,3 @@ pycodestyle -pylint>=2.2,<2.3 +pylint>=2.3,<2.4 vcrpy