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:
Diego M. Rodríguez 2018-11-27 02:56:18 +01:00 committed by Jay Gambetta
parent 5e50f68488
commit 815ddfe01e
13 changed files with 193 additions and 44 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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())

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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):

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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)