PulseQobj and Json Schema updates for pulse simulator (#2052)

* add json schema of pulse simulator configuration

* update custom field of nested dictionary parameter

* add hamiltonian schema

* change dt validation type to float

* add specific QobjConfig

* remove simulator dedicated config

* remove helper qobjs and use pattern properties

* update unittest

* avoid lint error

* relax required restriction in pulse config

* modify simulator config and add schema for each field

* fix schema

* update unittest

* removed unnecessary validation

* remove lint error

* change module name and import

* update simulator_spec

* set required to meas_lo_freq and qubit_lo_freq

* update field name

* allow for empty list of gates and basis_gates

* remove lint error

* reordered pulse qobj config arguments.

* Remove simulator_spec from jsonschema.

* remove simulator spec.
This commit is contained in:
nkanazawa 2019-04-28 05:47:28 +09:00 committed by Thomas Alexander
parent eddef45d42
commit 7922df9518
9 changed files with 93 additions and 69 deletions

View File

@ -7,9 +7,8 @@
"""Module for the Qobj structure.""" """Module for the Qobj structure."""
from .models.base import (QobjInstruction, QobjExperimentHeader, from .models.base import (QobjInstruction, QobjExperimentHeader, QobjExperimentConfig,
QobjExperimentConfig, QobjExperiment, QobjExperiment, QobjConfig, QobjHeader)
QobjConfig, QobjHeader)
from .models.pulse import (PulseQobjInstruction, PulseQobjExperimentConfig, from .models.pulse import (PulseQobjInstruction, PulseQobjExperimentConfig,
PulseQobjExperiment, PulseQobjConfig, PulseQobjExperiment, PulseQobjConfig,

View File

@ -10,9 +10,8 @@
from marshmallow.validate import Range, Regexp, Length, OneOf from marshmallow.validate import Range, Regexp, Length, OneOf
from qiskit.qobj.utils import MeasReturnType from qiskit.qobj.utils import MeasReturnType
from qiskit.validation import bind_schema, BaseSchema, BaseModel from qiskit.validation import BaseSchema, bind_schema, BaseModel
from qiskit.validation.fields import (Integer, String, Number, Complex, from qiskit.validation.fields import Integer, String, Number, Complex, List, Nested, DictParameters
List, Nested, MeasurementParameter)
from .base import (QobjInstructionSchema, QobjExperimentConfigSchema, QobjExperimentSchema, from .base import (QobjInstructionSchema, QobjExperimentConfigSchema, QobjExperimentSchema,
QobjConfigSchema, QobjInstruction, QobjExperimentConfig, QobjConfigSchema, QobjInstruction, QobjExperimentConfig,
QobjExperiment, QobjConfig) QobjExperiment, QobjConfig)
@ -23,7 +22,8 @@ class QobjMeasurementOptionSchema(BaseSchema):
# Required properties. # Required properties.
name = String(required=True) name = String(required=True)
params = MeasurementParameter(required=True) params = DictParameters(valid_value_types=(int, float, str, bool, type(None)),
required=True)
class QobjPulseLibrarySchema(BaseSchema): class QobjPulseLibrarySchema(BaseSchema):
@ -76,18 +76,20 @@ class PulseQobjExperimentSchema(QobjExperimentSchema):
class PulseQobjConfigSchema(QobjConfigSchema): class PulseQobjConfigSchema(QobjConfigSchema):
"""Schema for PulseQobjConfig.""" """Schema for PulseQobjConfig of device backend."""
# pylint: disable=invalid-name
# Required properties. # Required properties.
# TODO : check if they are always required by backend
meas_level = Integer(required=True, validate=Range(min=0, max=2)) meas_level = Integer(required=True, validate=Range(min=0, max=2))
memory_slot_size = Integer(required=True) meas_return = String(required=True, validate=OneOf(choices=(MeasReturnType.AVERAGE,
pulse_library = Nested(QobjPulseLibrarySchema, many=True, required=True) MeasReturnType.SINGLE)))
qubit_lo_freq = List(Number(), required=True) pulse_library = Nested(QobjPulseLibrarySchema, required=True, many=True)
meas_lo_freq = List(Number(), required=True) qubit_lo_freq = List(Number(validate=Range(min=0)), required=True)
rep_time = Integer(required=True) meas_lo_freq = List(Number(validate=Range(min=0)), required=True)
meas_return = String(validate=OneOf(choices=(MeasReturnType.AVERAGE,
MeasReturnType.SINGLE))) # Optional properties.
memory_slot_size = Integer(validate=Range(min=1))
rep_time = Integer(validate=Range(min=0))
@bind_schema(QobjMeasurementOptionSchema) @bind_schema(QobjMeasurementOptionSchema)
@ -182,29 +184,22 @@ class PulseQobjConfig(QobjConfig):
Attributes: Attributes:
meas_level (int): a value represents the level of measurement. meas_level (int): a value represents the level of measurement.
memory_slot_size (int): size of memory slot meas_lo_freq (list[float]): local oscillator frequency of measurement pulse.
meas_return (str): a level of measurement information. meas_return (str): a level of measurement information.
pulse_library (list[QobjPulseLibrary]): a pulse library. pulse_library (list[qiskit.qobj.QobjPulseLibrary]): a pulse library.
qubit_lo_freq (list): the list of frequencies for qubit drive LO's in GHz. qubit_lo_freq (list[float]): local oscillator frequency of driving pulse.
meas_lo_freq (list): the list of frequencies for measurement drive LO's in GHz.
rep_time (int): the value of repetition time of experiment in us.
""" """
def __init__(self, meas_level, memory_slot_size, meas_return, def __init__(self, meas_level, meas_return, pulse_library,
pulse_library, qubit_lo_freq, meas_lo_freq, rep_time, qubit_lo_freq, meas_lo_freq, **kwargs):
**kwargs):
self.meas_level = meas_level self.meas_level = meas_level
self.memory_slot_size = memory_slot_size
self.meas_return = meas_return self.meas_return = meas_return
self.pulse_library = pulse_library self.pulse_library = pulse_library
self.qubit_lo_freq = qubit_lo_freq self.qubit_lo_freq = qubit_lo_freq
self.meas_lo_freq = meas_lo_freq self.meas_lo_freq = meas_lo_freq
self.rep_time = rep_time
super().__init__(meas_level=meas_level, super().__init__(meas_level=meas_level,
memory_slot_size=memory_slot_size,
meas_return=meas_return, meas_return=meas_return,
pulse_library=pulse_library, pulse_library=pulse_library,
qubit_lo_freq=qubit_lo_freq, qubit_lo_freq=qubit_lo_freq,
meas_lo_freq=meas_lo_freq, meas_lo_freq=meas_lo_freq,
rep_time=rep_time,
**kwargs) **kwargs)

View File

@ -54,7 +54,7 @@
"type": "array", "type": "array",
"description": "List of basis gates names on the backend", "description": "List of basis gates names on the backend",
"items": {"type": "string"}, "items": {"type": "string"},
"minItems": 1 "minItems": 0
}, },
"coupling_map": { "coupling_map": {
"type": "array", "type": "array",
@ -66,7 +66,7 @@
"type": "array", "type": "array",
"description": "List of basis gates on the backend", "description": "List of basis gates on the backend",
"items": {"$ref": "#/definitions/gateconfig"}, "items": {"$ref": "#/definitions/gateconfig"},
"minItems": 1 "minItems": 0
}, },
"local": { "local": {
"type": "boolean", "type": "boolean",

View File

@ -720,7 +720,7 @@
"description": "Total number of qubits used in this experiment.", "description": "Total number of qubits used in this experiment.",
"minimum": 1, "minimum": 1,
"type": "integer" "type": "integer"
} }
}, },
"title": "Experiment level configuration", "title": "Experiment level configuration",
"type": "object" "type": "object"
@ -736,10 +736,10 @@
"description": "Total number of (slow) measurement slots used in this experiment.", "description": "Total number of (slow) measurement slots used in this experiment.",
"minimum": 0, "minimum": 0,
"type": "integer" "type": "integer"
}, },
"n_qubits": { "n_qubits": {
"description": "Total number of qubits used in this experiment.", "description": "Total number of qubits used in this experiment.",
"minimum": 1, "minimum": 1,
"type": "integer" "type": "integer"
}, },
"qreg_sizes": { "qreg_sizes": {
@ -895,7 +895,7 @@
"description": "The number of qubits requested on the backend, equivalent to the max of n_qubits requested by individual experiments.", "description": "The number of qubits requested on the backend, equivalent to the max of n_qubits requested by individual experiments.",
"minimum": 1, "minimum": 1,
"type": "integer" "type": "integer"
}, },
"seed": { "seed": {
"default": 1, "default": 1,
"type": "integer" "type": "integer"
@ -904,7 +904,7 @@
"description": "Number of repetitions of each experiment", "description": "Number of repetitions of each experiment",
"minimum": 1, "minimum": 1,
"type": "integer" "type": "integer"
} }
}, },
"title": "Qobj-level configuration", "title": "Qobj-level configuration",
"type": "object" "type": "object"
@ -1007,12 +1007,10 @@
}, },
"required": [ "required": [
"meas_level", "meas_level",
"pulse_library",
"memory_slot_size",
"meas_return",
"qubit_lo_freq",
"meas_lo_freq", "meas_lo_freq",
"rep_time" "meas_return",
"pulse_library",
"qubit_lo_freq"
] ]
}, },
"experiments": { "experiments": {

View File

@ -31,9 +31,9 @@ from marshmallow import fields as _fields
from qiskit.validation import ModelTypeValidator from qiskit.validation import ModelTypeValidator
from qiskit.validation.fields.polymorphic import ByAttribute, ByType, TryFrom from qiskit.validation.fields.polymorphic import ByAttribute, ByType, TryFrom
from qiskit.validation.fields.containers import Nested, List from qiskit.validation.fields.containers import Nested, List, Dict
from .custom import Complex, InstructionParameter, MeasurementParameter from .custom import Complex, InstructionParameter, DictParameters
class String(_fields.String, ModelTypeValidator): class String(_fields.String, ModelTypeValidator):

View File

@ -7,7 +7,7 @@
"""Container fields that represent nested/collections of schemas or types.""" """Container fields that represent nested/collections of schemas or types."""
from collections.abc import Iterable from collections.abc import Iterable, Mapping
from marshmallow import fields as _fields from marshmallow import fields as _fields
from marshmallow.exceptions import ValidationError from marshmallow.exceptions import ValidationError
@ -74,3 +74,10 @@ class List(_fields.List, ModelTypeValidator):
raise ValidationError(errors) raise ValidationError(errors)
return value return value
class Dict(_fields.Dict, ModelTypeValidator):
# pylint: disable=missing-docstring
__doc__ = _fields.Dict.__doc__
valid_types = (Mapping, )

View File

@ -12,7 +12,7 @@ import sympy
from marshmallow.utils import is_collection from marshmallow.utils import is_collection
from marshmallow.exceptions import ValidationError from marshmallow.exceptions import ValidationError
from marshmallow.compat import Mapping, Iterable from marshmallow.compat import Mapping
from qiskit.validation import ModelTypeValidator from qiskit.validation import ModelTypeValidator
@ -124,15 +124,27 @@ class InstructionParameter(ModelTypeValidator):
return root_value return root_value
class MeasurementParameter(ModelTypeValidator): class DictParameters(ModelTypeValidator):
"""Field for objects used in measurement kernel and discriminator parameters. """Field for objects used in measurement kernel and discriminator parameters.
""" """
default_error_messages = { default_error_messages = {
'invalid': 'Not a valid mapping type.', 'invalid_mapping': 'Not a valid mapping type.',
'invalid_sub': 'Not a valid value.' 'invalid': '{input} cannot be parsed as a parameter.'
} }
valid_types = (int, float, str, bool, Iterable, Mapping, type(None)) def __init__(self, valid_value_types, **kwargs):
"""Create new model.
Args:
valid_value_types (tuple): valid types as values.
"""
# pylint: disable=missing-param-doc
super(DictParameters, self).__init__(**kwargs)
self.valid_value_types = valid_value_types
def _expected_types(self):
return self.valid_value_types
def check_type(self, value, attr, data): def check_type(self, value, attr, data):
if value is None: if value is None:
@ -141,41 +153,47 @@ class MeasurementParameter(ModelTypeValidator):
_check_type = super().check_type _check_type = super().check_type
errors = [] errors = []
if isinstance(value, Mapping): if not isinstance(data[attr], Mapping):
for v in value.values(): self.fail('invalid_mapping')
try:
_check_type(v, None, value) try:
except ValidationError as err: if isinstance(value, Mapping):
errors.append(err.messages) for v in value.values():
else: self.check_type(v, attr, data)
errors.append('Not a valid mapping type.') elif is_collection(value):
for v in value:
self.check_type(v, attr, data)
else:
_check_type(value, attr, data)
except ValidationError as err:
errors.append(err.messages)
if errors: if errors:
raise ValidationError(errors) raise ValidationError(errors)
return value return value
def _serialize_sub(self, value): def _validate_values(self, value):
# pylint: disable=too-many-return-statements # pylint: disable=too-many-return-statements
if value is None: if value is None:
return None return None
if isinstance(value, (int, float, str, bool)): if isinstance(value, self.valid_value_types):
return value return value
if isinstance(value, Iterable): if is_collection(value):
return [self._serialize_sub(each) for each in value] return [self._validate_values(each) for each in value]
if isinstance(value, Mapping): if isinstance(value, Mapping):
return {str(k): self._serialize_sub(v) for k, v in value.items()} return {str(k): self._validate_values(v) for k, v in value.items()}
return self.fail('invalid_sub', input=value) return self.fail('invalid', input=value)
def _serialize(self, value, attr, obj): def _serialize(self, value, attr, obj):
# pylint: disable=too-many-return-statements # pylint: disable=too-many-return-statements
if value is None: if value is None:
return None return None
if isinstance(value, Mapping): if isinstance(value, Mapping):
return {str(k): self._serialize_sub(v) for k, v in value.items()} return {str(k): self._validate_values(v) for k, v in value.items()}
return self.fail('invalid') return self.fail('invalid_mapping')
def _deserialize(self, value, attr, data): def _deserialize(self, value, attr, data):
# pylint: disable=too-many-return-statements # pylint: disable=too-many-return-statements
@ -184,4 +202,4 @@ class MeasurementParameter(ModelTypeValidator):
if isinstance(value, Mapping): if isinstance(value, Mapping):
return value return value
return self.fail('invalid') return self.fail('invalid_mapping')

View File

@ -7,6 +7,8 @@
"""Validators for Qiskit validated classes.""" """Validators for Qiskit validated classes."""
from collections.abc import Mapping
from marshmallow import ValidationError from marshmallow import ValidationError
from marshmallow.validate import Validator from marshmallow.validate import Validator
@ -68,7 +70,13 @@ class PatternProperties(Validator):
def __call__(self, value): def __call__(self, value):
errors = {} errors = {}
for key, value_ in value.__dict__.items():
if isinstance(value, Mapping):
_dict = value
else:
_dict = value.__dict__
for key, value_ in _dict.items():
# Attempt to validate the keys against any field. # Attempt to validate the keys against any field.
field = None field = None
for validator, candidate in self.pattern_properties.items(): for validator, candidate in self.pattern_properties.items():

View File

@ -178,8 +178,7 @@ class TestPulseQobj(QiskitTestCase):
], ],
'qubit_lo_freq': [4.9], 'qubit_lo_freq': [4.9],
'meas_lo_freq': [6.9], 'meas_lo_freq': [6.9],
'rep_time': 1000 'rep_time': 1000},
},
'experiments': [ 'experiments': [
{'instructions': [ {'instructions': [
{'name': 'pulse0', 't0': 0, 'ch': 'd0'}, {'name': 'pulse0', 't0': 0, 'ch': 'd0'},
@ -225,7 +224,7 @@ class TestPulseQobj(QiskitTestCase):
'pulse_library': [{'name': 'pulse0', 'samples': [[0.1, 0.0]]}], 'pulse_library': [{'name': 'pulse0', 'samples': [[0.1, 0.0]]}],
'qubit_lo_freq': [4.9], 'qubit_lo_freq': [4.9],
'meas_lo_freq': [6.9], 'meas_lo_freq': [6.9],
'rep_time': 1000} 'rep_time': 1000},
), ),
QobjPulseLibrary: ( QobjPulseLibrary: (
QobjPulseLibrary(name='pulse0', samples=[0.1 + 0.0j]), QobjPulseLibrary(name='pulse0', samples=[0.1 + 0.0j]),