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."""
from .models.base import (QobjInstruction, QobjExperimentHeader,
QobjExperimentConfig, QobjExperiment,
QobjConfig, QobjHeader)
from .models.base import (QobjInstruction, QobjExperimentHeader, QobjExperimentConfig,
QobjExperiment, QobjConfig, QobjHeader)
from .models.pulse import (PulseQobjInstruction, PulseQobjExperimentConfig,
PulseQobjExperiment, PulseQobjConfig,

View File

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

View File

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

View File

@ -720,7 +720,7 @@
"description": "Total number of qubits used in this experiment.",
"minimum": 1,
"type": "integer"
}
}
},
"title": "Experiment level configuration",
"type": "object"
@ -736,10 +736,10 @@
"description": "Total number of (slow) measurement slots used in this experiment.",
"minimum": 0,
"type": "integer"
},
},
"n_qubits": {
"description": "Total number of qubits used in this experiment.",
"minimum": 1,
"minimum": 1,
"type": "integer"
},
"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.",
"minimum": 1,
"type": "integer"
},
},
"seed": {
"default": 1,
"type": "integer"
@ -904,7 +904,7 @@
"description": "Number of repetitions of each experiment",
"minimum": 1,
"type": "integer"
}
}
},
"title": "Qobj-level configuration",
"type": "object"
@ -1007,12 +1007,10 @@
},
"required": [
"meas_level",
"pulse_library",
"memory_slot_size",
"meas_return",
"qubit_lo_freq",
"meas_lo_freq",
"rep_time"
"meas_return",
"pulse_library",
"qubit_lo_freq"
]
},
"experiments": {

View File

@ -31,9 +31,9 @@ from marshmallow import fields as _fields
from qiskit.validation import ModelTypeValidator
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):

View File

@ -7,7 +7,7 @@
"""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.exceptions import ValidationError
@ -74,3 +74,10 @@ class List(_fields.List, ModelTypeValidator):
raise ValidationError(errors)
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.exceptions import ValidationError
from marshmallow.compat import Mapping, Iterable
from marshmallow.compat import Mapping
from qiskit.validation import ModelTypeValidator
@ -124,15 +124,27 @@ class InstructionParameter(ModelTypeValidator):
return root_value
class MeasurementParameter(ModelTypeValidator):
class DictParameters(ModelTypeValidator):
"""Field for objects used in measurement kernel and discriminator parameters.
"""
default_error_messages = {
'invalid': 'Not a valid mapping type.',
'invalid_sub': 'Not a valid value.'
'invalid_mapping': 'Not a valid mapping type.',
'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):
if value is None:
@ -141,41 +153,47 @@ class MeasurementParameter(ModelTypeValidator):
_check_type = super().check_type
errors = []
if isinstance(value, Mapping):
for v in value.values():
try:
_check_type(v, None, value)
except ValidationError as err:
errors.append(err.messages)
else:
errors.append('Not a valid mapping type.')
if not isinstance(data[attr], Mapping):
self.fail('invalid_mapping')
try:
if isinstance(value, Mapping):
for v in value.values():
self.check_type(v, attr, data)
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:
raise ValidationError(errors)
return value
def _serialize_sub(self, value):
def _validate_values(self, value):
# pylint: disable=too-many-return-statements
if value is None:
return None
if isinstance(value, (int, float, str, bool)):
if isinstance(value, self.valid_value_types):
return value
if isinstance(value, Iterable):
return [self._serialize_sub(each) for each in value]
if is_collection(value):
return [self._validate_values(each) for each in value]
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):
# pylint: disable=too-many-return-statements
if value is None:
return None
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):
# pylint: disable=too-many-return-statements
@ -184,4 +202,4 @@ class MeasurementParameter(ModelTypeValidator):
if isinstance(value, Mapping):
return value
return self.fail('invalid')
return self.fail('invalid_mapping')

View File

@ -7,6 +7,8 @@
"""Validators for Qiskit validated classes."""
from collections.abc import Mapping
from marshmallow import ValidationError
from marshmallow.validate import Validator
@ -68,7 +70,13 @@ class PatternProperties(Validator):
def __call__(self, value):
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.
field = None
for validator, candidate in self.pattern_properties.items():

View File

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