mirror of https://github.com/Qiskit/qiskit.git
Update backend.properties() to use BackendProperties (#1331)
* Fix BackendProperties required lengths Fix the required length of some items in `BackendProperties`, to adjust to specs. * Update backend.properties() return model Update `BaseBackend.properties()` in order to return instances of `BackendProperties` instead of dicts. Update the local simulators with a basic `BackendProperties` - some of the fields (gates, qubits) are populated with dummy values in order to conform to the specs, but need to be revised. * Enable test_aer_backend_properties, fix dummy sim Enable the `test_aer_backend_properties`, for checking that all local simulators produce schema-conformant BackendProperties. Fix the dummy sim with properties(). * Update IBMQBackend.properties() using model Update `IBMQBackend.properties()` for returning an instance of `BackendProperties` instead of a dict. The logic for converting the API properties has been moved to IBMQConnector, taking into account the format returned currently for QX and IBMQ, while waiting for the new endpoints with the final format. Enable `test_remote_backend_properties` for testing the feature. * Update CHANGELOG
This commit is contained in:
parent
5e50f68488
commit
815ddfe01e
|
@ -71,6 +71,7 @@ disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax
|
|||
protected-access, # disabled as we don't follow the public vs private
|
||||
# convention strictly
|
||||
duplicate-code, # disabled as it is too verbose
|
||||
redundant-returns-doc, # for @abstractmethod, it cannot interpret "pass"
|
||||
# disable the "too-many/few-..." refactoring hints
|
||||
too-many-lines, too-many-branches, too-many-locals, too-many-nested-blocks,
|
||||
too-many-statements, too-many-instance-attributes, too-many-arguments,
|
||||
|
|
|
@ -68,6 +68,7 @@ Changed
|
|||
updated methods include:
|
||||
- ``backend.status()``(#1301).
|
||||
- ``backend.configuration()`` (and ``__init__``) (#1323).
|
||||
- ``backend.properties()`` (#1331).
|
||||
- ``backend.provider()`` is now a method instead of a property (#1312).
|
||||
- Remove local backend (Aer) fallback (#1303)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import platform
|
|||
|
||||
import numpy as np
|
||||
|
||||
from qiskit.backends.models import BackendConfiguration
|
||||
from qiskit.backends.models import BackendConfiguration, BackendProperties
|
||||
from qiskit.result._utils import copy_qasm_from_qobj_into_result, result_from_old_style_dict
|
||||
from qiskit.backends import BaseBackend
|
||||
from qiskit.backends.aer.aerjob import AerJob
|
||||
|
@ -82,6 +82,23 @@ class QasmSimulator(BaseBackend):
|
|||
raise FileNotFoundError('Simulator executable not found (using %s)' %
|
||||
getattr(self._configuration, 'exe', 'default locations'))
|
||||
|
||||
def properties(self):
|
||||
"""Return backend properties"""
|
||||
properties = {
|
||||
'backend_name': self.name(),
|
||||
'backend_version': self.configuration().backend_version,
|
||||
'last_update_date': '2000-01-01 00:00:00Z',
|
||||
'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]],
|
||||
'gates': [{'qubits': [0], 'gate': 'TODO',
|
||||
'parameters':
|
||||
[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]}],
|
||||
'general': []
|
||||
}
|
||||
|
||||
return BackendProperties.from_dict(properties)
|
||||
|
||||
def run(self, qobj):
|
||||
"""Run a qobj on the backend."""
|
||||
job_id = str(uuid.uuid4())
|
||||
|
@ -145,6 +162,23 @@ class CliffordSimulator(BaseBackend):
|
|||
raise FileNotFoundError('Simulator executable not found (using %s)' %
|
||||
getattr(self._configuration, 'exe', 'default locations'))
|
||||
|
||||
def properties(self):
|
||||
"""Return backend properties"""
|
||||
properties = {
|
||||
'backend_name': self.name(),
|
||||
'backend_version': self.configuration().backend_version,
|
||||
'last_update_date': '2000-01-01 00:00:00Z',
|
||||
'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]],
|
||||
'gates': [{'qubits': [0], 'gate': 'TODO',
|
||||
'parameters':
|
||||
[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]}],
|
||||
'general': []
|
||||
}
|
||||
|
||||
return BackendProperties.from_dict(properties)
|
||||
|
||||
def run(self, qobj):
|
||||
"""Run a Qobj on the backend.
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ from collections import Counter
|
|||
|
||||
import numpy as np
|
||||
|
||||
from qiskit.backends.models import BackendConfiguration
|
||||
from qiskit.backends.models import BackendConfiguration, BackendProperties
|
||||
from qiskit.result._utils import copy_qasm_from_qobj_into_result, result_from_old_style_dict
|
||||
from qiskit.backends import BaseBackend
|
||||
from qiskit.backends.aer.aerjob import AerJob
|
||||
|
@ -120,6 +120,23 @@ class QasmSimulatorPy(BaseBackend):
|
|||
self._shots = 0
|
||||
self._qobj_config = None
|
||||
|
||||
def properties(self):
|
||||
"""Return backend properties"""
|
||||
properties = {
|
||||
'backend_name': self.name(),
|
||||
'backend_version': self.configuration().backend_version,
|
||||
'last_update_date': '2000-01-01 00:00:00Z',
|
||||
'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]],
|
||||
'gates': [{'qubits': [0], 'gate': 'TODO',
|
||||
'parameters':
|
||||
[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]}],
|
||||
'general': []
|
||||
}
|
||||
|
||||
return BackendProperties.from_dict(properties)
|
||||
|
||||
def _add_qasm_single(self, gate, qubit):
|
||||
"""Apply an arbitrary 1-qubit operator to a qubit.
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Interface to C++ quantum circuit simulator with realistic noise.
|
|||
import logging
|
||||
import uuid
|
||||
|
||||
from qiskit.backends.models import BackendConfiguration
|
||||
from qiskit.backends.models import BackendConfiguration, BackendProperties
|
||||
from qiskit.qobj import QobjInstruction
|
||||
from .qasm_simulator import QasmSimulator
|
||||
from ._simulatorerror import SimulatorError
|
||||
|
@ -47,6 +47,23 @@ class StatevectorSimulator(QasmSimulator):
|
|||
BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)),
|
||||
provider=provider)
|
||||
|
||||
def properties(self):
|
||||
"""Return backend properties"""
|
||||
properties = {
|
||||
'backend_name': self.name(),
|
||||
'backend_version': self.configuration().backend_version,
|
||||
'last_update_date': '2000-01-01 00:00:00Z',
|
||||
'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]],
|
||||
'gates': [{'qubits': [0], 'gate': 'TODO',
|
||||
'parameters':
|
||||
[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]}],
|
||||
'general': []
|
||||
}
|
||||
|
||||
return BackendProperties.from_dict(properties)
|
||||
|
||||
def run(self, qobj):
|
||||
"""Run a qobj on the the backend."""
|
||||
job_id = str(uuid.uuid4())
|
||||
|
|
|
@ -23,7 +23,7 @@ import uuid
|
|||
|
||||
from qiskit.backends.aer.aerjob import AerJob
|
||||
from qiskit.backends.aer._simulatorerror import SimulatorError
|
||||
from qiskit.backends.models import BackendConfiguration
|
||||
from qiskit.backends.models import BackendConfiguration, BackendProperties
|
||||
from qiskit.qobj import QobjInstruction
|
||||
from .qasm_simulator_py import QasmSimulatorPy
|
||||
|
||||
|
@ -52,6 +52,23 @@ class StatevectorSimulatorPy(QasmSimulatorPy):
|
|||
BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)),
|
||||
provider=provider)
|
||||
|
||||
def properties(self):
|
||||
"""Return backend properties"""
|
||||
properties = {
|
||||
'backend_name': self.name(),
|
||||
'backend_version': self.configuration().backend_version,
|
||||
'last_update_date': '2000-01-01 00:00:00Z',
|
||||
'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]],
|
||||
'gates': [{'qubits': [0], 'gate': 'TODO',
|
||||
'parameters':
|
||||
[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]}],
|
||||
'general': []
|
||||
}
|
||||
|
||||
return BackendProperties.from_dict(properties)
|
||||
|
||||
def run(self, qobj):
|
||||
"""Run qobj asynchronously.
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ import time
|
|||
|
||||
import numpy as np
|
||||
|
||||
from qiskit.backends.models import BackendConfiguration
|
||||
from qiskit.backends.models import BackendConfiguration, BackendProperties
|
||||
from qiskit.result._utils import copy_qasm_from_qobj_into_result, result_from_old_style_dict
|
||||
from qiskit.backends import BaseBackend
|
||||
from qiskit.backends.aer.aerjob import AerJob
|
||||
|
@ -126,6 +126,23 @@ class UnitarySimulatorPy(BaseBackend):
|
|||
self._unitary_state = None
|
||||
self._number_of_qubits = 0
|
||||
|
||||
def properties(self):
|
||||
"""Return backend properties"""
|
||||
properties = {
|
||||
'backend_name': self.name(),
|
||||
'backend_version': self.configuration().backend_version,
|
||||
'last_update_date': '2000-01-01 00:00:00Z',
|
||||
'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]],
|
||||
'gates': [{'qubits': [0], 'gate': 'TODO',
|
||||
'parameters':
|
||||
[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]}],
|
||||
'general': []
|
||||
}
|
||||
|
||||
return BackendProperties.from_dict(properties)
|
||||
|
||||
def _add_unitary_single(self, gate, qubit):
|
||||
"""Apply the single-qubit gate.
|
||||
|
||||
|
|
|
@ -47,13 +47,18 @@ class BaseBackend(ABC):
|
|||
"""Return the backend configuration.
|
||||
|
||||
Returns:
|
||||
BackendConfiguration: the configuration fot the backend.
|
||||
BackendConfiguration: the configuration for the backend.
|
||||
"""
|
||||
return self._configuration
|
||||
|
||||
@abstractmethod
|
||||
def properties(self):
|
||||
"""Return backend properties"""
|
||||
return {}
|
||||
"""Return backend properties.
|
||||
|
||||
Returns:
|
||||
BackendProperties: the configuration for the backend.
|
||||
"""
|
||||
pass
|
||||
|
||||
def provider(self):
|
||||
"""Return the backend Provider.
|
||||
|
|
|
@ -786,12 +786,43 @@ class IBMQConnector(object):
|
|||
|
||||
url = get_backend_properties_url(self.config, backend_type, hub)
|
||||
|
||||
ret = self.req.get(url)
|
||||
if not bool(ret):
|
||||
ret = {}
|
||||
else:
|
||||
ret["backend_name"] = backend_type
|
||||
return ret
|
||||
response = self.req.get(url)
|
||||
|
||||
# Check for empty properties (simulators).
|
||||
if not bool(response):
|
||||
return {}
|
||||
|
||||
# Adjust fields according to the specs (BackendProperties).
|
||||
properties = {
|
||||
'backend_name': backend_type,
|
||||
'backend_version': response.get('backend_version', '0.0.0'),
|
||||
'last_update_date': response['lastUpdateDate'],
|
||||
'gates': [],
|
||||
'qubits': [],
|
||||
'general': []
|
||||
}
|
||||
|
||||
# Convert "gates" field..
|
||||
for gate in response['multiQubitGates']:
|
||||
properties['gates'].append({
|
||||
'qubits': gate['qubits'],
|
||||
'gate': gate['type'],
|
||||
'parameters': [{'name': 'gateerr',
|
||||
'date': gate['gateError']['date'],
|
||||
'unit': 'us',
|
||||
'value': gate['gateError']['value']}]})
|
||||
|
||||
# Convert "qubits" field.
|
||||
for qubit in response['qubits']:
|
||||
qubit_edit = [
|
||||
{'name': key,
|
||||
'date': value['date'],
|
||||
'unit': value.get('unit', '-'),
|
||||
'value': value['value']} for key, value in qubit.items()
|
||||
if key not in ('name',)]
|
||||
properties['qubits'].append(qubit_edit)
|
||||
|
||||
return properties
|
||||
|
||||
def available_backends(self, hub=None, group=None, project=None,
|
||||
access_token=None, user_id=None):
|
||||
|
|
|
@ -14,9 +14,8 @@ import logging
|
|||
from marshmallow import ValidationError
|
||||
|
||||
from qiskit import QISKitError
|
||||
from qiskit._util import _camel_case_to_snake_case
|
||||
from qiskit.backends import BaseBackend, JobStatus
|
||||
from qiskit.backends.models import BackendStatus
|
||||
from qiskit.backends.models import BackendStatus, BackendProperties
|
||||
|
||||
from .api import ApiError
|
||||
from .ibmqjob import IBMQJob, IBMQJobPreQobj
|
||||
|
@ -68,20 +67,10 @@ class IBMQBackend(BaseBackend):
|
|||
|
||||
Returns:
|
||||
dict: The properties of the backend.
|
||||
|
||||
Raises:
|
||||
LookupError: If properties for the backend can't be found.
|
||||
"""
|
||||
properties = self._api.backend_properties(self.name())
|
||||
api_properties = self._api.backend_properties(self.name())
|
||||
|
||||
# Convert the properties to snake_case.
|
||||
# TODO: ideally should be handled at the API level.
|
||||
properties_edit = {}
|
||||
for key, val in properties.items():
|
||||
new_key = _camel_case_to_snake_case(key)
|
||||
properties_edit[new_key] = val
|
||||
|
||||
return properties_edit
|
||||
return BackendProperties.from_dict(api_properties)
|
||||
|
||||
def status(self):
|
||||
"""Return the online backend status.
|
||||
|
|
|
@ -30,7 +30,8 @@ class GateSchema(BaseSchema):
|
|||
qubits = List(Number(), required=True,
|
||||
validate=Length(min=1))
|
||||
gate = String(required=True)
|
||||
parameters = Nested(NduvSchema, required=True, many=True)
|
||||
parameters = Nested(NduvSchema, required=True, many=True,
|
||||
validate=Length(min=1))
|
||||
|
||||
|
||||
class BackendPropertiesSchema(BaseSchema):
|
||||
|
@ -41,9 +42,11 @@ class BackendPropertiesSchema(BaseSchema):
|
|||
backend_version = String(required=True,
|
||||
validate=Regexp("[0-9]+.[0-9]+.[0-9]+$"))
|
||||
last_update_date = DateTime(required=True)
|
||||
qubits = List(Nested(NduvSchema, many=True), required=True,
|
||||
qubits = List(Nested(NduvSchema, many=True,
|
||||
validate=Length(min=1)), required=True,
|
||||
validate=Length(min=1))
|
||||
gates = Nested(GateSchema, required=True, many=True)
|
||||
gates = Nested(GateSchema, required=True, many=True,
|
||||
validate=Length(min=1))
|
||||
general = Nested(NduvSchema, required=True, many=True)
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import time
|
|||
from qiskit import Result
|
||||
from qiskit.backends import BaseBackend
|
||||
from qiskit.backends import BaseJob
|
||||
from qiskit.backends.models import BackendProperties
|
||||
from qiskit.qobj import Qobj, QobjItem, QobjConfig, QobjHeader, QobjInstruction
|
||||
from qiskit.qobj import QobjExperiment, QobjExperimentHeader
|
||||
from qiskit.backends.jobstatus import JobStatus
|
||||
|
@ -67,6 +68,23 @@ class DummySimulator(BaseBackend):
|
|||
super().__init__(configuration or self.DEFAULT_CONFIGURATION.copy())
|
||||
self.time_alive = time_alive
|
||||
|
||||
def properties(self):
|
||||
"""Return backend properties"""
|
||||
properties = {
|
||||
'backend_name': self.name(),
|
||||
'backend_version': self.configuration().backend_version,
|
||||
'last_update_date': '2000-01-01 00:00:00Z',
|
||||
'qubits': [[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]],
|
||||
'gates': [{'qubits': [0], 'gate': 'TODO',
|
||||
'parameters':
|
||||
[{'name': 'TODO', 'date': '2000-01-01 00:00:00Z',
|
||||
'unit': 'TODO', 'value': 0}]}],
|
||||
'general': []
|
||||
}
|
||||
|
||||
return BackendProperties.from_dict(properties)
|
||||
|
||||
def run(self, qobj):
|
||||
job_id = str(uuid.uuid4())
|
||||
job = DummyJob(self.run_job, qobj, job_id, self)
|
||||
|
|
|
@ -134,12 +134,15 @@ class TestBackends(QiskitTestCase):
|
|||
|
||||
If all correct should pass the validation.
|
||||
"""
|
||||
schema_path = self._get_resource_path(
|
||||
'backend_properties_schema.json', path=Path.SCHEMAS)
|
||||
with open(schema_path, 'r') as schema_file:
|
||||
schema = json.load(schema_file)
|
||||
|
||||
aer_backends = Aer.backends()
|
||||
for backend in aer_backends:
|
||||
properties = backend.properties()
|
||||
# FIXME test against schema and decide what properties
|
||||
# is for a simulator
|
||||
self.assertEqual(len(properties), 0)
|
||||
jsonschema.validate(properties.to_dict(), schema)
|
||||
|
||||
@requires_qe_access
|
||||
def test_remote_backend_properties(self, qe_token, qe_url):
|
||||
|
@ -147,17 +150,13 @@ class TestBackends(QiskitTestCase):
|
|||
|
||||
If all correct should pass the validation.
|
||||
"""
|
||||
schema_path = self._get_resource_path(
|
||||
'backend_properties_schema.json', path=Path.SCHEMAS)
|
||||
with open(schema_path, 'r') as schema_file:
|
||||
schema = json.load(schema_file)
|
||||
|
||||
IBMQ.enable_account(qe_token, qe_url)
|
||||
remotes = IBMQ.backends(simulator=False)
|
||||
for backend in remotes:
|
||||
self.log.info(backend.name())
|
||||
properties = backend.properties()
|
||||
# FIXME test against schema and decide what properties
|
||||
# is for a simulator
|
||||
if backend.configuration().simulator:
|
||||
self.assertEqual(len(properties), 0)
|
||||
else:
|
||||
self.assertTrue(all(key in properties for key in (
|
||||
'last_update_date',
|
||||
'qubits',
|
||||
'backend_name')))
|
||||
jsonschema.validate(properties.to_dict(), schema)
|
||||
|
|
Loading…
Reference in New Issue