From 68f65d2f2f391cb84c69645e81e70314dd359327 Mon Sep 17 00:00:00 2001 From: Lauren Capelluto Date: Fri, 21 Aug 2020 14:05:22 -0400 Subject: [PATCH] Update the qobj schema to support pulse gate calibrations. (#4761) * Update the qobj schema to support pulse gate calibrations. * Fixup qobj classes: add docstrings, call out to super * Move pulse library to top level. Fill in more details in qasm_qobj.py * Fix mistake in schema: the pulse_library definition already contains the 'array' requirement * Move pulse library to top level in qasm_qobj.py as well * Add GateCalibration to qobj/__init__.py * Fixup some implementation errors: gates contains a list of dict items, which needed to be fixed in both the schema and the py files * Put gate calibrations into calibrations.gates to leave room for adding other metadata in the future. Make a qobj.common file to allow qobj.qasm to use features of qobj.pulse * Add an example json * Schema version should be referenced not hardcoded a second time * Pretty print example file * Check if calibrations is present when doing to from dict * Fixup qobj test * Update the qobj schema to support pulse gate calibrations. * Fixup qobj classes: add docstrings, call out to super * Move pulse library to top level. Fill in more details in qasm_qobj.py * Fix mistake in schema: the pulse_library definition already contains the 'array' requirement * Move pulse library to top level in qasm_qobj.py as well * Add GateCalibration to qobj/__init__.py * Fixup some implementation errors: gates contains a list of dict items, which needed to be fixed in both the schema and the py files * Put gate calibrations into calibrations.gates to leave room for adding other metadata in the future. Make a qobj.common file to allow qobj.qasm to use features of qobj.pulse * Add an example json * Schema version should be referenced not hardcoded a second time * Pretty print example file * Update backend snapshots with new conf or defs (#4897) In #4728 how we handle the difference between the object units and the serialization format. However the snapshot update didn't actually save the correct values in the json and this is causing issues for tutorials that rely on this. This commit updates the backend snapshots for backends by rerunning the update script to correct this issue. * One reference to get_sample_pulse is raising deprecation warnings from the assembler (#4903) * Check if calibrations is present when doing to from dict * Fixup qobj test * Add qobj.config.calibrations for common cals * style * Move recent qasm qobj changes to the new qobj.common file * Update schema with qobj level and experiment level calibrations * Add schema test * Apply suggestions from code review Co-authored-by: SooluThomas * Fix encoding for validation of new pulse library field in qasm qobj. Add qobj test Co-authored-by: Matthew Treinish Co-authored-by: SooluThomas Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- qiskit/qobj/__init__.py | 9 +- qiskit/qobj/common.py | 83 ++ qiskit/qobj/pulse_qobj.py | 8 +- qiskit/qobj/qasm_qobj.py | 188 +++-- .../schemas/examples/qasm_w_pulse_gates.json | 797 ++++++++++++++++++ qiskit/schemas/qobj_schema.json | 66 +- test/python/qobj/test_qobj.py | 51 +- test/python/test_schemas.py | 3 +- 8 files changed, 1143 insertions(+), 62 deletions(-) create mode 100644 qiskit/qobj/common.py create mode 100644 qiskit/schemas/examples/qasm_w_pulse_gates.json diff --git a/qiskit/qobj/__init__.py b/qiskit/qobj/__init__.py index 2feca4e51a78..eda56a97f328 100644 --- a/qiskit/qobj/__init__.py +++ b/qiskit/qobj/__init__.py @@ -38,6 +38,8 @@ QasmQobjExperimentConfig QasmQobjExperiment QasmQobjConfig + QasmExperimentCalibrations + GateCalibration Pulse ===== @@ -64,6 +66,9 @@ import warnings +from qiskit.qobj.common import QobjExperimentHeader +from qiskit.qobj.common import QobjHeader + from qiskit.qobj.pulse_qobj import PulseQobj from qiskit.qobj.pulse_qobj import PulseQobjInstruction from qiskit.qobj.pulse_qobj import PulseQobjExperimentConfig @@ -72,13 +77,13 @@ from qiskit.qobj.pulse_qobj import QobjMeasurementOption from qiskit.qobj.pulse_qobj import PulseLibraryItem +from qiskit.qobj.qasm_qobj import GateCalibration +from qiskit.qobj.qasm_qobj import QasmExperimentCalibrations from qiskit.qobj.qasm_qobj import QasmQobj from qiskit.qobj.qasm_qobj import QasmQobjInstruction from qiskit.qobj.qasm_qobj import QasmQobjExperiment from qiskit.qobj.qasm_qobj import QasmQobjConfig -from qiskit.qobj.qasm_qobj import QobjExperimentHeader from qiskit.qobj.qasm_qobj import QasmQobjExperimentConfig -from qiskit.qobj.qasm_qobj import QobjHeader from .utils import validate_qobj_against_schema diff --git a/qiskit/qobj/common.py b/qiskit/qobj/common.py new file mode 100644 index 000000000000..ed3b8caebbe0 --- /dev/null +++ b/qiskit/qobj/common.py @@ -0,0 +1,83 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +# pylint: disable=invalid-name + +"""Module providing definitions of common Qobj classes.""" +import json +import os +from types import SimpleNamespace + +import fastjsonschema + + +path_part = 'schemas/qobj_schema.json' +path = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + path_part) +with open(path) as fd: + json_schema = json.loads(fd.read()) +validator = fastjsonschema.compile(json_schema) + + +class QobjDictField(SimpleNamespace): + """A class used to represent a dictionary field in Qobj + + Exists as a backwards compatibility shim around a dictionary for Qobjs + previously constructed using marshmallow. + """ + + def __init__(self, **kwargs): + """Instantiate a new Qobj dict field object. + + Args: + kwargs: arbitrary keyword arguments that can be accessed as + attributes of the object. + """ + self.__dict__.update(kwargs) + + def to_dict(self): + """Return a dictionary format representation of the QASM Qobj. + + Returns: + dict: The dictionary form of the QobjHeader. + """ + return self.__dict__ + + @classmethod + def from_dict(cls, data): + """Create a new QobjHeader object from a dictionary. + + Args: + data (dict): A dictionary representing the QobjHeader to create. It + will be in the same format as output by :func:`to_dict`. + + Returns: + QobjDictFieldr: The QobjDictField from the input dictionary. + """ + + return cls(**data) + + def __eq__(self, other): + if isinstance(other, self.__class__): + if self.__dict__ == other.__dict__: + return True + return False + + +class QobjHeader(QobjDictField): + """A class used to represent a dictionary header in Qobj objects.""" + pass + + +class QobjExperimentHeader(QobjHeader): + """A class representing a header dictionary for a Qobj Experiment.""" + pass diff --git a/qiskit/qobj/pulse_qobj.py b/qiskit/qobj/pulse_qobj.py index ddf040a8744f..7faf521461fe 100644 --- a/qiskit/qobj/pulse_qobj.py +++ b/qiskit/qobj/pulse_qobj.py @@ -22,10 +22,10 @@ import numpy -from qiskit.qobj.qasm_qobj import QobjDictField -from qiskit.qobj.qasm_qobj import QobjHeader -from qiskit.qobj.qasm_qobj import QobjExperimentHeader -from qiskit.qobj.qasm_qobj import validator +from qiskit.qobj.common import QobjDictField +from qiskit.qobj.common import QobjHeader +from qiskit.qobj.common import QobjExperimentHeader +from qiskit.qobj.common import validator class QobjMeasurementOption: diff --git a/qiskit/qobj/qasm_qobj.py b/qiskit/qobj/qasm_qobj.py index e80bca195635..3429f99c8261 100644 --- a/qiskit/qobj/qasm_qobj.py +++ b/qiskit/qobj/qasm_qobj.py @@ -10,27 +10,20 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# pylint: disable=invalid-name +# pylint: disable=method-hidden,arguments-differ """Module providing definitions of QASM Qobj classes.""" -import os +import copy import pprint +import json from types import SimpleNamespace -import json -import fastjsonschema +import numpy from qiskit.circuit.parameterexpression import ParameterExpression - - -path_part = 'schemas/qobj_schema.json' -path = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), - path_part) -with open(path) as fd: - json_schema = json.loads(fd.read()) -validator = fastjsonschema.compile(json_schema) +from qiskit.qobj.pulse_qobj import PulseQobjInstruction, PulseLibraryItem +from qiskit.qobj.common import QobjDictField, QobjHeader, validator class QasmQobjInstruction: @@ -248,7 +241,7 @@ class QasmQobjConfig(SimpleNamespace): def __init__(self, shots=None, max_credits=None, seed_simulator=None, memory=None, parameter_binds=None, memory_slots=None, - n_qubits=None, **kwargs): + n_qubits=None, pulse_library=None, calibrations=None, **kwargs): """Model for RunConfig. Args: @@ -259,6 +252,8 @@ def __init__(self, shots=None, max_credits=None, seed_simulator=None, parameter_binds (list[dict]): List of parameter bindings memory_slots (int): The number of memory slots on the device n_qubits (int): The number of qubits on the device + pulse_library (list): List of :class:`PulseLibraryItem`. + calibrations (QasmExperimentCalibrations): Information required for Pulse gates. kwargs: Additional free form key value fields to add to the configuration. """ @@ -283,6 +278,12 @@ def __init__(self, shots=None, max_credits=None, seed_simulator=None, if n_qubits is not None: self.n_qubits = n_qubits + if pulse_library is not None: + self.pulse_library = pulse_library + + if calibrations is not None: + self.calibrations = calibrations + if kwargs: self.__dict__.update(kwargs) @@ -292,7 +293,14 @@ def to_dict(self): Returns: dict: The dictionary form of the QasmQobjConfig. """ - return self.__dict__ + out_dict = copy.copy(self.__dict__) + if hasattr(self, 'pulse_library'): + out_dict['pulse_library'] = [x.to_dict() for x in self.pulse_library] + + if hasattr(self, 'calibrations'): + out_dict['calibrations'] = self.calibrations.to_dict() + + return out_dict @classmethod def from_dict(cls, data): @@ -304,6 +312,15 @@ def from_dict(cls, data): Returns: QasmQobjConfig: The object from the input dictionary. """ + if 'pulse_library' in data: + pulse_lib = data.pop('pulse_library') + pulse_lib_obj = [PulseLibraryItem.from_dict(x) for x in pulse_lib] + data['pulse_library'] = pulse_lib_obj + + if 'calibrations' in data: + calibrations = data.pop('calibrations') + data['calibrations'] = QasmExperimentCalibrations.from_dict(calibrations) + return cls(**data) def __eq__(self, other): @@ -313,70 +330,124 @@ def __eq__(self, other): return False -class QobjDictField(SimpleNamespace): - """A class used to represent a dictionary field in Qobj +class QasmQobjExperimentHeader(QobjDictField): + """A header for a single QASM experiment in the qobj.""" + pass + - Exists as a backwards compatibility shim around a dictionary for Qobjs - previously constructed using marshmallow. +class QasmQobjExperimentConfig(QobjDictField): + """Configuration for a single QASM experiment in the qobj.""" + + def __init__(self, calibrations=None, **kwargs): + """ + Args: + calibrations (QasmExperimentCalibrations): Information required for Pulse gates. + kwargs: Additional free form key value fields to add to the + configuration. + """ + if calibrations: + self.calibrations = calibrations + super().__init__(**kwargs) + + def to_dict(self): + out_dict = copy.copy(self.__dict__) + if hasattr(self, 'calibrations'): + out_dict['calibrations'] = self.calibrations.to_dict() + return out_dict + + @classmethod + def from_dict(cls, data): + if 'calibrations' in data: + calibrations = data.pop('calibrations') + data['calibrations'] = QasmExperimentCalibrations.from_dict(calibrations) + return cls(**data) + + +class QasmExperimentCalibrations: + """A container for any calibrations data. The gates attribute contains a list of + GateCalibrations. """ - def __init__(self, **kwargs): - """Instantiate a new Qobj dict field object. + def __init__(self, gates): + """ + Initialize a container for calibrations. Args: - kwargs: arbitrary keyword arguments that can be accessed as - attributes of the object. + gates (list(GateCalibration)) """ - self.__dict__.update(kwargs) + self.gates = gates def to_dict(self): - """Return a dictionary format representation of the QASM Qobj. + """Return a dictionary format representation of the calibrations. Returns: - dict: The dictionary form of the QobjHeader. + dict: The dictionary form of the GateCalibration. """ - return self.__dict__ + out_dict = copy.copy(self.__dict__) + out_dict['gates'] = [x.to_dict() for x in self.gates] + return out_dict @classmethod def from_dict(cls, data): - """Create a new QobjHeader object from a dictionary. + """Create a new GateCalibration object from a dictionary. Args: - data (dict): A dictionary representing the QobjHeader to create. It - will be in the same format as output by :func:`to_dict`. + data (dict): A dictionary representing the QasmExperimentCalibrations to + create. It will be in the same format as output by :func:`to_dict`. Returns: - QobjDictFieldr: The QobjDictField from the input dictionary. + QasmExperimentCalibrations: The QasmExperimentCalibrations from the input dictionary. """ - + gates = data.pop('gates') + data['gates'] = [GateCalibration.from_dict(x) for x in gates] return cls(**data) - def __eq__(self, other): - if isinstance(other, self.__class__): - if self.__dict__ == other.__dict__: - return True - return False +class GateCalibration: + """Each calibration specifies a unique gate by name, qubits and params, and + contains the Pulse instructions to implement it.""" -class QasmQobjExperimentHeader(QobjDictField): - """A header for a single QASM experiment in the qobj.""" - pass + def __init__(self, name, qubits, params, instructions): + """ + Initialize a single gate calibration. Instructions may reference waveforms which should be + made available in the pulse_library. + Args: + name (str): Gate name. + qubits (list(int)): Qubits the gate applies to. + params (list(complex)): Gate parameter values, if any. + instructions (list(PulseQobjInstruction)): The gate implementation. + """ + self.name = name + self.qubits = qubits + self.params = params + self.instructions = instructions -class QasmQobjExperimentConfig(QobjDictField): - """Configuration for a single QASM experiment in the qobj.""" - pass + def to_dict(self): + """Return a dictionary format representation of the Gate Calibration. + Returns: + dict: The dictionary form of the GateCalibration. + """ + out_dict = copy.copy(self.__dict__) + out_dict['instructions'] = [x.to_dict() for x in self.instructions] + return out_dict -class QobjHeader(QobjDictField): - """A class used to represent a dictionary header in Qobj objects.""" - pass + @classmethod + def from_dict(cls, data): + """Create a new GateCalibration object from a dictionary. + Args: + data (dict): A dictionary representing the GateCalibration to create. It + will be in the same format as output by :func:`to_dict`. -class QobjExperimentHeader(QobjHeader): - """A class representing a header dictionary for a Qobj Experiment.""" - pass + Returns: + GateCalibration: The GateCalibration from the input dictionary. + """ + instructions = data.pop('instructions') + data['instructions'] = [PulseQobjInstruction.from_dict(x) for x in instructions] + return cls(**data) class QasmQobj: @@ -403,7 +474,20 @@ def __init__(self, qobj_id=None, config=None, experiments=None, self.experiments = experiments or [] self.qobj_id = qobj_id self.type = 'QASM' - self.schema_version = '1.2.0' + self.schema_version = '1.3.0' + + def _validate_json_schema(self, out_dict): + class QobjEncoder(json.JSONEncoder): + """A json encoder for qobj""" + def default(self, obj): + if isinstance(obj, numpy.ndarray): + return obj.tolist() + if isinstance(obj, complex): + return (obj.real, obj.imag) + return json.JSONEncoder.default(self, obj) + + json_str = json.dumps(out_dict, cls=QobjEncoder) + validator(json.loads(json_str)) def __repr__(self): experiments_str = [repr(x) for x in self.experiments] @@ -460,12 +544,12 @@ def default(self, obj): 'qobj_id': self.qobj_id, 'header': self.header.to_dict(), 'config': self.config.to_dict(), - 'schema_version': '1.2.0', + 'schema_version': self.schema_version, 'type': 'QASM', 'experiments': [x.to_dict() for x in self.experiments] } if validate: - validator(out_dict) + self._validate_json_schema(out_dict) return out_dict @classmethod diff --git a/qiskit/schemas/examples/qasm_w_pulse_gates.json b/qiskit/schemas/examples/qasm_w_pulse_gates.json new file mode 100644 index 000000000000..0efe80ae1a75 --- /dev/null +++ b/qiskit/schemas/examples/qasm_w_pulse_gates.json @@ -0,0 +1,797 @@ +{ + "qobj_id": "762bf8dd-8ec1-4241-b606-8249b38a922f", + "header": { + "backend_name": "fake_almaden", + "backend_version": "1.4.6" + }, + "config": { + "shots": 1024, + "memory": false, + "parameter_binds": [], + "init_qubits": true, + "parametric_pulses": [ + "gaussian" + ], + "memory_slots": 3, + "n_qubits": 20, + "pulse_library": [ + { + "name": "1c5a0346ff72c04c45d536d50d13b0d42cf65dc8e320d32385d36861d3bb1e41", + "samples": [ + [ + 0.000293973741723399, + 0 + ], + [ + 0.0006061156705983722, + 0 + ], + [ + 0.0010529363460618408, + 0 + ], + [ + 0.0016816340894609918, + 0 + ], + [ + 0.0025510324687094362, + 0 + ], + [ + 0.0037324323669642883, + 0 + ], + [ + 0.005309679452559498, + 0 + ], + [ + 0.007378103423579345, + 0 + ], + [ + 0.01004198697717997, + 0 + ], + [ + 0.013410274492023352, + 0 + ], + [ + 0.017590344314623686, + 0 + ], + [ + 0.02267984896088997, + 0 + ], + [ + 0.02875686896878555, + 0 + ], + [ + 0.03586891039602005, + 0 + ], + [ + 0.04402157155522451, + 0 + ], + [ + 0.05316796861270546, + 0 + ], + [ + 0.06320019267589688, + 0 + ], + [ + 0.07394412447755479, + 0 + ], + [ + 0.08515881854928489, + 0 + ], + [ + 0.09654136844205609, + 0 + ], + [ + 0.10773768702210337, + 0 + ], + [ + 0.1183590210887793, + 0 + ], + [ + 0.12800333669616534, + 0 + ], + [ + 0.13628005118799236, + 0 + ], + [ + 0.1428360489048848, + 0 + ], + [ + 0.1473805904084687, + 0 + ], + [ + 0.14970667552142972, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.15, + 0 + ], + [ + 0.14970667552142972, + 0 + ], + [ + 0.1473805904084687, + 0 + ], + [ + 0.1428360489048848, + 0 + ], + [ + 0.13628005118799236, + 0 + ], + [ + 0.12800333669616534, + 0 + ], + [ + 0.1183590210887793, + 0 + ], + [ + 0.10773768702210337, + 0 + ], + [ + 0.09654136844205609, + 0 + ], + [ + 0.08515881854928489, + 0 + ], + [ + 0.07394412447755479, + 0 + ], + [ + 0.06320019267589688, + 0 + ], + [ + 0.05316796861270546, + 0 + ], + [ + 0.04402157155522451, + 0 + ], + [ + 0.03586891039602005, + 0 + ], + [ + 0.02875686896878555, + 0 + ], + [ + 0.02267984896088997, + 0 + ], + [ + 0.017590344314623686, + 0 + ], + [ + 0.013410274492023352, + 0 + ], + [ + 0.01004198697717997, + 0 + ], + [ + 0.007378103423579345, + 0 + ], + [ + 0.005309679452559498, + 0 + ], + [ + 0.0037324323669642883, + 0 + ], + [ + 0.0025510324687094362, + 0 + ], + [ + 0.0016816340894609918, + 0 + ], + [ + 0.0010529363460618408, + 0 + ], + [ + 0.0006061156705983722, + 0 + ], + [ + 0.000293973741723399, + 0 + ] + ] + } + ], + "calibrations": { + "gates": [ + { + "name": "rxt", + "qubits": [ + 0 + ], + "params": [ + 3.14 + ], + "instructions": [ + { + "name": "parametric_pulse", + "t0": 0, + "ch": "d0", + "pulse_shape": "gaussian", + "parameters": { + "duration": 128, + "amp": [ + 0.2, + 0 + ], + "sigma": 16 + } + }, + { + "name": "parametric_pulse", + "t0": 128, + "ch": "d0", + "pulse_shape": "gaussian", + "parameters": { + "duration": 128, + "amp": [ + 0.2, + 0 + ], + "sigma": 16 + } + } + ] + }, + { + "name": "rxt", + "qubits": [ + 1 + ], + "params": [ + 3.14 + ], + "instructions": [ + { + "name": "1c5a0346ff72c04c45d536d50d13b0d42cf65dc8e320d32385d36861d3bb1e41", + "t0": 0, + "ch": "d1" + } + ] + }, + { + "name": "rxt", + "qubits": [ + 1 + ], + "params": [ + 1.57 + ], + "instructions": [ + { + "name": "1c5a0346ff72c04c45d536d50d13b0d42cf65dc8e320d32385d36861d3bb1e41", + "t0": 0, + "ch": "d1" + } + ] + } + ] + } + }, + "schema_version": "1.3.0", + "type": "QASM", + "experiments": [ + { + "config": { + "n_qubits": 20, + "memory_slots": 3, + "calibrations": { + "gates": [ + { + "name": "h", + "qubits": [ + 0 + ], + "params": [], + "instructions": [ + { + "name": "fc", + "t0": 0, + "ch": "d0", + "phase": 1.57 + }, + { + "name": "parametric_pulse", + "t0": 0, + "ch": "d0", + "pulse_shape": "gaussian", + "parameters": { + "duration": 128, + "amp": [ + 0.2, + 0 + ], + "sigma": 16 + } + } + ] + } + ] + } + }, + "header": { + "qubit_labels": [ + [ + "q", + 0 + ], + [ + "q", + 1 + ], + [ + "q", + 2 + ], + [ + "q", + 3 + ], + [ + "q", + 4 + ], + [ + "q", + 5 + ], + [ + "q", + 6 + ], + [ + "q", + 7 + ], + [ + "q", + 8 + ], + [ + "q", + 9 + ], + [ + "q", + 10 + ], + [ + "q", + 11 + ], + [ + "q", + 12 + ], + [ + "q", + 13 + ], + [ + "q", + 14 + ], + [ + "q", + 15 + ], + [ + "q", + 16 + ], + [ + "q", + 17 + ], + [ + "q", + 18 + ], + [ + "q", + 19 + ] + ], + "n_qubits": 20, + "qreg_sizes": [ + [ + "q", + 20 + ] + ], + "clbit_labels": [ + [ + "c", + 0 + ], + [ + "c", + 1 + ], + [ + "c", + 2 + ] + ], + "memory_slots": 3, + "creg_sizes": [ + [ + "c", + 3 + ] + ], + "name": "circuit7", + "global_phase": 0 + }, + "instructions": [ + { + "name": "h", + "qubits": [ + 0 + ] + }, + { + "name": "u3", + "params": [ + 3.141592653589793, + 0, + 3.141592653589793 + ], + "qubits": [ + 1 + ] + }, + { + "name": "cx", + "qubits": [ + 0, + 1 + ] + }, + { + "name": "rxt", + "params": [ + 3.14 + ], + "qubits": [ + 0 + ] + }, + { + "name": "rxt", + "params": [ + 1.57 + ], + "qubits": [ + 1 + ] + }, + { + "name": "rxt", + "params": [ + 3.14 + ], + "qubits": [ + 1 + ] + }, + { + "name": "rxt", + "params": [ + 1.57 + ], + "qubits": [ + 1 + ] + }, + { + "name": "measure", + "qubits": [ + 0 + ], + "memory": [ + 0 + ] + }, + { + "name": "measure", + "qubits": [ + 1 + ], + "memory": [ + 1 + ] + } + ] + }, + { + "config": { + "n_qubits": 20, + "memory_slots": 3 + }, + "header": { + "qubit_labels": [ + [ + "q", + 0 + ], + [ + "q", + 1 + ], + [ + "q", + 2 + ], + [ + "q", + 3 + ], + [ + "q", + 4 + ], + [ + "q", + 5 + ], + [ + "q", + 6 + ], + [ + "q", + 7 + ], + [ + "q", + 8 + ], + [ + "q", + 9 + ], + [ + "q", + 10 + ], + [ + "q", + 11 + ], + [ + "q", + 12 + ], + [ + "q", + 13 + ], + [ + "q", + 14 + ], + [ + "q", + 15 + ], + [ + "q", + 16 + ], + [ + "q", + 17 + ], + [ + "q", + 18 + ], + [ + "q", + 19 + ] + ], + "n_qubits": 20, + "qreg_sizes": [ + [ + "q", + 20 + ] + ], + "clbit_labels": [ + [ + "c", + 0 + ], + [ + "c", + 1 + ], + [ + "c", + 2 + ] + ], + "memory_slots": 3, + "creg_sizes": [ + [ + "c", + 3 + ] + ], + "name": "circuit8", + "global_phase": 0 + }, + "instructions": [ + { + "name": "u2", + "params": [ + 0, + 3.141592653589793 + ], + "qubits": [ + 0 + ] + }, + { + "name": "u3", + "params": [ + 3.141592653589793, + 0, + 3.141592653589793 + ], + "qubits": [ + 1 + ] + }, + { + "name": "cx", + "qubits": [ + 0, + 1 + ] + }, + { + "name": "rxt", + "params": [ + 3.14 + ], + "qubits": [ + 0 + ] + }, + { + "name": "rxt", + "params": [ + 1.57 + ], + "qubits": [ + 1 + ] + }, + { + "name": "rxt", + "params": [ + 3.14 + ], + "qubits": [ + 1 + ] + }, + { + "name": "rxt", + "params": [ + 1.57 + ], + "qubits": [ + 1 + ] + }, + { + "name": "measure", + "qubits": [ + 0 + ], + "memory": [ + 0 + ] + }, + { + "name": "measure", + "qubits": [ + 1 + ], + "memory": [ + 1 + ] + } + ] + } + ] +} diff --git a/qiskit/schemas/qobj_schema.json b/qiskit/schemas/qobj_schema.json index eeb0f04cd8ea..df5e6d2fa717 100644 --- a/qiskit/schemas/qobj_schema.json +++ b/qiskit/schemas/qobj_schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-04/schema#", "id": "http://www.qiskit.org/schemas/qobj_schema.json", "description": "OpenQuantum quantum object data structure for running experiments", - "version": "1.2.0", + "version": "1.3.0", "definitions": { "bfunc": { "properties": { @@ -847,6 +847,41 @@ }, "type": "array" }, + "calibration_entry": { + "description": "The instruction description of each calibrated gate.", + "type": "object", + "properties": { + "name": { + "description": "Gate name", + "type": "string" + }, + "qubits": { + "items": { + "minimum": 0, + "type": "integer" + }, + "minItems": 1, + "type": "array" + }, + "params": { + "description": "Gate parameters", + "type": "array" + }, + "instructions": { + "description": "Pulse instructions implementing the given gate.", + "type": "array", + "items": { + "$ref": "#/definitions/qobjinstructions" + } + } + }, + "required": [ + "name", + "qubits", + "params", + "instructions" + ] + }, "qobjexp": { "description": "Quantum experiment", "properties": { @@ -866,6 +901,19 @@ "init_qubits": { "description": "Whether to initialize the qubits in the ground state before each shot.", "type": "boolean" + }, + "calibrations": { + "description": "User-specified pulse-level calibrations for operations utilized in this experiment.", + "type": "object", + "properties": { + "gates": { + "description": "A list of dicts which contain all the required information to lower a gate to waveforms.", + "type": "array", + "items": { + "$ref": "#/definitions/calibration_entry" + } + } + } } }, "title": "Experiment level configuration", @@ -1064,6 +1112,22 @@ "$ref": "#/definitions/discriminator" } } + }, + "pulse_library": { + "$ref": "#/definitions/pulse_library" + }, + "calibrations": { + "description": "User-specified pulse-level calibrations for operations utilized across all experiments in this job.", + "type": "object", + "properties": { + "gates": { + "description": "A list of dicts which contain all the required information to lower a gate to waveforms.", + "type": "array", + "items": { + "$ref": "#/definitions/calibration_entry" + } + } + } } }, "title": "Qobj-level configuration", diff --git a/test/python/qobj/test_qobj.py b/test/python/qobj/test_qobj.py index b45c9e9b0a25..ef71665ba08b 100644 --- a/test/python/qobj/test_qobj.py +++ b/test/python/qobj/test_qobj.py @@ -26,7 +26,8 @@ PulseQobjInstruction, PulseQobjExperiment, PulseQobjConfig, QobjMeasurementOption, PulseLibraryItem, QasmQobjInstruction, - QasmQobjExperiment, QasmQobjConfig) + QasmQobjExperiment, QasmQobjConfig, + QasmExperimentCalibrations, GateCalibration) from qiskit.qobj import validate_qobj_against_schema from qiskit.validation.jsonschema.exceptions import SchemaValidationError @@ -120,7 +121,7 @@ def test_snapshot_instruction_to_dict(self): expected_dict = { 'qobj_id': '12345', 'type': 'QASM', - 'schema_version': '1.2.0', + 'schema_version': '1.3.0', 'header': {}, 'config': {'max_credits': 10, 'memory_slots': 2, 'shots': 1024}, 'experiments': [ @@ -200,6 +201,52 @@ def test_change_qobj_after_compile(self): self.assertTrue(qobj1.experiments[1].config.shots == 1) self.assertTrue(qobj1.config.shots == 1024) + def test_gate_calibrations_to_dict(self): + """Test gate calibrations to dict.""" + + pulse_library = [PulseLibraryItem(name='test', samples=[1j, 1j])] + valid_qobj = QasmQobj( + qobj_id='12345', + header=QobjHeader(), + config=QasmQobjConfig(shots=1024, memory_slots=2, max_credits=10, + pulse_library=pulse_library), + experiments=[ + QasmQobjExperiment( + instructions=[ + QasmQobjInstruction(name='u1', qubits=[1], params=[0.4]) + ], + config=QasmQobjConfig( + calibrations=QasmExperimentCalibrations( + gates=[ + GateCalibration(name='u1', qubits=[1], + params=[0.4], instructions=[]) + ] + ) + ) + ) + ] + ) + res = valid_qobj.to_dict(validate=True) + expected_dict = { + 'qobj_id': '12345', + 'type': 'QASM', + 'schema_version': '1.3.0', + 'header': {}, + 'config': {'max_credits': 10, 'memory_slots': 2, 'shots': 1024, + 'pulse_library': [{'name': 'test', 'samples': [1j, 1j]}]}, + 'experiments': [ + {'instructions': [ + {'name': 'u1', 'params': [0.4], 'qubits': [1]} + ], + 'config': { + 'calibrations': { + 'gates': [{'name': 'u1', 'qubits': [1], + 'params': [0.4], 'instructions': []}]}}, + 'header': {}} + ], + } + self.assertEqual(expected_dict, res) + class TestPulseQobj(QiskitTestCase): """Tests for PulseQobj.""" diff --git a/test/python/test_schemas.py b/test/python/test_schemas.py index 38d0858018ec..7763d818942a 100644 --- a/test/python/test_schemas.py +++ b/test/python/test_schemas.py @@ -45,7 +45,8 @@ class TestSchemaExamples(QiskitTestCase): "job_status_example.json"], "qobj": [ "qobj_openpulse_example.json", - "qobj_openqasm_example.json"], + "qobj_openqasm_example.json", + "qasm_w_pulse_gates.json"], "result": [ "result_openqasm_example.json", "result_openpulse_level_0_example.json",