diff --git a/qiskit_ibm_runtime/qpy/__init__.py b/qiskit_ibm_runtime/qpy/__init__.py deleted file mode 100644 index 14c75d674a..0000000000 --- a/qiskit_ibm_runtime/qpy/__init__.py +++ /dev/null @@ -1,87 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# 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. - -""" -########################################################### -QPY serialization (:mod:`qiskit_ibm_runtime.qpy`) -########################################################### - -.. currentmodule:: qiskit_ibm_runtime.qpy - -********* -Using QPY -********* - -This is downstream fork of the :mod:`qiskit.qpy` module. - -Using QPY is defined to be straightforward and mirror the user API of the -serializers in Python's standard library, ``pickle`` and ``json``. There are -2 user facing functions: :func:`qiskit.circuit.qpy_serialization.dump` and -:func:`qiskit.circuit.qpy_serialization.load` which are used to dump QPY data -to a file object and load circuits from QPY data in a file object respectively. -For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit import qpy - - qc = QuantumCircuit(2, name='Bell', metadata={'test': True}) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - - with open('bell.qpy', 'wb') as fd: - qpy.dump(qc, fd) - - with open('bell.qpy', 'rb') as fd: - new_qc = qpy.load(fd)[0] - -API documentation -================= - -.. autosummary:: - :toctree: ../stubs/ - - load - dump - -QPY Compatibility -================= - -The QPY format is designed to be backwards compatible moving forward. This means -you should be able to load a QPY with any newer Qiskit version than the one -that generated it. However, loading a QPY file with an older Qiskit version is -not supported and may not work. - -For example, if you generated a QPY file using qiskit-terra 0.18.1 you could -load that QPY file with qiskit-terra 0.19.0 and a hypothetical qiskit-terra -0.29.0. However, loading that QPY file with 0.18.0 is not supported and may not -work. - -********** -QPY Format -********** - -https://qiskit.org/documentation/apidoc/qpy.html#qpy-format -""" - -from .interface import dump, load - -# For backward compatibility. Provide, Runtime, Experiment call these private functions. -from .binary_io import ( - _write_instruction, - _read_instruction, - _write_parameter_expression, - _read_parameter_expression, - _read_parameter_expression_v3, - _write_parameter, - _read_parameter, -) diff --git a/qiskit_ibm_runtime/qpy/binary_io/__init__.py b/qiskit_ibm_runtime/qpy/binary_io/__init__.py deleted file mode 100644 index 566ebbae53..0000000000 --- a/qiskit_ibm_runtime/qpy/binary_io/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# 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. - -"""Read and write QPY-serializable objects.""" - -from .value import ( - dumps_value, - loads_value, - write_value, - read_value, - # for backward compatibility; provider, runtime, experiment call this private methods. - _write_parameter, - _write_parameter_expression, - _read_parameter_expression, - _read_parameter_expression_v3, - _read_parameter, -) - -from .circuits import ( - write_circuit, - read_circuit, - # for backward compatibility; provider calls this private methods. - _write_instruction, - _read_instruction, -) -from .schedules import ( - write_schedule_block, - read_schedule_block, -) diff --git a/qiskit_ibm_runtime/qpy/binary_io/circuits.py b/qiskit_ibm_runtime/qpy/binary_io/circuits.py deleted file mode 100644 index f51f48b7da..0000000000 --- a/qiskit_ibm_runtime/qpy/binary_io/circuits.py +++ /dev/null @@ -1,1280 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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 - -"""Binary IO for circuit objects.""" - -import io -import json -import struct -import uuid -import warnings - -from collections import defaultdict -import numpy as np - -from qiskit import circuit as circuit_mod -from qiskit.circuit import library, controlflow, CircuitInstruction, ControlFlowOp -from qiskit.circuit.classical import expr -from qiskit.circuit.classicalregister import ClassicalRegister, Clbit -from qiskit.circuit.gate import Gate -from qiskit.circuit.singleton import SingletonInstruction, SingletonGate -from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.instruction import Instruction -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.circuit.quantumregister import QuantumRegister, Qubit -from qiskit.quantum_info.operators import SparsePauliOp -from qiskit.synthesis import evolution as evo_synth -from qiskit.transpiler.layout import Layout, TranspileLayout -from .. import common, formats, type_keys -from . import value, schedules - - -def _read_header_v2( # type: ignore[no-untyped-def] - file_obj, version, vectors, metadata_deserializer=None -): - data = formats.CIRCUIT_HEADER_V2._make( - struct.unpack( - formats.CIRCUIT_HEADER_V2_PACK, - file_obj.read(formats.CIRCUIT_HEADER_V2_SIZE), - ) - ) - name = file_obj.read(data.name_size).decode(common.ENCODE) - global_phase = value.loads_value( - data.global_phase_type, - file_obj.read(data.global_phase_size), - version=version, - vectors=vectors, - ) - header = { - "global_phase": global_phase, - "num_qubits": data.num_qubits, - "num_clbits": data.num_clbits, - "num_registers": data.num_registers, - "num_instructions": data.num_instructions, - } - metadata_raw = file_obj.read(data.metadata_size) - metadata = json.loads(metadata_raw, cls=metadata_deserializer) - return header, name, metadata - - -def _read_header(file_obj, metadata_deserializer=None): # type: ignore[no-untyped-def] - data = formats.CIRCUIT_HEADER._make( - struct.unpack(formats.CIRCUIT_HEADER_PACK, file_obj.read(formats.CIRCUIT_HEADER_SIZE)) - ) - name = file_obj.read(data.name_size).decode(common.ENCODE) - header = { - "global_phase": data.global_phase, - "num_qubits": data.num_qubits, - "num_clbits": data.num_clbits, - "num_registers": data.num_registers, - "num_instructions": data.num_instructions, - } - metadata_raw = file_obj.read(data.metadata_size) - metadata = json.loads(metadata_raw, cls=metadata_deserializer) - return header, name, metadata - - -def _read_registers_v4(file_obj, num_registers): # type: ignore[no-untyped-def] - registers = {"q": {}, "c": {}} - for _reg in range(num_registers): - data = formats.REGISTER_V4._make( - struct.unpack( - formats.REGISTER_V4_PACK, - file_obj.read(formats.REGISTER_V4_SIZE), - ) - ) - name = file_obj.read(data.name_size).decode("utf8") - REGISTER_ARRAY_PACK = "!%sq" % data.size - bit_indices_raw = file_obj.read(struct.calcsize(REGISTER_ARRAY_PACK)) - bit_indices = list(struct.unpack(REGISTER_ARRAY_PACK, bit_indices_raw)) - if data.type.decode("utf8") == "q": - registers["q"][name] = (data.standalone, bit_indices, data.in_circuit) - else: - registers["c"][name] = (data.standalone, bit_indices, data.in_circuit) - return registers - - -def _read_registers(file_obj, num_registers): # type: ignore[no-untyped-def] - registers = {"q": {}, "c": {}} - for _reg in range(num_registers): - data = formats.REGISTER._make( - struct.unpack( - formats.REGISTER_PACK, - file_obj.read(formats.REGISTER_SIZE), - ) - ) - name = file_obj.read(data.name_size).decode("utf8") - REGISTER_ARRAY_PACK = "!%sI" % data.size - bit_indices_raw = file_obj.read(struct.calcsize(REGISTER_ARRAY_PACK)) - bit_indices = list(struct.unpack(REGISTER_ARRAY_PACK, bit_indices_raw)) - if data.type.decode("utf8") == "q": - registers["q"][name] = (data.standalone, bit_indices, True) - else: - registers["c"][name] = (data.standalone, bit_indices, True) - return registers - - -def _loads_instruction_parameter( # type: ignore[no-untyped-def] - type_key, data_bytes, version, vectors, registers, circuit, use_symengine -): - if type_key == type_keys.Program.CIRCUIT: - param = common.data_from_binary(data_bytes, read_circuit, version=version) - elif type_key == type_keys.Container.RANGE: - data = formats.RANGE._make(struct.unpack(formats.RANGE_PACK, data_bytes)) - param = range(data.start, data.stop, data.step) - elif type_key == type_keys.Container.TUPLE: - param = tuple( - common.sequence_from_binary( - data_bytes, - _loads_instruction_parameter, - version=version, - vectors=vectors, - registers=registers, - circuit=circuit, - use_symengine=use_symengine, - ) - ) - elif type_key == type_keys.Value.INTEGER: - # TODO This uses little endian. Should be fixed in the next QPY version. - param = struct.unpack("= 5 and instruction.conditional_key == type_keys.Condition.TWO_TUPLE - ): - condition = ( - _loads_register_param(condition_register, circuit, registers), - instruction.condition_value, - ) - elif version >= 5 and instruction.conditional_key == type_keys.Condition.EXPRESSION: - condition = value.read_value( - file_obj, - version, - vectors, - clbits=circuit.clbits, - cregs=registers["c"], - use_symengine=use_symengine, - ) - if circuit is not None: - qubit_indices = dict(enumerate(circuit.qubits)) - clbit_indices = dict(enumerate(circuit.clbits)) - else: - qubit_indices = {} - clbit_indices = {} - - # Load Arguments - if circuit is not None: - for _qarg in range(instruction.num_qargs): - qarg = formats.CIRCUIT_INSTRUCTION_ARG._make( - struct.unpack( - formats.CIRCUIT_INSTRUCTION_ARG_PACK, - file_obj.read(formats.CIRCUIT_INSTRUCTION_ARG_SIZE), - ) - ) - if qarg.type.decode(common.ENCODE) == "c": - raise TypeError("Invalid input carg prior to all qargs") - qargs.append(qubit_indices[qarg.size]) - for _carg in range(instruction.num_cargs): - carg = formats.CIRCUIT_INSTRUCTION_ARG._make( - struct.unpack( - formats.CIRCUIT_INSTRUCTION_ARG_PACK, - file_obj.read(formats.CIRCUIT_INSTRUCTION_ARG_SIZE), - ) - ) - if carg.type.decode(common.ENCODE) == "q": - raise TypeError("Invalid input qarg after all qargs") - cargs.append(clbit_indices[carg.size]) - - # Load Parameters - for _param in range(instruction.num_parameters): - type_key, data_bytes = common.read_generic_typed_data(file_obj) - param = _loads_instruction_parameter( - type_key, - data_bytes, - version, - vectors, - registers, - circuit, - use_symengine, - ) - params.append(param) - - # Load Gate object - if gate_name in {"Gate", "Instruction", "ControlledGate"}: - inst_obj = _parse_custom_operation( - custom_operations, - gate_name, - params, - version, - vectors, - registers, - use_symengine, - ) - inst_obj.condition = condition - if instruction.label_size > 0: - inst_obj.label = label - if circuit is None: - return inst_obj - circuit._append(inst_obj, qargs, cargs) - return None - elif gate_name in custom_operations: - inst_obj = _parse_custom_operation( - custom_operations, - gate_name, - params, - version, - vectors, - registers, - use_symengine, - ) - inst_obj.condition = condition - if instruction.label_size > 0: - inst_obj.label = label - if circuit is None: - return inst_obj - circuit._append(inst_obj, qargs, cargs) - return None - elif hasattr(library, gate_name): - gate_class = getattr(library, gate_name) - elif hasattr(circuit_mod, gate_name): - gate_class = getattr(circuit_mod, gate_name) - elif hasattr(controlflow, gate_name): - gate_class = getattr(controlflow, gate_name) - else: - raise AttributeError("Invalid instruction type: %s" % gate_name) - - if instruction.label_size <= 0: - label = None - if gate_name in {"IfElseOp", "WhileLoopOp"}: - gate = gate_class(condition, *params, label=label) - elif version >= 5 and issubclass(gate_class, ControlledGate): - if gate_name in { - "MCPhaseGate", - "MCU1Gate", - "MCXGrayCode", - "MCXGate", - "MCXRecursive", - "MCXVChain", - }: - gate = gate_class(*params, instruction.num_ctrl_qubits, label=label) - else: - gate = gate_class(*params, label=label) - if ( - gate.num_ctrl_qubits != instruction.num_ctrl_qubits - or gate.ctrl_state != instruction.ctrl_state - ): - gate = gate.to_mutable() - gate.num_ctrl_qubits = instruction.num_ctrl_qubits - gate.ctrl_state = instruction.ctrl_state - if condition: - gate = gate.c_if(*condition) - else: - if gate_name in {"Initialize", "StatePreparation"}: - if isinstance(params[0], str): - # the params are the labels of the initial state - gate = gate_class("".join(label for label in params)) - elif instruction.num_parameters == 1: - # the params is the integer indicating which qubits to initialize - gate = gate_class(int(params[0].real), instruction.num_qargs) - else: - # the params represent a list of complex amplitudes - gate = gate_class(params) - elif gate_name in { - "UCRXGate", - "UCRYGate", - "UCRZGate", - "DiagonalGate", - }: - gate = gate_class(params) - else: - if gate_name == "Barrier": - params = [len(qargs)] - elif gate_name in {"BreakLoopOp", "ContinueLoopOp"}: - params = [len(qargs), len(cargs)] - if label is not None: - if issubclass(gate_class, (SingletonInstruction, SingletonGate)): - gate = gate_class(*params, label=label) - else: - gate = gate_class(*params) - gate.label = label - else: - gate = gate_class(*params) - if condition: - if not isinstance(gate, ControlFlowOp): - gate = gate.c_if(*condition) - else: - gate.condition = condition - if circuit is None: - return gate - if not isinstance(gate, Instruction): - circuit.append(gate, qargs, cargs) - else: - circuit._append(CircuitInstruction(gate, qargs, cargs)) - return None - - -def _parse_custom_operation( # type: ignore[no-untyped-def] - custom_operations, gate_name, params, version, vectors, registers, use_symengine -): - if version >= 5: - ( - type_str, - num_qubits, - num_clbits, - definition, - num_ctrl_qubits, - ctrl_state, - base_gate_raw, - ) = custom_operations[gate_name] - else: - type_str, num_qubits, num_clbits, definition = custom_operations[gate_name] - type_key = type_keys.CircuitInstruction(type_str) - - if type_key == type_keys.CircuitInstruction.INSTRUCTION: - inst_obj = Instruction(gate_name, num_qubits, num_clbits, params) - if definition is not None: - inst_obj.definition = definition - return inst_obj - - if type_key == type_keys.CircuitInstruction.GATE: - inst_obj = Gate(gate_name, num_qubits, params) - inst_obj.definition = definition - return inst_obj - - if version >= 5 and type_key == type_keys.CircuitInstruction.CONTROLLED_GATE: - with io.BytesIO(base_gate_raw) as base_gate_obj: - base_gate = _read_instruction( - base_gate_obj, - None, - registers, - custom_operations, - version, - vectors, - use_symengine, - ) - if ctrl_state < 2**num_ctrl_qubits - 1: - # If open controls, we need to discard the control suffix when setting the name. - gate_name = gate_name.rsplit("_", 1)[0] - inst_obj = ControlledGate( - gate_name, - num_qubits, - params, - num_ctrl_qubits=num_ctrl_qubits, - ctrl_state=ctrl_state, - base_gate=base_gate, - ) - inst_obj.definition = definition - return inst_obj - - if type_key == type_keys.CircuitInstruction.PAULI_EVOL_GATE: - return definition - - raise ValueError("Invalid custom instruction type '%s'" % type_str) - - -def _read_pauli_evolution_gate(file_obj, version, vectors): # type: ignore[no-untyped-def] - pauli_evolution_def = formats.PAULI_EVOLUTION_DEF._make( - struct.unpack( - formats.PAULI_EVOLUTION_DEF_PACK, - file_obj.read(formats.PAULI_EVOLUTION_DEF_SIZE), - ) - ) - if pauli_evolution_def.operator_size != 1 and pauli_evolution_def.standalone_op: - raise ValueError( - "Can't have a standalone operator with {pauli_evolution_raw[0]} operators in the payload" - ) - - operator_list = [] - for _ in range(pauli_evolution_def.operator_size): - op_elem = formats.SPARSE_PAULI_OP_LIST_ELEM._make( - struct.unpack( - formats.SPARSE_PAULI_OP_LIST_ELEM_PACK, - file_obj.read(formats.SPARSE_PAULI_OP_LIST_ELEM_SIZE), - ) - ) - op_raw_data = common.data_from_binary(file_obj.read(op_elem.size), np.load) - operator_list.append(SparsePauliOp.from_list(op_raw_data)) - - if pauli_evolution_def.standalone_op: - pauli_op = operator_list[0] - else: - pauli_op = operator_list - - time = value.loads_value( - pauli_evolution_def.time_type, - file_obj.read(pauli_evolution_def.time_size), - version=version, - vectors=vectors, - ) - synth_data = json.loads(file_obj.read(pauli_evolution_def.synth_method_size)) - synthesis = getattr(evo_synth, synth_data["class"])(**synth_data["settings"]) - return_gate = library.PauliEvolutionGate(pauli_op, time=time, synthesis=synthesis) - return return_gate - - -def _read_custom_operations(file_obj, version, vectors): # type: ignore[no-untyped-def] - custom_operations = {} - custom_definition_header = formats.CUSTOM_CIRCUIT_DEF_HEADER._make( - struct.unpack( - formats.CUSTOM_CIRCUIT_DEF_HEADER_PACK, - file_obj.read(formats.CUSTOM_CIRCUIT_DEF_HEADER_SIZE), - ) - ) - if custom_definition_header.size > 0: - for _ in range(custom_definition_header.size): - if version < 5: - data = formats.CUSTOM_CIRCUIT_INST_DEF._make( - struct.unpack( - formats.CUSTOM_CIRCUIT_INST_DEF_PACK, - file_obj.read(formats.CUSTOM_CIRCUIT_INST_DEF_SIZE), - ) - ) - else: - data = formats.CUSTOM_CIRCUIT_INST_DEF_V2._make( - struct.unpack( - formats.CUSTOM_CIRCUIT_INST_DEF_V2_PACK, - file_obj.read(formats.CUSTOM_CIRCUIT_INST_DEF_V2_SIZE), - ) - ) - - name = file_obj.read(data.gate_name_size).decode(common.ENCODE) - type_str = data.type - definition_circuit = None - if data.custom_definition: - def_binary = file_obj.read(data.size) - if version < 3 or not name.startswith(r"###PauliEvolutionGate_"): - definition_circuit = common.data_from_binary( - def_binary, read_circuit, version=version - ) - elif name.startswith(r"###PauliEvolutionGate_"): - definition_circuit = common.data_from_binary( - def_binary, - _read_pauli_evolution_gate, - version=version, - vectors=vectors, - ) - if version < 5: - data_payload = ( - type_str, - data.num_qubits, - data.num_clbits, - definition_circuit, - ) - else: - base_gate = file_obj.read(data.base_gate_size) - data_payload = ( - type_str, - data.num_qubits, - data.num_clbits, - definition_circuit, - data.num_ctrl_qubits, - data.ctrl_state, - base_gate, - ) - custom_operations[name] = data_payload - return custom_operations - - -def _read_calibrations( # type: ignore[no-untyped-def] - file_obj, version, vectors, metadata_deserializer -): - calibrations = {} - - header = formats.CALIBRATION._make( - struct.unpack(formats.CALIBRATION_PACK, file_obj.read(formats.CALIBRATION_SIZE)) - ) - for _ in range(header.num_cals): - defheader = formats.CALIBRATION_DEF._make( - struct.unpack( - formats.CALIBRATION_DEF_PACK, - file_obj.read(formats.CALIBRATION_DEF_SIZE), - ) - ) - name = file_obj.read(defheader.name_size).decode(common.ENCODE) - qubits = tuple( - struct.unpack("!q", file_obj.read(struct.calcsize("!q")))[0] - for _ in range(defheader.num_qubits) - ) - params = tuple( - value.read_value(file_obj, version, vectors) for _ in range(defheader.num_params) - ) - schedule = schedules.read_schedule_block(file_obj, version, metadata_deserializer) - - if name not in calibrations: - calibrations[name] = {(qubits, params): schedule} - else: - calibrations[name][(qubits, params)] = schedule - - return calibrations - - -def _dumps_register(register, index_map): # type: ignore[no-untyped-def] - if isinstance(register, ClassicalRegister): - return register.name.encode(common.ENCODE) - # Clbit. - return b"\x00" + str(index_map["c"][register]).encode(common.ENCODE) - - -def _dumps_instruction_parameter(param, index_map, use_symengine): # type: ignore[no-untyped-def] - if isinstance(param, QuantumCircuit): - type_key = type_keys.Program.CIRCUIT - data_bytes = common.data_to_binary(param, write_circuit) - elif isinstance(param, range): - type_key = type_keys.Container.RANGE - data_bytes = struct.pack(formats.RANGE_PACK, param.start, param.stop, param.step) - elif isinstance(param, tuple): - type_key = type_keys.Container.TUPLE - data_bytes = common.sequence_to_binary( - param, - _dumps_instruction_parameter, - index_map=index_map, - use_symengine=use_symengine, - ) - elif isinstance(param, int): - # TODO This uses little endian. This should be fixed in next QPY version. - type_key = type_keys.Value.INTEGER - data_bytes = struct.pack("= 0: - circuit._layout._input_qubit_count = header.input_qubit_count - circuit._layout._output_qubit_list = circuit.qubits - - -def write_circuit( # type: ignore[no-untyped-def] - file_obj, circuit, metadata_serializer=None, use_symengine=False -): - """Write a single QuantumCircuit object in the file like object. - - Args: - file_obj (FILE): The file like object to write the circuit data in. - circuit (QuantumCircuit): The circuit data to write. - metadata_serializer (JSONEncoder): An optional JSONEncoder class that - will be passed the :attr:`.QuantumCircuit.metadata` dictionary for - ``circuit`` and will be used as the ``cls`` kwarg - on the ``json.dump()`` call to JSON serialize that dictionary. - use_symengine (bool): If True, symbolic objects will be serialized using symengine's - native mechanism. This is a faster serialization alternative, but not supported in all - platforms. Please check that your target platform is supported by the symengine library - before setting this option, as it will be required by qpy to deserialize the payload. - """ - metadata_raw = json.dumps( - circuit.metadata, separators=(",", ":"), cls=metadata_serializer - ).encode(common.ENCODE) - metadata_size = len(metadata_raw) - num_instructions = len(circuit) - circuit_name = circuit.name.encode(common.ENCODE) - global_phase_type, global_phase_data = value.dumps_value(circuit.global_phase) - - with io.BytesIO() as reg_buf: - num_qregs = _write_registers(reg_buf, circuit.qregs, circuit.qubits) - num_cregs = _write_registers(reg_buf, circuit.cregs, circuit.clbits) - registers_raw = reg_buf.getvalue() - num_registers = num_qregs + num_cregs - - # Write circuit header - header_raw = formats.CIRCUIT_HEADER_V2( - name_size=len(circuit_name), - global_phase_type=global_phase_type, - global_phase_size=len(global_phase_data), - num_qubits=circuit.num_qubits, - num_clbits=circuit.num_clbits, - metadata_size=metadata_size, - num_registers=num_registers, - num_instructions=num_instructions, - ) - header = struct.pack(formats.CIRCUIT_HEADER_V2_PACK, *header_raw) - file_obj.write(header) - file_obj.write(circuit_name) - file_obj.write(global_phase_data) - file_obj.write(metadata_raw) - # Write header payload - file_obj.write(registers_raw) - instruction_buffer = io.BytesIO() - custom_operations = {} - index_map = {} - index_map["q"] = {bit: index for index, bit in enumerate(circuit.qubits)} - index_map["c"] = {bit: index for index, bit in enumerate(circuit.clbits)} - for instruction in circuit.data: - _write_instruction( - instruction_buffer, instruction, custom_operations, index_map, use_symengine - ) - - with io.BytesIO() as custom_operations_buffer: - new_custom_operations = list(custom_operations.keys()) - while new_custom_operations: - operations_to_serialize = new_custom_operations.copy() - new_custom_operations = [] - for name in operations_to_serialize: - operation = custom_operations[name] - new_custom_operations.extend( - _write_custom_operation( - custom_operations_buffer, - name, - operation, - custom_operations, - use_symengine, - ) - ) - - file_obj.write(struct.pack(formats.CUSTOM_CIRCUIT_DEF_HEADER_PACK, len(custom_operations))) - file_obj.write(custom_operations_buffer.getvalue()) - - file_obj.write(instruction_buffer.getvalue()) - instruction_buffer.close() - - # Write calibrations - _write_calibrations(file_obj, circuit.calibrations, metadata_serializer) - _write_layout(file_obj, circuit) - - -def read_circuit( # type: ignore[no-untyped-def] - file_obj, version, metadata_deserializer=None, use_symengine=False -): - """Read a single QuantumCircuit object from the file like object. - - Args: - file_obj (FILE): The file like object to read the circuit data from. - version (int): QPY version. - metadata_deserializer (JSONDecoder): An optional JSONDecoder class - that will be used for the ``cls`` kwarg on the internal - ``json.load`` call used to deserialize the JSON payload used for - the :attr:`.QuantumCircuit.metadata` attribute for a circuit - in the file-like object. If this is not specified the circuit metadata will - be parsed as JSON with the stdlib ``json.load()`` function using - the default ``JSONDecoder`` class. - - use_symengine (bool): If True, symbolic objects will be de-serialized using - symengine's native mechanism. This is a faster serialization alternative, but not - supported in all platforms. Please check that your target platform is supported by - the symengine library before setting this option, as it will be required by qpy to - deserialize the payload. - - Returns: - QuantumCircuit: The circuit object from the file. - - Raises: - QpyError: Invalid register. - """ - vectors = {} - if version < 2: - header, name, metadata = _read_header(file_obj, metadata_deserializer=metadata_deserializer) - else: - header, name, metadata = _read_header_v2( - file_obj, version, vectors, metadata_deserializer=metadata_deserializer - ) - - global_phase = header["global_phase"] - num_qubits = header["num_qubits"] - num_clbits = header["num_clbits"] - num_registers = header["num_registers"] - num_instructions = header["num_instructions"] - # `out_registers` is two "name: register" maps segregated by type for the rest of QPY, and - # `all_registers` is the complete ordered list used to construct the `QuantumCircuit`. - out_registers = {"q": {}, "c": {}} - all_registers = [] - out_bits = {"q": [None] * num_qubits, "c": [None] * num_clbits} - if num_registers > 0: - if version < 4: - registers = _read_registers(file_obj, num_registers) - else: - registers = _read_registers_v4(file_obj, num_registers) - - for bit_type_label, bit_type, reg_type in [ - ("q", Qubit, QuantumRegister), - ("c", Clbit, ClassicalRegister), - ]: - # This does two passes through the registers. In the first, we're actually just - # constructing the `Bit` instances: any register that is `standalone` "owns" all its - # bits in the old Qiskit data model, so we have to construct those by creating the - # register and taking the bits from them. That's the case even if that register isn't - # actually in the circuit, which is why we stored them (with `in_circuit=False`) in QPY. - # - # Since there's no guarantees in QPY about the ordering of registers, we have to pass - # through all registers to create the bits first, because we can't reliably know if a - # non-standalone register contains bits from a standalone one until we've seen all - # standalone registers. - typed_bits = out_bits[bit_type_label] - typed_registers = registers[bit_type_label] - for register_name, ( - standalone, - indices, - _incircuit, - ) in typed_registers.items(): - if not standalone: - continue - register = reg_type(len(indices), register_name) - out_registers[bit_type_label][register_name] = register - for owned, index in zip(register, indices): - # Negative indices are for bits that aren't in the circuit. - if index >= 0: - typed_bits[index] = owned - # Any remaining unset bits aren't owned, so we can construct them in the standard way. - typed_bits = [bit if bit is not None else bit_type() for bit in typed_bits] - # Finally _properly_ construct all the registers. Bits can be in more than one - # register, including bits that are old-style "owned" by a register. - for register_name, ( - standalone, - indices, - in_circuit, - ) in typed_registers.items(): - if standalone: - register = out_registers[bit_type_label][register_name] - else: - register = reg_type( - name=register_name, - bits=[typed_bits[x] if x >= 0 else bit_type() for x in indices], - ) - out_registers[bit_type_label][register_name] = register - if in_circuit: - all_registers.append(register) - out_bits[bit_type_label] = typed_bits - else: - out_bits = { - "q": [Qubit() for _ in out_bits["q"]], - "c": [Clbit() for _ in out_bits["c"]], - } - circ = QuantumCircuit( - out_bits["q"], - out_bits["c"], - *all_registers, - name=name, - global_phase=global_phase, - metadata=metadata, - ) - - custom_operations = _read_custom_operations(file_obj, version, vectors) - for _instruction in range(num_instructions): - _read_instruction( - file_obj, - circ, - out_registers, - custom_operations, - version, - vectors, - use_symengine, - ) - - # Read calibrations - if version >= 5: - circ.calibrations = _read_calibrations(file_obj, version, vectors, metadata_deserializer) - - for vec_name, (vector, initialized_params) in vectors.items(): - if len(initialized_params) != len(vector): - warnings.warn( - f"The ParameterVector: '{vec_name}' is not fully identical to its " - "pre-serialization state. Elements " - f"{', '.join([str(x) for x in set(range(len(vector))) - initialized_params])} " - "in the ParameterVector will be not equal to the pre-serialized ParameterVector " - f"as they weren't used in the circuit: {circ.name}", - UserWarning, - ) - if version >= 8: - if version >= 10: - _read_layout_v2(file_obj, circ) - else: - _read_layout(file_obj, circ) - return circ diff --git a/qiskit_ibm_runtime/qpy/binary_io/schedules.py b/qiskit_ibm_runtime/qpy/binary_io/schedules.py deleted file mode 100644 index 183bb50df1..0000000000 --- a/qiskit_ibm_runtime/qpy/binary_io/schedules.py +++ /dev/null @@ -1,656 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# 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 - -"""Read and write schedule and schedule instructions.""" -import json -import struct -import zlib -import warnings - -from io import BytesIO -import numpy as np - -from qiskit.pulse import library, channels, instructions -from qiskit.pulse.schedule import ScheduleBlock -from qiskit.utils import optionals as _optional -from qiskit.pulse.configuration import Kernel, Discriminator -from .. import formats, common, type_keys -from ..exceptions import QpyError -from . import value - - -if _optional.HAS_SYMENGINE: - import symengine as sym -else: - import sympy as sym - - -def _read_channel(file_obj, version): # type: ignore[no-untyped-def] - type_key = common.read_type_key(file_obj) - index = value.read_value(file_obj, version, {}) - - channel_cls = type_keys.ScheduleChannel.retrieve(type_key) - - return channel_cls(index) - - -def _read_waveform(file_obj, version): # type: ignore[no-untyped-def] - header = formats.WAVEFORM._make( - struct.unpack( - formats.WAVEFORM_PACK, - file_obj.read(formats.WAVEFORM_SIZE), - ) - ) - samples_raw = file_obj.read(header.data_size) - samples = common.data_from_binary(samples_raw, np.load) - name = value.read_value(file_obj, version, {}) - - return library.Waveform( - samples=samples, - name=name, - epsilon=header.epsilon, - limit_amplitude=header.amp_limited, - ) - - -def _loads_obj(type_key, binary_data, version, vectors): # type: ignore[no-untyped-def] - """Wraps `value.loads_value` to deserialize binary data to dictionary - or list objects which are not supported by `value.loads_value`. - """ - if type_key == b"D": - with BytesIO(binary_data) as container: - return common.read_mapping( - file_obj=container, - deserializer=_loads_obj, - version=version, - vectors=vectors, - ) - elif type_key == b"l": - with BytesIO(binary_data) as container: - return common.read_sequence( - file_obj=container, - deserializer=_loads_obj, - version=version, - vectors=vectors, - ) - else: - return value.loads_value(type_key, binary_data, version, vectors) - - -def _read_kernel(file_obj, version): # type: ignore[no-untyped-def] - params = common.read_mapping( - file_obj=file_obj, - deserializer=_loads_obj, - version=version, - vectors={}, - ) - name = value.read_value(file_obj, version, {}) - return Kernel(name=name, **params) - - -def _read_discriminator(file_obj, version): # type: ignore[no-untyped-def] - params = common.read_mapping( - file_obj=file_obj, - deserializer=_loads_obj, - version=version, - vectors={}, - ) - name = value.read_value(file_obj, version, {}) - return Discriminator(name=name, **params) - - -def _loads_symbolic_expr(expr_bytes, use_symengine=False): # type: ignore[no-untyped-def] - if expr_bytes == b"": - return None - - if use_symengine: - _optional.HAS_SYMENGINE.require_now("load a symengine expression") - from symengine.lib.symengine_wrapper import ( # pylint: disable=import-outside-toplevel, no-name-in-module - load_basic, - ) - - expr = load_basic(zlib.decompress(expr_bytes)) - - else: - from sympy import parse_expr # pylint: disable=import-outside-toplevel - - expr_txt = zlib.decompress(expr_bytes).decode(common.ENCODE) - expr = parse_expr(expr_txt) - if _optional.HAS_SYMENGINE: - from symengine import sympify # pylint: disable=import-outside-toplevel - - return sympify(expr) - return expr - - -def _read_symbolic_pulse(file_obj, version): # type: ignore[no-untyped-def] - make = formats.SYMBOLIC_PULSE._make - pack = formats.SYMBOLIC_PULSE_PACK - size = formats.SYMBOLIC_PULSE_SIZE - - header = make( - struct.unpack( - pack, - file_obj.read(size), - ) - ) - pulse_type = file_obj.read(header.type_size).decode(common.ENCODE) - envelope = _loads_symbolic_expr(file_obj.read(header.envelope_size)) - constraints = _loads_symbolic_expr(file_obj.read(header.constraints_size)) - valid_amp_conditions = _loads_symbolic_expr(file_obj.read(header.valid_amp_conditions_size)) - parameters = common.read_mapping( - file_obj, - deserializer=value.loads_value, - version=version, - vectors={}, - ) - - # In the transition to Qiskit Terra 0.23 (QPY version 6), the representation of library pulses - # was changed from complex "amp" to float "amp" and "angle". The existing library pulses in - # previous versions are handled here separately to conform with the new representation. To - # avoid role assumption for "amp" for custom pulses, only the library pulses are handled this - # way. - - # List of pulses in the library in QPY version 5 and below: - legacy_library_pulses = ["Gaussian", "GaussianSquare", "Drag", "Constant"] - class_name = "SymbolicPulse" # Default class name, if not in the library - - if pulse_type in legacy_library_pulses: - # Once complex amp support will be deprecated we will need: - # parameters["angle"] = np.angle(parameters["amp"]) - # parameters["amp"] = np.abs(parameters["amp"]) - - # In the meanwhile we simply add: - parameters["angle"] = 0 - _amp, _angle = sym.symbols("amp, angle") - envelope = envelope.subs(_amp, _amp * sym.exp(sym.I * _angle)) - - # And warn that this will change in future releases: - warnings.warn( - "Complex amp support for symbolic library pulses will be deprecated. " - "Once deprecated, library pulses loaded from old QPY files (Terra version < 0.23)," - " will be converted automatically to float (amp,angle) representation.", - PendingDeprecationWarning, - ) - class_name = "ScalableSymbolicPulse" - - duration = value.read_value(file_obj, version, {}) - name = value.read_value(file_obj, version, {}) - - if class_name == "SymbolicPulse": - return library.SymbolicPulse( - pulse_type=pulse_type, - duration=duration, - parameters=parameters, - name=name, - limit_amplitude=header.amp_limited, - envelope=envelope, - constraints=constraints, - valid_amp_conditions=valid_amp_conditions, - ) - elif class_name == "ScalableSymbolicPulse": - return library.ScalableSymbolicPulse( - pulse_type=pulse_type, - duration=duration, - amp=parameters["amp"], - angle=parameters["angle"], - parameters=parameters, - name=name, - limit_amplitude=header.amp_limited, - envelope=envelope, - constraints=constraints, - valid_amp_conditions=valid_amp_conditions, - ) - else: - raise NotImplementedError(f"Unknown class '{class_name}'") - - -def _read_symbolic_pulse_v6(file_obj, version, use_symengine): # type: ignore[no-untyped-def] - make = formats.SYMBOLIC_PULSE_V2._make - pack = formats.SYMBOLIC_PULSE_PACK_V2 - size = formats.SYMBOLIC_PULSE_SIZE_V2 - - header = make( - struct.unpack( - pack, - file_obj.read(size), - ) - ) - class_name = file_obj.read(header.class_name_size).decode(common.ENCODE) - pulse_type = file_obj.read(header.type_size).decode(common.ENCODE) - envelope = _loads_symbolic_expr(file_obj.read(header.envelope_size), use_symengine) - constraints = _loads_symbolic_expr(file_obj.read(header.constraints_size), use_symengine) - valid_amp_conditions = _loads_symbolic_expr( - file_obj.read(header.valid_amp_conditions_size), use_symengine - ) - parameters = common.read_mapping( - file_obj, - deserializer=value.loads_value, - version=version, - vectors={}, - ) - - duration = value.read_value(file_obj, version, {}) - name = value.read_value(file_obj, version, {}) - - if class_name == "SymbolicPulse": - return library.SymbolicPulse( - pulse_type=pulse_type, - duration=duration, - parameters=parameters, - name=name, - limit_amplitude=header.amp_limited, - envelope=envelope, - constraints=constraints, - valid_amp_conditions=valid_amp_conditions, - ) - elif class_name == "ScalableSymbolicPulse": - return library.ScalableSymbolicPulse( - pulse_type=pulse_type, - duration=duration, - amp=parameters["amp"], - angle=parameters["angle"], - parameters=parameters, - name=name, - limit_amplitude=header.amp_limited, - envelope=envelope, - constraints=constraints, - valid_amp_conditions=valid_amp_conditions, - ) - else: - raise NotImplementedError(f"Unknown class '{class_name}'") - - -def _read_alignment_context(file_obj, version): # type: ignore[no-untyped-def] - type_key = common.read_type_key(file_obj) - - context_params = common.read_sequence( - file_obj, - deserializer=value.loads_value, - version=version, - vectors={}, - ) - context_cls = type_keys.ScheduleAlignment.retrieve(type_key) - - instance = object.__new__(context_cls) - instance._context_params = tuple(context_params) - - return instance - - -# pylint: disable=too-many-return-statements -def _loads_operand(type_key, data_bytes, version, use_symengine): # type: ignore[no-untyped-def] - if type_key == type_keys.ScheduleOperand.WAVEFORM: - return common.data_from_binary(data_bytes, _read_waveform, version=version) - if type_key == type_keys.ScheduleOperand.SYMBOLIC_PULSE: - if version < 6: - return common.data_from_binary(data_bytes, _read_symbolic_pulse, version=version) - else: - return common.data_from_binary( - data_bytes, - _read_symbolic_pulse_v6, - version=version, - use_symengine=use_symengine, - ) - if type_key == type_keys.ScheduleOperand.CHANNEL: - return common.data_from_binary(data_bytes, _read_channel, version=version) - if type_key == type_keys.ScheduleOperand.OPERAND_STR: - return data_bytes.decode(common.ENCODE) - if type_key == type_keys.ScheduleOperand.KERNEL: - return common.data_from_binary( - data_bytes, - _read_kernel, - version=version, - ) - if type_key == type_keys.ScheduleOperand.DISCRIMINATOR: - return common.data_from_binary( - data_bytes, - _read_discriminator, - version=version, - ) - - return value.loads_value(type_key, data_bytes, version, {}) - - -def _read_element( # type: ignore[no-untyped-def] - file_obj, version, metadata_deserializer, use_symengine -): - type_key = common.read_type_key(file_obj) - - if type_key == type_keys.Program.SCHEDULE_BLOCK: - return read_schedule_block(file_obj, version, metadata_deserializer, use_symengine) - - operands = common.read_sequence( - file_obj, - deserializer=_loads_operand, - version=version, - use_symengine=use_symengine, - ) - name = value.read_value(file_obj, version, {}) - - instance = object.__new__(type_keys.ScheduleInstruction.retrieve(type_key)) - instance._operands = tuple(operands) - instance._name = name - instance._hash = None - - return instance - - -def _loads_reference_item( # type: ignore[no-untyped-def] - type_key, data_bytes, version, metadata_deserializer -): - if type_key == type_keys.Value.NULL: - return None - if type_key == type_keys.Program.SCHEDULE_BLOCK: - return common.data_from_binary( - data_bytes, - deserializer=read_schedule_block, - version=version, - metadata_deserializer=metadata_deserializer, - ) - - raise QpyError( - f"Loaded schedule reference item is neither None nor ScheduleBlock. " - f"Type key {type_key} is not valid data type for a reference items. " - "This data cannot be loaded. Please check QPY version." - ) - - -def _write_channel(file_obj, data): # type: ignore[no-untyped-def] - type_key = type_keys.ScheduleChannel.assign(data) - common.write_type_key(file_obj, type_key) - value.write_value(file_obj, data.index) - - -def _write_waveform(file_obj, data): # type: ignore[no-untyped-def] - samples_bytes = common.data_to_binary(data.samples, np.save) - - header = struct.pack( - formats.WAVEFORM_PACK, - data.epsilon, - len(samples_bytes), - data._limit_amplitude, - ) - file_obj.write(header) - file_obj.write(samples_bytes) - value.write_value(file_obj, data.name) - - -def _dumps_obj(obj): # type: ignore[no-untyped-def] - """Wraps `value.dumps_value` to serialize dictionary and list objects - which are not supported by `value.dumps_value`. - """ - if isinstance(obj, dict): - with BytesIO() as container: - common.write_mapping(file_obj=container, mapping=obj, serializer=_dumps_obj) - binary_data = container.getvalue() - return b"D", binary_data - elif isinstance(obj, list): - with BytesIO() as container: - common.write_sequence(file_obj=container, sequence=obj, serializer=_dumps_obj) - binary_data = container.getvalue() - return b"l", binary_data - else: - return value.dumps_value(obj) - - -def _write_kernel(file_obj, data): # type: ignore[no-untyped-def] - name = data.name - params = data.params - common.write_mapping(file_obj=file_obj, mapping=params, serializer=_dumps_obj) - value.write_value(file_obj, name) - - -def _write_discriminator(file_obj, data): # type: ignore[no-untyped-def] - name = data.name - params = data.params - common.write_mapping(file_obj=file_obj, mapping=params, serializer=_dumps_obj) - value.write_value(file_obj, name) - - -def _dumps_symbolic_expr(expr, use_symengine): # type: ignore[no-untyped-def] - if expr is None: - return b"" - - if use_symengine: - _optional.HAS_SYMENGINE.require_now("dump a symengine expression") - expr_bytes = expr.__reduce__()[1][0] - else: - from sympy import srepr, sympify # pylint: disable=import-outside-toplevel - - expr_bytes = srepr(sympify(expr)).encode(common.ENCODE) - return zlib.compress(expr_bytes) - - -def _write_symbolic_pulse(file_obj, data, use_symengine): # type: ignore[no-untyped-def] - class_name_bytes = data.__class__.__name__.encode(common.ENCODE) - pulse_type_bytes = data.pulse_type.encode(common.ENCODE) - envelope_bytes = _dumps_symbolic_expr(data.envelope, use_symengine) - constraints_bytes = _dumps_symbolic_expr(data.constraints, use_symengine) - valid_amp_conditions_bytes = _dumps_symbolic_expr(data.valid_amp_conditions, use_symengine) - - header_bytes = struct.pack( - formats.SYMBOLIC_PULSE_PACK_V2, - len(class_name_bytes), - len(pulse_type_bytes), - len(envelope_bytes), - len(constraints_bytes), - len(valid_amp_conditions_bytes), - data._limit_amplitude, - ) - file_obj.write(header_bytes) - file_obj.write(class_name_bytes) - file_obj.write(pulse_type_bytes) - file_obj.write(envelope_bytes) - file_obj.write(constraints_bytes) - file_obj.write(valid_amp_conditions_bytes) - common.write_mapping( - file_obj, - mapping=data._params, - serializer=value.dumps_value, - ) - value.write_value(file_obj, data.duration) - value.write_value(file_obj, data.name) - - -def _write_alignment_context(file_obj, context): # type: ignore[no-untyped-def] - type_key = type_keys.ScheduleAlignment.assign(context) - common.write_type_key(file_obj, type_key) - common.write_sequence( - file_obj, - sequence=context._context_params, - serializer=value.dumps_value, - ) - - -def _dumps_operand(operand, use_symengine): # type: ignore[no-untyped-def] - if isinstance(operand, library.Waveform): - type_key = type_keys.ScheduleOperand.WAVEFORM - data_bytes = common.data_to_binary(operand, _write_waveform) - elif isinstance(operand, library.SymbolicPulse): - type_key = type_keys.ScheduleOperand.SYMBOLIC_PULSE - data_bytes = common.data_to_binary( - operand, _write_symbolic_pulse, use_symengine=use_symengine - ) - elif isinstance(operand, channels.Channel): - type_key = type_keys.ScheduleOperand.CHANNEL - data_bytes = common.data_to_binary(operand, _write_channel) - elif isinstance(operand, str): - type_key = type_keys.ScheduleOperand.OPERAND_STR - data_bytes = operand.encode(common.ENCODE) - elif isinstance(operand, Kernel): - type_key = type_keys.ScheduleOperand.KERNEL - data_bytes = common.data_to_binary(operand, _write_kernel) - elif isinstance(operand, Discriminator): - type_key = type_keys.ScheduleOperand.DISCRIMINATOR - data_bytes = common.data_to_binary(operand, _write_discriminator) - else: - type_key, data_bytes = value.dumps_value(operand) - - return type_key, data_bytes - - -def _write_element( # type: ignore[no-untyped-def] - file_obj, element, metadata_serializer, use_symengine -): - if isinstance(element, ScheduleBlock): - common.write_type_key(file_obj, type_keys.Program.SCHEDULE_BLOCK) - write_schedule_block(file_obj, element, metadata_serializer, use_symengine) - else: - type_key = type_keys.ScheduleInstruction.assign(element) - common.write_type_key(file_obj, type_key) - common.write_sequence( - file_obj, - sequence=element.operands, - serializer=_dumps_operand, - use_symengine=use_symengine, - ) - value.write_value(file_obj, element.name) - - -def _dumps_reference_item(schedule, metadata_serializer): # type: ignore[no-untyped-def] - if schedule is None: - type_key = type_keys.Value.NULL - data_bytes = b"" - else: - type_key = type_keys.Program.SCHEDULE_BLOCK - data_bytes = common.data_to_binary( - obj=schedule, - serializer=write_schedule_block, - metadata_serializer=metadata_serializer, - ) - return type_key, data_bytes - - -def read_schedule_block( # type: ignore[no-untyped-def] - file_obj, version, metadata_deserializer=None, use_symengine=False -): - """Read a single ScheduleBlock from the file like object. - - Args: - file_obj (File): A file like object that contains the QPY binary data. - version (int): QPY version. - metadata_deserializer (JSONDecoder): An optional JSONDecoder class - that will be used for the ``cls`` kwarg on the internal - ``json.load`` call used to deserialize the JSON payload used for - the :attr:`.ScheduleBlock.metadata` attribute for a schdule block - in the file-like object. If this is not specified the circuit metadata will - be parsed as JSON with the stdlib ``json.load()`` function using - the default ``JSONDecoder`` class. - - use_symengine (bool): If True, symbolic objects will be serialized using symengine's - native mechanism. This is a faster serialization alternative, but not supported in all - platforms. Please check that your target platform is supported by the symengine library - before setting this option, as it will be required by qpy to deserialize the payload. - - Returns: - ScheduleBlock: The schedule block object from the file. - - Raises: - TypeError: If any of the instructions is invalid data format. - QiskitError: QPY version is earlier than block support. - """ - - data = formats.SCHEDULE_BLOCK_HEADER._make( - struct.unpack( - formats.SCHEDULE_BLOCK_HEADER_PACK, - file_obj.read(formats.SCHEDULE_BLOCK_HEADER_SIZE), - ) - ) - name = file_obj.read(data.name_size).decode(common.ENCODE) - metadata_raw = file_obj.read(data.metadata_size) - metadata = json.loads(metadata_raw, cls=metadata_deserializer) - context = _read_alignment_context(file_obj, version) - - block = ScheduleBlock( - name=name, - metadata=metadata, - alignment_context=context, - ) - for _ in range(data.num_elements): - block_elm = _read_element(file_obj, version, metadata_deserializer, use_symengine) - block.append(block_elm, inplace=True) - - # Load references - if version >= 7: - flat_key_refdict = common.read_mapping( - file_obj=file_obj, - deserializer=_loads_reference_item, - version=version, - metadata_deserializer=metadata_deserializer, - ) - ref_dict = {} - for key_str, schedule in flat_key_refdict.items(): - if schedule is not None: - composite_key = tuple(key_str.split(instructions.Reference.key_delimiter)) - ref_dict[composite_key] = schedule - if ref_dict: - block.assign_references(ref_dict, inplace=True) - - return block - - -def write_schedule_block( # type: ignore[no-untyped-def] - file_obj, block, metadata_serializer=None, use_symengine=False -): - """Write a single ScheduleBlock object in the file like object. - - Args: - file_obj (File): The file like object to write the circuit data in. - block (ScheduleBlock): A schedule block data to write. - metadata_serializer (JSONEncoder): An optional JSONEncoder class that - will be passed the :attr:`.ScheduleBlock.metadata` dictionary for - ``block`` and will be used as the ``cls`` kwarg - on the ``json.dump()`` call to JSON serialize that dictionary. - - use_symengine (bool): If True, symbolic objects will be serialized using symengine's - native mechanism. This is a faster serialization alternative, but not supported in all - platforms. Please check that your target platform is supported by the symengine library - before setting this option, as it will be required by qpy to deserialize the payload. - - Raises: - TypeError: If any of the instructions is invalid data format. - """ - metadata = json.dumps(block.metadata, separators=(",", ":"), cls=metadata_serializer).encode( - common.ENCODE - ) - block_name = block.name.encode(common.ENCODE) - - # Write schedule block header - header_raw = formats.SCHEDULE_BLOCK_HEADER( - name_size=len(block_name), - metadata_size=len(metadata), - num_elements=len(block), - ) - header = struct.pack(formats.SCHEDULE_BLOCK_HEADER_PACK, *header_raw) - file_obj.write(header) - file_obj.write(block_name) - file_obj.write(metadata) - - _write_alignment_context(file_obj, block.alignment_context) - for block_elm in block._blocks: - _write_element(file_obj, block_elm, metadata_serializer, use_symengine) - - # Write references - flat_key_refdict = {} - for ref_keys, schedule in block._reference_manager.items(): - # Do not call block.reference. This returns the reference of most outer program by design. - key_str = instructions.Reference.key_delimiter.join(ref_keys) - flat_key_refdict[key_str] = schedule - common.write_mapping( - file_obj=file_obj, - mapping=flat_key_refdict, - serializer=_dumps_reference_item, - metadata_serializer=metadata_serializer, - ) diff --git a/qiskit_ibm_runtime/qpy/binary_io/value.py b/qiskit_ibm_runtime/qpy/binary_io/value.py deleted file mode 100644 index ca89d7ab36..0000000000 --- a/qiskit_ibm_runtime/qpy/binary_io/value.py +++ /dev/null @@ -1,586 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# 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. -"""Binary IO for any value objects, such as numbers, string, parameters.""" -# pylint: disable=line-too-long - -from __future__ import annotations - -import collections.abc -import struct -import uuid - -from typing import Any -import numpy as np - -from qiskit.circuit import CASE_DEFAULT, Clbit, ClassicalRegister -from qiskit.circuit.classical import expr, types -from qiskit.circuit.parameter import Parameter -from qiskit.circuit.parameterexpression import ParameterExpression -from qiskit.circuit.parametervector import ParameterVector, ParameterVectorElement -from qiskit.utils import optionals as _optional - -from .. import common, formats, exceptions, type_keys - - -def _write_parameter(file_obj, obj): # type: ignore[no-untyped-def] - name_bytes = obj._name.encode(common.ENCODE) - file_obj.write(struct.pack(formats.PARAMETER_PACK, len(name_bytes), obj._uuid.bytes)) - file_obj.write(name_bytes) - - -def _write_parameter_vec(file_obj, obj): # type: ignore[no-untyped-def] - name_bytes = obj._vector._name.encode(common.ENCODE) - file_obj.write( - struct.pack( - formats.PARAMETER_VECTOR_ELEMENT_PACK, - len(name_bytes), - obj._vector._size, - obj._uuid.bytes, - obj._index, - ) - ) - file_obj.write(name_bytes) - - -def _write_parameter_expression(file_obj, obj, use_symengine): # type: ignore[no-untyped-def] - if use_symengine: - _optional.HAS_SYMENGINE.require_now("write_parameter_expression") - expr_bytes = obj._symbol_expr.__reduce__()[1][0] - else: - from sympy import srepr, sympify # pylint: disable=import-outside-toplevel - - expr_bytes = srepr(sympify(obj._symbol_expr)).encode(common.ENCODE) - - param_expr_header_raw = struct.pack( - formats.PARAMETER_EXPR_PACK, len(obj._parameter_symbols), len(expr_bytes) - ) - file_obj.write(param_expr_header_raw) - file_obj.write(expr_bytes) - - for symbol, value in obj._parameter_symbols.items(): - symbol_key = type_keys.Value.assign(symbol) - - # serialize key - if symbol_key == type_keys.Value.PARAMETER_VECTOR: - symbol_data = common.data_to_binary(symbol, _write_parameter_vec) - else: - symbol_data = common.data_to_binary(symbol, _write_parameter) - - # serialize value - if value == symbol._symbol_expr: - value_key = symbol_key - value_data = bytes() - else: - value_key, value_data = dumps_value(value, use_symengine=use_symengine) - - elem_header = struct.pack( - formats.PARAM_EXPR_MAP_ELEM_V3_PACK, - symbol_key, - value_key, - len(value_data), - ) - file_obj.write(elem_header) - file_obj.write(symbol_data) - file_obj.write(value_data) - - -class _ExprWriter(expr.ExprVisitor[None]): - __slots__ = ("file_obj", "clbit_indices") - - def __init__(self, file_obj, clbit_indices): # type: ignore[no-untyped-def] - self.file_obj = file_obj - self.clbit_indices = clbit_indices - - def visit_generic(self, node, /): # type: ignore[no-untyped-def] - raise exceptions.QpyError(f"unhandled Expr object '{node}'") - - def visit_var(self, node, /): # type: ignore[no-untyped-def] - self.file_obj.write(type_keys.Expression.VAR) - _write_expr_type(self.file_obj, node.type) - if isinstance(node.var, Clbit): - self.file_obj.write(type_keys.ExprVar.CLBIT) - self.file_obj.write( - struct.pack( - formats.EXPR_VAR_CLBIT_PACK, - *formats.EXPR_VAR_CLBIT(self.clbit_indices[node.var]), - ) - ) - elif isinstance(node.var, ClassicalRegister): - self.file_obj.write(type_keys.ExprVar.REGISTER) - self.file_obj.write( - struct.pack( - formats.EXPR_VAR_REGISTER_PACK, - *formats.EXPR_VAR_REGISTER(len(node.var.name)), - ) - ) - self.file_obj.write(node.var.name.encode(common.ENCODE)) - else: - raise exceptions.QpyError(f"unhandled Var object '{node.var}'") - - def visit_value(self, node, /): # type: ignore[no-untyped-def] - self.file_obj.write(type_keys.Expression.VALUE) - _write_expr_type(self.file_obj, node.type) - if node.value is True or node.value is False: - self.file_obj.write(type_keys.ExprValue.BOOL) - self.file_obj.write( - struct.pack(formats.EXPR_VALUE_BOOL_PACK, *formats.EXPR_VALUE_BOOL(node.value)) - ) - elif isinstance(node.value, int): - self.file_obj.write(type_keys.ExprValue.INT) - if node.value == 0: - num_bytes = 0 - buffer = b"" - else: - # This wastes a byte for `-(2 ** (8*n - 1))` for natural `n`, but they'll still - # decode fine so it's not worth another special case. They'll encode to - # b"\xff\x80\x00\x00...", but we could encode them to b"\x80\x00\x00...". - num_bytes = (node.value.bit_length() // 8) + 1 - buffer = node.value.to_bytes(num_bytes, "big", signed=True) - self.file_obj.write( - struct.pack(formats.EXPR_VALUE_INT_PACK, *formats.EXPR_VALUE_INT(num_bytes)) - ) - self.file_obj.write(buffer) - else: - raise exceptions.QpyError(f"unhandled Value object '{node.value}'") - - def visit_cast(self, node, /): # type: ignore[no-untyped-def] - self.file_obj.write(type_keys.Expression.CAST) - _write_expr_type(self.file_obj, node.type) - self.file_obj.write( - struct.pack(formats.EXPRESSION_CAST_PACK, *formats.EXPRESSION_CAST(node.implicit)) - ) - node.operand.accept(self) - - def visit_unary(self, node, /): # type: ignore[no-untyped-def] - self.file_obj.write(type_keys.Expression.UNARY) - _write_expr_type(self.file_obj, node.type) - self.file_obj.write( - struct.pack(formats.EXPRESSION_UNARY_PACK, *formats.EXPRESSION_UNARY(node.op.value)) - ) - node.operand.accept(self) - - def visit_binary(self, node, /): # type: ignore[no-untyped-def] - self.file_obj.write(type_keys.Expression.BINARY) - _write_expr_type(self.file_obj, node.type) - self.file_obj.write( - struct.pack(formats.EXPRESSION_BINARY_PACK, *formats.EXPRESSION_UNARY(node.op.value)) - ) - node.left.accept(self) - node.right.accept(self) - - -def _write_expr( # type: ignore[no-untyped-def] - file_obj, node: expr.Expr, clbit_indices: collections.abc.Mapping[Clbit, int] -) -> None: - node.accept(_ExprWriter(file_obj, clbit_indices)) # type: ignore[no-untyped-call] - - -def _write_expr_type(file_obj, type_: types.Type): # type: ignore[no-untyped-def] - if type_.kind is types.Bool: - file_obj.write(type_keys.ExprType.BOOL) - elif type_.kind is types.Uint: - file_obj.write(type_keys.ExprType.UINT) - file_obj.write( - struct.pack( - formats.EXPR_TYPE_UINT_PACK, # type: ignore[attr-defined] - *formats.EXPR_TYPE_UINT(type_.width), # type: ignore[attr-defined] - ) - ) - else: - raise exceptions.QpyError(f"unhandled Type object '{type_};") - - -def _read_parameter(file_obj): # type: ignore[no-untyped-def] - data = formats.PARAMETER( - *struct.unpack(formats.PARAMETER_PACK, file_obj.read(formats.PARAMETER_SIZE)) - ) - param_uuid = uuid.UUID(bytes=data.uuid) - name = file_obj.read(data.name_size).decode(common.ENCODE) - return Parameter(name, uuid=param_uuid) - - -def _read_parameter_vec(file_obj, vectors): # type: ignore[no-untyped-def] - data = formats.PARAMETER_VECTOR_ELEMENT( - *struct.unpack( - formats.PARAMETER_VECTOR_ELEMENT_PACK, - file_obj.read(formats.PARAMETER_VECTOR_ELEMENT_SIZE), - ), - ) - param_uuid = uuid.UUID(bytes=data.uuid) - name = file_obj.read(data.vector_name_size).decode(common.ENCODE) - if name not in vectors: - vectors[name] = (ParameterVector(name, data.vector_size), set()) - vector = vectors[name][0] - if vector[data.index]._uuid != param_uuid: - vectors[name][1].add(data.index) - vector._params[data.index] = ParameterVectorElement(vector, data.index, uuid=param_uuid) - return vector[data.index] - - -def _read_parameter_expression(file_obj): # type: ignore[no-untyped-def] - data = formats.PARAMETER_EXPR( - *struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE)) - ) - # pylint: disable=import-outside-toplevel - from sympy.parsing.sympy_parser import parse_expr - - if _optional.HAS_SYMENGINE: - from symengine import sympify # pylint: disable=import-outside-toplevel - - expr_ = sympify(parse_expr(file_obj.read(data.expr_size).decode(common.ENCODE))) - else: - expr_ = parse_expr(file_obj.read(data.expr_size).decode(common.ENCODE)) - symbol_map = {} - for _ in range(data.map_elements): - elem_data = formats.PARAM_EXPR_MAP_ELEM( - *struct.unpack( - formats.PARAM_EXPR_MAP_ELEM_PACK, - file_obj.read(formats.PARAM_EXPR_MAP_ELEM_SIZE), - ) - ) - symbol = _read_parameter(file_obj) - - elem_key = type_keys.Value(elem_data.type) - binary_data = file_obj.read(elem_data.size) - if elem_key == type_keys.Value.INTEGER: - value = struct.unpack("!q", binary_data) - elif elem_key == type_keys.Value.FLOAT: - value = struct.unpack("!d", binary_data) - elif elem_key == type_keys.Value.COMPLEX: - value = complex(*struct.unpack(formats.COMPLEX_PACK, binary_data)) - elif elem_key == type_keys.Value.PARAMETER: - value = symbol._symbol_expr - elif elem_key == type_keys.Value.PARAMETER_EXPRESSION: - value = common.data_from_binary(binary_data, _read_parameter_expression) - else: - raise exceptions.QpyError("Invalid parameter expression map type: %s" % elem_key) - symbol_map[symbol] = value - - return ParameterExpression(symbol_map, expr_) - - -# pylint: disable=too-many-return-statements -def _read_expr( # type: ignore[no-untyped-def] - file_obj, - clbits: collections.abc.Sequence[Clbit], - cregs: collections.abc.Mapping[str, ClassicalRegister], -) -> expr.Expr: # pylint: disable=too-many-return-statements - type_key = file_obj.read(formats.EXPRESSION_DISCRIMINATOR_SIZE) # type: ignore[attr-defined] - type_ = _read_expr_type(file_obj) - if type_key == type_keys.Expression.VAR: - var_type_key = file_obj.read(formats.EXPR_VAR_DISCRIMINATOR_SIZE) # type: ignore[attr-defined] - if var_type_key == type_keys.ExprVar.CLBIT: - payload = formats.EXPR_VAR_CLBIT._make( # type: ignore[attr-defined] - struct.unpack( - formats.EXPR_VAR_CLBIT_PACK, # type: ignore[attr-defined] - file_obj.read(formats.EXPR_VAR_CLBIT_SIZE), # type: ignore[attr-defined] - ) - ) - return expr.Var(clbits[payload.index], type_) - if var_type_key == type_keys.ExprVar.REGISTER: - payload = formats.EXPR_VAR_REGISTER._make( # type: ignore[attr-defined] - struct.unpack( - formats.EXPR_VAR_REGISTER_PACK, # type: ignore[attr-defined] - file_obj.read(formats.EXPR_VAR_REGISTER_SIZE), # type: ignore[attr-defined] - ) - ) - name = file_obj.read(payload.reg_name_size).decode(common.ENCODE) - return expr.Var(cregs[name], type_) - raise exceptions.QpyError("Invalid classical-expression Var key '{var_type_key}'") - if type_key == type_keys.Expression.VALUE: - value_type_key = file_obj.read(formats.EXPR_VALUE_DISCRIMINATOR_SIZE) # type: ignore[attr-defined] - if value_type_key == type_keys.ExprValue.BOOL: - payload = formats.EXPR_VALUE_BOOL._make( # type: ignore[attr-defined] - struct.unpack( - formats.EXPR_VALUE_BOOL_PACK, # type: ignore[attr-defined] - file_obj.read(formats.EXPR_VALUE_BOOL_SIZE), # type: ignore[attr-defined] - ) - ) - return expr.Value(payload.value, type_) - if value_type_key == type_keys.ExprValue.INT: - payload = formats.EXPR_VALUE_INT._make( # type: ignore[attr-defined] - struct.unpack( - formats.EXPR_VALUE_INT_PACK, # type: ignore[attr-defined] - file_obj.read(formats.EXPR_VALUE_INT_SIZE), # type: ignore[attr-defined] - ) - ) - return expr.Value( - int.from_bytes(file_obj.read(payload.num_bytes), "big", signed=True), - type_, - ) - raise exceptions.QpyError("Invalid classical-expression Value key '{value_type_key}'") - if type_key == type_keys.Expression.CAST: - payload = formats.EXPRESSION_CAST._make( # type: ignore[attr-defined] - struct.unpack( - formats.EXPRESSION_CAST_PACK, # type: ignore[attr-defined] - file_obj.read(formats.EXPRESSION_CAST_SIZE), # type: ignore[attr-defined] - ) - ) - return expr.Cast(_read_expr(file_obj, clbits, cregs), type_, implicit=payload.implicit) - if type_key == type_keys.Expression.UNARY: - payload = formats.EXPRESSION_UNARY._make( # type: ignore[attr-defined] - struct.unpack( - formats.EXPRESSION_UNARY_PACK, # type: ignore[attr-defined] - file_obj.read(formats.EXPRESSION_UNARY_SIZE), # type: ignore[attr-defined] - ) - ) - return expr.Unary(expr.Unary.Op(payload.opcode), _read_expr(file_obj, clbits, cregs), type_) - if type_key == type_keys.Expression.BINARY: - payload = formats.EXPRESSION_BINARY._make( # type: ignore[attr-defined] - struct.unpack( - formats.EXPRESSION_BINARY_PACK, # type: ignore[attr-defined] - file_obj.read(formats.EXPRESSION_BINARY_SIZE), # type: ignore[attr-defined] - ) - ) - return expr.Binary( - expr.Binary.Op(payload.opcode), - _read_expr(file_obj, clbits, cregs), - _read_expr(file_obj, clbits, cregs), - type_, - ) - raise exceptions.QpyError("Invalid classical-expression Expr key '{type_key}'") - - -def _read_expr_type(file_obj) -> types.Type: # type: ignore[no-untyped-def] - type_key = file_obj.read(formats.EXPR_TYPE_DISCRIMINATOR_SIZE) # type: ignore[no-untyped-def, attr-defined] - if type_key == type_keys.ExprType.BOOL: - return types.Bool() - if type_key == type_keys.ExprType.UINT: # type: ignore[no-untyped-def, attr-defined] - elem = formats.EXPR_TYPE_UINT._make( # type: ignore[no-untyped-def, attr-defined] - struct.unpack( # type: ignore[no-untyped-def] - formats.EXPR_TYPE_UINT_PACK, # type: ignore[attr-defined] - file_obj.read(formats.EXPR_TYPE_UINT_SIZE), # type: ignore[no-untyped-def, attr-defined] - ) - ) - return types.Uint(elem.width) - raise exceptions.QpyError(f"Invalid classical-expression Type key '{type_key}'") - - -def _read_parameter_expression_v3(file_obj, vectors, use_symengine): # type: ignore[no-untyped-def] - data = formats.PARAMETER_EXPR( - *struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE)) - ) - # pylint: disable=import-outside-toplevel - from sympy.parsing.sympy_parser import parse_expr - - # pylint: disable=import-outside-toplevel - - payload = file_obj.read(data.expr_size) - if use_symengine: - _optional.HAS_SYMENGINE.require_now("read_parameter_expression_v3") - from symengine.lib.symengine_wrapper import ( # pylint: disable = no-name-in-module - load_basic, - ) - - expr_ = load_basic(payload) - else: - if _optional.HAS_SYMENGINE: - from symengine import sympify - - expr_ = sympify(parse_expr(payload.decode(common.ENCODE))) - else: - expr_ = parse_expr(payload.decode(common.ENCODE)) - symbol_map = {} - for _ in range(data.map_elements): - elem_data = formats.PARAM_EXPR_MAP_ELEM_V3( - *struct.unpack( - formats.PARAM_EXPR_MAP_ELEM_V3_PACK, - file_obj.read(formats.PARAM_EXPR_MAP_ELEM_V3_SIZE), - ) - ) - symbol_key = type_keys.Value(elem_data.symbol_type) - - if symbol_key == type_keys.Value.PARAMETER: - symbol = _read_parameter(file_obj) - elif symbol_key == type_keys.Value.PARAMETER_VECTOR: - symbol = _read_parameter_vec(file_obj, vectors) - else: - raise exceptions.QpyError("Invalid parameter expression map type: %s" % symbol_key) - - elem_key = type_keys.Value(elem_data.type) - binary_data = file_obj.read(elem_data.size) - if elem_key == type_keys.Value.INTEGER: - value = struct.unpack("!q", binary_data) - elif elem_key == type_keys.Value.FLOAT: - value = struct.unpack("!d", binary_data) - elif elem_key == type_keys.Value.COMPLEX: - value = complex(*struct.unpack(formats.COMPLEX_PACK, binary_data)) - elif elem_key in (type_keys.Value.PARAMETER, type_keys.Value.PARAMETER_VECTOR): - value = symbol._symbol_expr - elif elem_key == type_keys.Value.PARAMETER_EXPRESSION: - value = common.data_from_binary( - binary_data, - _read_parameter_expression_v3, - vectors=vectors, - use_symengine=use_symengine, - ) - else: - raise exceptions.QpyError("Invalid parameter expression map type: %s" % elem_key) - symbol_map[symbol] = value - - return ParameterExpression(symbol_map, expr_) - - -def dumps_value(obj, *, index_map=None, use_symengine=False): # type: ignore[no-untyped-def] - """Serialize input value object. - Args: - obj (any): Arbitrary value object to serialize. - index_map (dict): Dictionary with two keys, "q" and "c". Each key has a value that is a - dictionary mapping :class:`.Qubit` or :class:`.Clbit` instances (respectively) to their - integer indices. - use_symengine (bool): If True, symbolic objects will be serialized using symengine's - native mechanism. This is a faster serialization alternative, but not supported in all - platforms. Please check that your target platform is supported by the symengine library - before setting this option, as it will be required by qpy to deserialize the payload. - Returns: - tuple: TypeKey and binary data. - Raises: - QpyError: Serializer for given format is not ready. - """ - type_key = type_keys.Value.assign(obj) - - if type_key == type_keys.Value.INTEGER: - binary_data = struct.pack("!q", obj) - elif type_key == type_keys.Value.FLOAT: - binary_data = struct.pack("!d", obj) - elif type_key == type_keys.Value.COMPLEX: - binary_data = struct.pack(formats.COMPLEX_PACK, obj.real, obj.imag) - elif type_key == type_keys.Value.NUMPY_OBJ: - binary_data = common.data_to_binary(obj, np.save) - elif type_key == type_keys.Value.STRING: - binary_data = obj.encode(common.ENCODE) - elif type_key in (type_keys.Value.NULL, type_keys.Value.CASE_DEFAULT): - binary_data = b"" - elif type_key == type_keys.Value.PARAMETER_VECTOR: - binary_data = common.data_to_binary(obj, _write_parameter_vec) - elif type_key == type_keys.Value.PARAMETER: - binary_data = common.data_to_binary(obj, _write_parameter) - elif type_key == type_keys.Value.PARAMETER_EXPRESSION: - binary_data = common.data_to_binary( - obj, _write_parameter_expression, use_symengine=use_symengine - ) # type: ignore[no-untyped-call] - elif type_key == type_keys.Value.EXPRESSION: - clbit_indices = {} if index_map is None else index_map["c"] - binary_data = common.data_to_binary(obj, _write_expr, clbit_indices=clbit_indices) - else: - raise exceptions.QpyError(f"Serialization for {type_key} is not implemented in value I/O.") - - return type_key, binary_data - - -def write_value(file_obj, obj, *, index_map=None, use_symengine=False): # type: ignore[no-untyped-def] - """Write a value to the file like object. - Args: - file_obj (File): A file like object to write data. - obj (any): Value to write. - index_map (dict): Dictionary with two keys, "q" and "c". - use_symengine (bool): If True, symbolic objects will be serialized using symengine's - native mechanism. This is a faster serialization alternative, but not supported in all - platforms. Please check that your target platform is supported by the symengine library - before setting this option, as it will be required by qpy to deserialize the payload. - """ - type_key, data = dumps_value(obj, index_map=index_map, use_symengine=use_symengine) - common.write_generic_typed_data(file_obj, type_key, data) - - -def loads_value( # type: ignore[no-untyped-def] - type_key, - binary_data, - version, - vectors, - *, - clbits=(), - cregs=None, - use_symengine=False, -) -> Any: - """Deserialize input binary data to value object. - Args: - type_key (ValueTypeKey): Type enum information. - binary_data (bytes): Data to deserialize. - version (int): QPY version. - vectors (dict): ParameterVector in current scope. - clbits (Sequence[Clbit]): Clbits in the current scope. - cregs (Mapping[str, ClassicalRegister]): Classical registers in the current scope. - use_symengine (bool): If True, symbolic objects will be de-serialized using symengine's - native mechanism. This is a faster serialization alternative, but not supported in all - platforms. Please check that your target platform is supported by the symengine library - before setting this option, as it will be required by qpy to deserialize the payload. - Returns: - any: Deserialized value object. - Raises: - QpyError: Serializer for given format is not ready. - """ - # pylint: disable=too-many-return-statements - - if isinstance(type_key, bytes): - type_key = type_keys.Value(type_key) - - if type_key == type_keys.Value.INTEGER: - return struct.unpack("!q", binary_data)[0] - if type_key == type_keys.Value.FLOAT: - return struct.unpack("!d", binary_data)[0] - if type_key == type_keys.Value.COMPLEX: - return complex(*struct.unpack(formats.COMPLEX_PACK, binary_data)) # type: ignore[attr-defined] - if type_key == type_keys.Value.NUMPY_OBJ: - return common.data_from_binary(binary_data, np.load) - if type_key == type_keys.Value.STRING: - return binary_data.decode(common.ENCODE) - if type_key == type_keys.Value.NULL: - return None - if type_key == type_keys.Value.CASE_DEFAULT: - return CASE_DEFAULT - if type_key == type_keys.Value.PARAMETER_VECTOR: - return common.data_from_binary(binary_data, _read_parameter_vec, vectors=vectors) - if type_key == type_keys.Value.PARAMETER: - return common.data_from_binary(binary_data, _read_parameter) - if type_key == type_keys.Value.PARAMETER_EXPRESSION: - if version < 3: - return common.data_from_binary(binary_data, _read_parameter_expression) - else: - return common.data_from_binary( - binary_data, - _read_parameter_expression_v3, - vectors=vectors, - use_symengine=use_symengine, - ) - if type_key == type_keys.Value.EXPRESSION: - return common.data_from_binary(binary_data, _read_expr, clbits=clbits, cregs=cregs or {}) - - raise exceptions.QpyError(f"Serialization for {type_key} is not implemented in value I/O.") - - -def read_value(file_obj, version, vectors, *, clbits=(), cregs=None, use_symengine=False): # type: ignore[no-untyped-def] - """Read a value from the file like object. - Args: - file_obj (File): A file like object to write data. - version (int): QPY version. - vectors (dict): ParameterVector in current scope. - clbits (Sequence[Clbit]): Clbits in the current scope. - cregs (Mapping[str, ClassicalRegister]): Classical registers in the current scope. - use_symengine (bool): If True, symbolic objects will be de-serialized using symengine's - native mechanism. This is a faster serialization alternative, but not supported in all - platforms. Please check that your target platform is supported by the symengine library - before setting this option, as it will be required by qpy to deserialize the payload. - Returns: - any: Deserialized value object. - """ - type_key, data = common.read_generic_typed_data(file_obj) - - return loads_value( - type_key, - data, - version, - vectors, - clbits=clbits, - cregs=cregs, - use_symengine=use_symengine, - ) diff --git a/qiskit_ibm_runtime/qpy/common.py b/qiskit_ibm_runtime/qpy/common.py deleted file mode 100644 index dce023f937..0000000000 --- a/qiskit_ibm_runtime/qpy/common.py +++ /dev/null @@ -1,310 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# 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=too-many-return-statements - -""" -Common functions across several serialization and deserialization modules. -""" - -import io -import struct - -from typing import Any -from . import formats - -QPY_VERSION = 10 -ENCODE = "utf8" - - -def read_generic_typed_data(file_obj): # type: ignore[no-untyped-def] - """Read a single data chunk from the file like object. - - Args: - file_obj (File): A file like object that contains the QPY binary data. - - Returns: - tuple: Tuple of type key binary and the bytes object of the single data. - """ - data = formats.INSTRUCTION_PARAM._make( - struct.unpack( - formats.INSTRUCTION_PARAM_PACK, - file_obj.read(formats.INSTRUCTION_PARAM_SIZE), - ) - ) - - return data.type, file_obj.read(data.size) - - -def read_sequence(file_obj, deserializer, **kwargs): # type: ignore[no-untyped-def] - """Read a sequence of data from the file like object. - - Args: - file_obj (File): A file like object that contains the QPY binary data. - deserializer (Callable): Deserializer callback that can handle input object type. - This must take type key and binary data of the element and return object. - **kwargs: Options set to the deserializer. - - Returns: - list: Deserialized object. - """ - sequence = [] - - data = formats.SEQUENCE._make( - struct.unpack(formats.SEQUENCE_PACK, file_obj.read(formats.SEQUENCE_SIZE)) - ) - for _ in range(data.num_elements): - type_key, datum_bytes = read_generic_typed_data(file_obj) - sequence.append(deserializer(type_key, datum_bytes, **kwargs)) - - return sequence - - -def read_mapping(file_obj, deserializer, **kwargs): # type: ignore[no-untyped-def] - """Read a mapping from the file like object. - - .. note:: - - This function must be used to make a binary data of mapping - which include QPY serialized values. - It's easier to use JSON serializer followed by encoding for standard data formats. - This only supports flat dictionary and key must be string. - - Args: - file_obj (File): A file like object that contains the QPY binary data. - deserializer (Callable): Deserializer callback that can handle mapping item. - This must take type key and binary data of the mapping value and return object. - **kwargs: Options set to the deserializer. - - Returns: - dict: Deserialized object. - """ - mapping = {} - - data = formats.SEQUENCE._make( - struct.unpack(formats.SEQUENCE_PACK, file_obj.read(formats.SEQUENCE_SIZE)) - ) - for _ in range(data.num_elements): - map_header = formats.MAP_ITEM._make( - struct.unpack(formats.MAP_ITEM_PACK, file_obj.read(formats.MAP_ITEM_SIZE)) - ) - key = file_obj.read(map_header.key_size).decode(ENCODE) - datum = deserializer(map_header.type, file_obj.read(map_header.size), **kwargs) - mapping[key] = datum - - return mapping - - -def read_type_key(file_obj): # type: ignore[no-untyped-def] - """Read a type key from the file like object. - - Args: - file_obj (File): A file like object that contains the QPY binary data. - - Returns: - bytes: Type key. - """ - key_size = struct.calcsize("!1c") - return struct.unpack("!1c", file_obj.read(key_size))[0] - - -def write_generic_typed_data(file_obj, type_key, data_binary): # type: ignore[no-untyped-def] - """Write statically typed binary data to the file like object. - - Args: - file_obj (File): A file like object to write data. - type_key (Enum): Object type of the data. - data_binary (bytes): Binary data to write. - """ - data_header = struct.pack(formats.INSTRUCTION_PARAM_PACK, type_key, len(data_binary)) - file_obj.write(data_header) - file_obj.write(data_binary) - - -def write_sequence(file_obj, sequence, serializer, **kwargs): # type: ignore[no-untyped-def] - """Write a sequence of data in the file like object. - - Args: - file_obj (File): A file like object to write data. - sequence (Sequence): Object to serialize. - serializer (Callable): Serializer callback that can handle input object type. - This must return type key and binary data of each element. - **kwargs: Options set to the serializer. - """ - num_elements = len(sequence) - - file_obj.write(struct.pack(formats.SEQUENCE_PACK, num_elements)) - for datum in sequence: - type_key, datum_bytes = serializer(datum, **kwargs) - write_generic_typed_data(file_obj, type_key, datum_bytes) - - -def write_mapping(file_obj, mapping, serializer, **kwargs): # type: ignore[no-untyped-def] - """Write a mapping in the file like object. - - .. note:: - - This function must be used to make a binary data of mapping - which include QPY serialized values. - It's easier to use JSON serializer followed by encoding for standard data formats. - This only supports flat dictionary and key must be string. - - Args: - file_obj (File): A file like object to write data. - mapping (Mapping): Object to serialize. - serializer (Callable): Serializer callback that can handle mapping item. - This must return type key and binary data of the mapping value. - **kwargs: Options set to the serializer. - """ - num_elements = len(mapping) - - file_obj.write(struct.pack(formats.SEQUENCE_PACK, num_elements)) - for key, datum in mapping.items(): - key_bytes = key.encode(ENCODE) - type_key, datum_bytes = serializer(datum, **kwargs) - item_header = struct.pack(formats.MAP_ITEM_PACK, len(key_bytes), type_key, len(datum_bytes)) - file_obj.write(item_header) - file_obj.write(key_bytes) - file_obj.write(datum_bytes) - - -def write_type_key(file_obj, type_key): # type: ignore[no-untyped-def] - """Write a type key in the file like object. - - Args: - file_obj (File): A file like object that contains the QPY binary data. - type_key (bytes): Type key to write. - """ - file_obj.write(struct.pack("!1c", type_key)) - - -def data_to_binary(obj, serializer, **kwargs): # type: ignore[no-untyped-def] - """Convert object into binary data with specified serializer. - - Args: - obj (any): Object to serialize. - serializer (Callable): Serializer callback that can handle input object type. - **kwargs: Options set to the serializer. - - Returns: - bytes: Binary data. - """ - with io.BytesIO() as container: - serializer(container, obj, **kwargs) - binary_data = container.getvalue() - - return binary_data - - -def sequence_to_binary(sequence, serializer, **kwargs): # type: ignore[no-untyped-def] - """Convert sequence into binary data with specified serializer. - - Args: - sequence (Sequence): Object to serialize. - serializer (Callable): Serializer callback that can handle input object type. - This must return type key and binary data of each element. - **kwargs: Options set to the serializer. - - Returns: - bytes: Binary data. - """ - with io.BytesIO() as container: - write_sequence(container, sequence, serializer, **kwargs) - binary_data = container.getvalue() - - return binary_data - - -def mapping_to_binary(mapping, serializer, **kwargs): # type: ignore[no-untyped-def] - """Convert mapping into binary data with specified serializer. - - .. note:: - - This function must be used to make a binary data of mapping - which include QPY serialized values. - It's easier to use JSON serializer followed by encoding for standard data formats. - This only supports flat dictionary and key must be string. - - Args: - mapping (Mapping): Object to serialize. - serializer (Callable): Serializer callback that can handle mapping item. - This must return type key and binary data of the mapping value. - **kwargs: Options set to the serializer. - - Returns: - bytes: Binary data. - """ - with io.BytesIO() as container: - write_mapping(container, mapping, serializer, **kwargs) - binary_data = container.getvalue() - - return binary_data - - -def data_from_binary(binary_data, deserializer, **kwargs) -> Any: # type: ignore[no-untyped-def] - """Load object from binary data with specified deserializer. - - Args: - binary_data (bytes): Binary data to deserialize. - deserializer (Callable): Deserializer callback that can handle input object type. - **kwargs: Options set to the deserializer. - - Returns: - any: Deserialized object. - """ - with io.BytesIO(binary_data) as container: - container.seek(0) - obj = deserializer(container, **kwargs) - return obj - - -def sequence_from_binary(binary_data, deserializer, **kwargs): # type: ignore[no-untyped-def] - """Load object from binary sequence with specified deserializer. - - Args: - binary_data (bytes): Binary data to deserialize. - deserializer (Callable): Deserializer callback that can handle input object type. - This must take type key and binary data of the element and return object. - **kwargs: Options set to the deserializer. - - Returns: - any: Deserialized sequence. - """ - with io.BytesIO(binary_data) as container: - sequence = read_sequence(container, deserializer, **kwargs) - - return sequence - - -def mapping_from_binary(binary_data, deserializer, **kwargs): # type: ignore[no-untyped-def] - """Load object from binary mapping with specified deserializer. - - .. note:: - - This function must be used to make a binary data of mapping - which include QPY serialized values. - It's easier to use JSON serializer followed by encoding for standard data formats. - This only supports flat dictionary and key must be string. - - Args: - binary_data (bytes): Binary data to deserialize. - deserializer (Callable): Deserializer callback that can handle mapping item. - This must take type key and binary data of the mapping value and return object. - **kwargs: Options set to the deserializer. - - Returns: - dict: Deserialized object. - """ - with io.BytesIO(binary_data) as container: - mapping = read_mapping(container, deserializer, **kwargs) - - return mapping diff --git a/qiskit_ibm_runtime/qpy/exceptions.py b/qiskit_ibm_runtime/qpy/exceptions.py deleted file mode 100644 index a537ed9a1d..0000000000 --- a/qiskit_ibm_runtime/qpy/exceptions.py +++ /dev/null @@ -1,29 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# 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. - -"""Exception for errors raised by the pulse module.""" -from typing import Any -from qiskit.qpy.exceptions import QpyError -from ..exceptions import IBMError - - -class IBMQpyError(QpyError, IBMError): - """Errors raised by the qpy module.""" - - def __init__(self, *message: Any): - """Set the error message.""" - super().__init__(*message) - self.message = " ".join(message) - - def __str__(self) -> str: - """Return the message.""" - return repr(self.message) diff --git a/qiskit_ibm_runtime/qpy/formats.py b/qiskit_ibm_runtime/qpy/formats.py deleted file mode 100644 index 133a931b41..0000000000 --- a/qiskit_ibm_runtime/qpy/formats.py +++ /dev/null @@ -1,374 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# 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 -# type: ignore[name-match] - -"""Binary format definition.""" - -import struct -from collections import namedtuple - -# FILE_HEADER_V10 -FILE_HEADER_V10 = namedtuple( - "FILE_HEADER", - [ - "preface", - "qpy_version", - "major_version", - "minor_version", - "patch_version", - "num_programs", - "symbolic_encoding", - ], -) -FILE_HEADER_V10_PACK = "!6sBBBBQc" -FILE_HEADER_V10_SIZE = struct.calcsize(FILE_HEADER_V10_PACK) - - -# FILE_HEADER -FILE_HEADER = namedtuple( - "FILE_HEADER", - [ - "preface", - "qpy_version", - "major_version", - "minor_version", - "patch_version", - "num_programs", - ], -) -FILE_HEADER_PACK = "!6sBBBBQ" -FILE_HEADER_SIZE = struct.calcsize(FILE_HEADER_PACK) - -# CIRCUIT_HEADER_V2 -CIRCUIT_HEADER_V2 = namedtuple( - "HEADER", - [ - "name_size", - "global_phase_type", - "global_phase_size", - "num_qubits", - "num_clbits", - "metadata_size", - "num_registers", - "num_instructions", - ], -) -CIRCUIT_HEADER_V2_PACK = "!H1cHIIQIQ" -CIRCUIT_HEADER_V2_SIZE = struct.calcsize(CIRCUIT_HEADER_V2_PACK) - -# CIRCUIT_HEADER -CIRCUIT_HEADER = namedtuple( - "HEADER", - [ - "name_size", - "global_phase", - "num_qubits", - "num_clbits", - "metadata_size", - "num_registers", - "num_instructions", - ], -) -CIRCUIT_HEADER_PACK = "!HdIIQIQ" -CIRCUIT_HEADER_SIZE = struct.calcsize(CIRCUIT_HEADER_PACK) - -# REGISTER -REGISTER_V4 = namedtuple("REGISTER", ["type", "standalone", "size", "name_size", "in_circuit"]) -REGISTER_V4_PACK = "!1c?IH?" -REGISTER_V4_SIZE = struct.calcsize(REGISTER_V4_PACK) - -REGISTER = namedtuple("REGISTER", ["type", "standalone", "size", "name_size"]) -REGISTER_PACK = "!1c?IH" -REGISTER_SIZE = struct.calcsize(REGISTER_PACK) - -# CIRCUIT_INSTRUCTION -CIRCUIT_INSTRUCTION = namedtuple( - "CIRCUIT_INSTRUCTION", - [ - "name_size", - "label_size", - "num_parameters", - "num_qargs", - "num_cargs", - "has_condition", - "condition_register_size", - "condition_value", - ], -) -CIRCUIT_INSTRUCTION_PACK = "!HHHII?Hq" -CIRCUIT_INSTRUCTION_SIZE = struct.calcsize(CIRCUIT_INSTRUCTION_PACK) - -# CIRCUIT_INSTRUCTION_V2 -CIRCUIT_INSTRUCTION_V2 = namedtuple( - "CIRCUIT_INSTRUCTION", - [ - "name_size", - "label_size", - "num_parameters", - "num_qargs", - "num_cargs", - "conditional_key", - "condition_register_size", - "condition_value", - "num_ctrl_qubits", - "ctrl_state", - ], -) -CIRCUIT_INSTRUCTION_V2_PACK = "!HHHIIBHqII" -CIRCUIT_INSTRUCTION_V2_SIZE = struct.calcsize(CIRCUIT_INSTRUCTION_V2_PACK) - - -# CIRCUIT_INSTRUCTION_ARG -CIRCUIT_INSTRUCTION_ARG = namedtuple("CIRCUIT_INSTRUCTION_ARG", ["type", "size"]) -CIRCUIT_INSTRUCTION_ARG_PACK = "!1cI" -CIRCUIT_INSTRUCTION_ARG_SIZE = struct.calcsize(CIRCUIT_INSTRUCTION_ARG_PACK) - -# SparsePauliOp List -SPARSE_PAULI_OP_LIST_ELEM = namedtuple("SPARSE_PAULI_OP_LIST_ELEMENT", ["size"]) -SPARSE_PAULI_OP_LIST_ELEM_PACK = "!Q" -SPARSE_PAULI_OP_LIST_ELEM_SIZE = struct.calcsize(SPARSE_PAULI_OP_LIST_ELEM_PACK) - -# Pauli Evolution Gate -PAULI_EVOLUTION_DEF = namedtuple( - "PAULI_EVOLUTION_DEF", - ["operator_size", "standalone_op", "time_type", "time_size", "synth_method_size"], -) -PAULI_EVOLUTION_DEF_PACK = "!Q?1cQQ" -PAULI_EVOLUTION_DEF_SIZE = struct.calcsize(PAULI_EVOLUTION_DEF_PACK) - -# CUSTOM_CIRCUIT_DEF_HEADER -CUSTOM_CIRCUIT_DEF_HEADER = namedtuple("CUSTOM_CIRCUIT_DEF_HEADER", ["size"]) -CUSTOM_CIRCUIT_DEF_HEADER_PACK = "!Q" -CUSTOM_CIRCUIT_DEF_HEADER_SIZE = struct.calcsize(CUSTOM_CIRCUIT_DEF_HEADER_PACK) - -# CUSTOM_CIRCUIT_INST_DEF_V2 -CUSTOM_CIRCUIT_INST_DEF_V2 = namedtuple( - "CUSTOM_CIRCUIT_INST_DEF", - [ - "gate_name_size", - "type", - "num_qubits", - "num_clbits", - "custom_definition", - "size", - "num_ctrl_qubits", - "ctrl_state", - "base_gate_size", - ], -) -CUSTOM_CIRCUIT_INST_DEF_V2_PACK = "!H1cII?QIIQ" -CUSTOM_CIRCUIT_INST_DEF_V2_SIZE = struct.calcsize(CUSTOM_CIRCUIT_INST_DEF_V2_PACK) - -# CUSTOM_CIRCUIT_INST_DEF -CUSTOM_CIRCUIT_INST_DEF = namedtuple( - "CUSTOM_CIRCUIT_INST_DEF", - ["gate_name_size", "type", "num_qubits", "num_clbits", "custom_definition", "size"], -) -CUSTOM_CIRCUIT_INST_DEF_PACK = "!H1cII?Q" -CUSTOM_CIRCUIT_INST_DEF_SIZE = struct.calcsize(CUSTOM_CIRCUIT_INST_DEF_PACK) - -# CALIBRATION -CALIBRATION = namedtuple("CALIBRATION", ["num_cals"]) -CALIBRATION_PACK = "!H" -CALIBRATION_SIZE = struct.calcsize(CALIBRATION_PACK) - -# CALIBRATION_DEF -CALIBRATION_DEF = namedtuple("CALIBRATION_DEF", ["name_size", "num_qubits", "num_params", "type"]) -CALIBRATION_DEF_PACK = "!HHH1c" -CALIBRATION_DEF_SIZE = struct.calcsize(CALIBRATION_DEF_PACK) - -# SCHEDULE_BLOCK binary format -SCHEDULE_BLOCK_HEADER = namedtuple( - "SCHEDULE_BLOCK", - [ - "name_size", - "metadata_size", - "num_elements", - ], -) -SCHEDULE_BLOCK_HEADER_PACK = "!HQH" -SCHEDULE_BLOCK_HEADER_SIZE = struct.calcsize(SCHEDULE_BLOCK_HEADER_PACK) - -# WAVEFORM binary format -WAVEFORM = namedtuple("WAVEFORM", ["epsilon", "data_size", "amp_limited"]) -WAVEFORM_PACK = "!fI?" -WAVEFORM_SIZE = struct.calcsize(WAVEFORM_PACK) - -# SYMBOLIC_PULSE -SYMBOLIC_PULSE = namedtuple( - "SYMBOLIC_PULSE", - [ - "type_size", - "envelope_size", - "constraints_size", - "valid_amp_conditions_size", - "amp_limited", - ], -) -SYMBOLIC_PULSE_PACK = "!HHHH?" -SYMBOLIC_PULSE_SIZE = struct.calcsize(SYMBOLIC_PULSE_PACK) - -# SYMBOLIC_PULSE_V2 -SYMBOLIC_PULSE_V2 = namedtuple( - "SYMBOLIC_PULSE", - [ - "class_name_size", - "type_size", - "envelope_size", - "constraints_size", - "valid_amp_conditions_size", - "amp_limited", - ], -) -SYMBOLIC_PULSE_PACK_V2 = "!HHHHH?" -SYMBOLIC_PULSE_SIZE_V2 = struct.calcsize(SYMBOLIC_PULSE_PACK_V2) - -# INSTRUCTION_PARAM -INSTRUCTION_PARAM = namedtuple("INSTRUCTION_PARAM", ["type", "size"]) -INSTRUCTION_PARAM_PACK = "!1cQ" -INSTRUCTION_PARAM_SIZE = struct.calcsize(INSTRUCTION_PARAM_PACK) - -# PARAMETER -PARAMETER = namedtuple("PARAMETER", ["name_size", "uuid"]) -PARAMETER_PACK = "!H16s" -PARAMETER_SIZE = struct.calcsize(PARAMETER_PACK) - -# COMPLEX -COMPLEX = namedtuple("COMPLEX", ["real", "imag"]) -COMPLEX_PACK = "!dd" -COMPLEX_SIZE = struct.calcsize(COMPLEX_PACK) - -# PARAMETER_VECTOR_ELEMENT -PARAMETER_VECTOR_ELEMENT = namedtuple( - "PARAMETER_VECTOR_ELEMENT", ["vector_name_size", "vector_size", "uuid", "index"] -) -PARAMETER_VECTOR_ELEMENT_PACK = "!HQ16sQ" -PARAMETER_VECTOR_ELEMENT_SIZE = struct.calcsize(PARAMETER_VECTOR_ELEMENT_PACK) - -# PARAMETER_EXPR -PARAMETER_EXPR = namedtuple("PARAMETER_EXPR", ["map_elements", "expr_size"]) -PARAMETER_EXPR_PACK = "!QQ" -PARAMETER_EXPR_SIZE = struct.calcsize(PARAMETER_EXPR_PACK) - -# PARAMETER_EXPR_MAP_ELEM_V3 -PARAM_EXPR_MAP_ELEM_V3 = namedtuple("PARAMETER_EXPR_MAP_ELEM", ["symbol_type", "type", "size"]) -PARAM_EXPR_MAP_ELEM_V3_PACK = "!ccQ" -PARAM_EXPR_MAP_ELEM_V3_SIZE = struct.calcsize(PARAM_EXPR_MAP_ELEM_V3_PACK) - -# PARAMETER_EXPR_MAP_ELEM -PARAM_EXPR_MAP_ELEM = namedtuple("PARAMETER_EXPR_MAP_ELEM", ["type", "size"]) -PARAM_EXPR_MAP_ELEM_PACK = "!cQ" -PARAM_EXPR_MAP_ELEM_SIZE = struct.calcsize(PARAM_EXPR_MAP_ELEM_PACK) - -# RANGE -RANGE = namedtuple("RANGE", ["start", "stop", "step"]) -RANGE_PACK = "!qqq" -RANGE_SIZE = struct.calcsize(RANGE_PACK) - -# SEQUENCE -SEQUENCE = namedtuple("SEQUENCE", ["num_elements"]) -SEQUENCE_PACK = "!Q" -SEQUENCE_SIZE = struct.calcsize(SEQUENCE_PACK) - -# MAP_ITEM -MAP_ITEM = namedtuple("MAP_ITEM", ["key_size", "type", "size"]) -MAP_ITEM_PACK = "!H1cH" -MAP_ITEM_SIZE = struct.calcsize(MAP_ITEM_PACK) - -LAYOUT_V2 = namedtuple( - "LAYOUT", - [ - "exists", - "initial_layout_size", - "input_mapping_size", - "final_layout_size", - "extra_registers", - "input_qubit_count", - ], -) -LAYOUT_V2_PACK = "!?iiiIi" -LAYOUT_V2_SIZE = struct.calcsize(LAYOUT_V2_PACK) - -LAYOUT = namedtuple( - "LAYOUT", - [ - "exists", - "initial_layout_size", - "input_mapping_size", - "final_layout_size", - "extra_registers", - ], -) -LAYOUT_PACK = "!?iiiI" -LAYOUT_SIZE = struct.calcsize(LAYOUT_PACK) - -INITIAL_LAYOUT_BIT = namedtuple("INITIAL_LAYOUT_BIT", ["index", "register_size"]) -INITIAL_LAYOUT_BIT_PACK = "!ii" -INITIAL_LAYOUT_BIT_SIZE = struct.calcsize(INITIAL_LAYOUT_BIT_PACK) - - -# EXPRESSION - -EXPRESSION_DISCRIMINATOR_SIZE = 1 - -EXPRESSION_CAST = namedtuple("EXPRESSION_CAST", ["implicit"]) -EXPRESSION_CAST_PACK = "!?" -EXPRESSION_CAST_SIZE = struct.calcsize(EXPRESSION_CAST_PACK) - -EXPRESSION_UNARY = namedtuple("EXPRESSION_UNARY", ["opcode"]) -EXPRESSION_UNARY_PACK = "!B" -EXPRESSION_UNARY_SIZE = struct.calcsize(EXPRESSION_UNARY_PACK) - -EXPRESSION_BINARY = namedtuple("EXPRESSION_BINARY", ["opcode"]) -EXPRESSION_BINARY_PACK = "!B" -EXPRESSION_BINARY_SIZE = struct.calcsize(EXPRESSION_BINARY_PACK) - - -# EXPR_TYPE - -EXPR_TYPE_DISCRIMINATOR_SIZE = 1 - -EXPR_TYPE_BOOL = namedtuple("EXPR_TYPE_BOOL", []) -EXPR_TYPE_BOOL_PACK = "!" -EXPR_TYPE_BOOL_SIZE = struct.calcsize(EXPR_TYPE_BOOL_PACK) - -EXPR_TYPE_UINT = namedtuple("EXPR_TYPE_UINT", ["width"]) # type: ignore[no-untyped-def] -EXPR_TYPE_UINT_PACK = "!L" -EXPR_TYPE_UINT_SIZE = struct.calcsize(EXPR_TYPE_UINT_PACK) - - -# EXPR_VAR - -EXPR_VAR_DISCRIMINATOR_SIZE = 1 - -EXPR_VAR_CLBIT = namedtuple("EXPR_VAR_CLBIT", ["index"]) -EXPR_VAR_CLBIT_PACK = "!L" -EXPR_VAR_CLBIT_SIZE = struct.calcsize(EXPR_VAR_CLBIT_PACK) - -EXPR_VAR_REGISTER = namedtuple("EXPR_VAR_REGISTER", ["reg_name_size"]) -EXPR_VAR_REGISTER_PACK = "!H" -EXPR_VAR_REGISTER_SIZE = struct.calcsize(EXPR_VAR_REGISTER_PACK) - - -# EXPR_VALUE - -EXPR_VALUE_DISCRIMINATOR_SIZE = 1 - -EXPR_VALUE_BOOL = namedtuple("EXPR_VALUE_BOOL", ["value"]) -EXPR_VALUE_BOOL_PACK = "!?" -EXPR_VALUE_BOOL_SIZE = struct.calcsize(EXPR_VALUE_BOOL_PACK) - -EXPR_VALUE_INT = namedtuple("EXPR_VALUE_INT", ["num_bytes"]) -EXPR_VALUE_INT_PACK = "!B" -EXPR_VALUE_INT_SIZE = struct.calcsize(EXPR_VALUE_INT_PACK) diff --git a/qiskit_ibm_runtime/qpy/interface.py b/qiskit_ibm_runtime/qpy/interface.py deleted file mode 100644 index 6a77697d65..0000000000 --- a/qiskit_ibm_runtime/qpy/interface.py +++ /dev/null @@ -1,312 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. -"""User interface of qpy serializer.""" - -from json import JSONEncoder, JSONDecoder -from typing import Union, List, BinaryIO, Type, Optional -from collections.abc import Iterable -import struct -import warnings -import re - -from qiskit.circuit import QuantumCircuit -from qiskit.pulse import ScheduleBlock -from qiskit.exceptions import QiskitError -from qiskit.version import __version__ - -from . import formats, common, binary_io, type_keys -from .exceptions import QpyError - - -# pylint: disable=invalid-name -QPY_SUPPORTED_TYPES = Union[QuantumCircuit, ScheduleBlock] - - -# This version pattern is taken from the pypa packaging project: -# https://github.com/pypa/packaging/blob/21.3/packaging/version.py#L223-L254 -# which is dual licensed Apache 2.0 and BSD see the source for the original -# authors and other details -VERSION_PATTERN = ( - "^" - + r""" - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P
                                          # pre-release
-            [-_\.]?
-            (?P(a|b|c|rc|alpha|beta|pre|preview))
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-        (?P                                         # post release
-            (?:-(?P[0-9]+))
-            |
-            (?:
-                [-_\.]?
-                (?Ppost|rev|r)
-                [-_\.]?
-                (?P[0-9]+)?
-            )
-        )?
-        (?P                                          # dev release
-            [-_\.]?
-            (?Pdev)
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-    )
-    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
-"""
-    + "$"
-)
-VERSION_PATTERN_REGEX = re.compile(VERSION_PATTERN, re.VERBOSE | re.IGNORECASE)
-
-
-def dump(  # type: ignore[no-untyped-def]
-    programs: Union[List[QPY_SUPPORTED_TYPES], QPY_SUPPORTED_TYPES],
-    file_obj: BinaryIO,
-    metadata_serializer: Optional[Type[JSONEncoder]] = None,
-    use_symengine: bool = False,
-):
-    """Write QPY binary data to a file
-
-    This function is used to save a circuit to a file for later use or transfer
-    between machines. The QPY format is backwards compatible and can be
-    loaded with future versions of Qiskit.
-
-    For example:
-
-    .. code-block:: python
-
-        from qiskit.circuit import QuantumCircuit
-        from qiskit import qpy
-
-        qc = QuantumCircuit(2, name='Bell', metadata={'test': True})
-        qc.h(0)
-        qc.cx(0, 1)
-        qc.measure_all()
-
-    from this you can write the qpy data to a file:
-
-    .. code-block:: python
-
-        with open('bell.qpy', 'wb') as fd:
-            qpy.dump(qc, fd)
-
-    or a gzip compressed file:
-
-    .. code-block:: python
-
-        import gzip
-
-        with gzip.open('bell.qpy.gz', 'wb') as fd:
-            qpy.dump(qc, fd)
-
-    Which will save the qpy serialized circuit to the provided file.
-
-    Args:
-        programs: QPY supported object(s) to store in the specified file like object.
-            QPY supports :class:`.QuantumCircuit` and :class:`.ScheduleBlock`.
-            Different data types must be separately serialized.
-        file_obj: The file like object to write the QPY data too
-        metadata_serializer: An optional JSONEncoder class that
-            will be passed the ``.metadata`` attribute for each program in ``programs`` and will be
-            used as the ``cls`` kwarg on the `json.dump()`` call to JSON serialize that dictionary.
-        use_symengine: If True, all objects containing symbolic expressions will be serialized
-            using symengine's native mechanism. This is a faster serialization alternative,
-            but not supported in all platforms. Please check that your target platform is supported
-            by the symengine library before setting this option, as it will be required by qpy to
-            deserialize the payload. For this reason, the option defaults to False.
-
-    Raises:
-        QpyError: When multiple data format is mixed in the output.
-        TypeError: When invalid data type is input.
-    """
-    if not isinstance(programs, Iterable):
-        programs = [programs]
-
-    program_types = set()
-    for program in programs:
-        program_types.add(type(program))
-
-    if len(program_types) > 1:
-        raise QpyError(
-            "Input programs contain multiple data types. "
-            "Different data type must be serialized separately."
-        )
-    program_type = next(iter(program_types))
-
-    if issubclass(program_type, QuantumCircuit):
-        type_key = type_keys.Program.CIRCUIT
-        writer = binary_io.write_circuit
-    elif program_type is ScheduleBlock:
-        type_key = type_keys.Program.SCHEDULE_BLOCK
-        writer = binary_io.write_schedule_block
-    else:
-        raise TypeError(f"'{program_type}' is not supported data type.")
-
-    version_match = VERSION_PATTERN_REGEX.search(__version__)
-    version_parts = [int(x) for x in version_match.group("release").split(".")]
-    encoding = type_keys.SymExprEncoding.assign(use_symengine)  # type: ignore[no-untyped-call]
-    header = struct.pack(
-        formats.FILE_HEADER_V10_PACK,  # type: ignore[attr-defined]
-        b"QISKIT",
-        common.QPY_VERSION,
-        version_parts[0],
-        version_parts[1],
-        version_parts[2],
-        len(programs),  # type: ignore[arg-type]
-        encoding,
-    )
-    file_obj.write(header)
-    common.write_type_key(file_obj, type_key)  # type: ignore[no-untyped-call]
-
-    for program in programs:
-        writer(  # type: ignore[no-untyped-call]
-            file_obj,
-            program,
-            metadata_serializer=metadata_serializer,
-            use_symengine=use_symengine,
-        )
-
-
-def load(  # type: ignore[no-untyped-def]
-    file_obj: BinaryIO,
-    metadata_deserializer: Optional[Type[JSONDecoder]] = None,
-) -> List[QPY_SUPPORTED_TYPES]:
-    """Load a QPY binary file
-
-    This function is used to load a serialized QPY Qiskit program file and create
-    :class:`~qiskit.circuit.QuantumCircuit` objects or
-    :class:`~qiskit.pulse.schedule.ScheduleBlock` objects from its contents.
-    For example:
-
-    .. code-block:: python
-
-        from qiskit import qpy
-
-        with open('bell.qpy', 'rb') as fd:
-            circuits = qpy.load(fd)
-
-    or with a gzip compressed file:
-
-    .. code-block:: python
-
-        import gzip
-        from qiskit import qpy
-
-        with gzip.open('bell.qpy.gz', 'rb') as fd:
-            circuits = qpy.load(fd)
-
-    which will read the contents of the qpy and return a list of
-    :class:`~qiskit.circuit.QuantumCircuit` objects or
-    :class:`~qiskit.pulse.schedule.ScheduleBlock` objects from the file.
-
-    Args:
-        file_obj: A file like object that contains the QPY binary
-            data for a circuit or pulse schedule.
-        metadata_deserializer: An optional JSONDecoder class
-            that will be used for the ``cls`` kwarg on the internal
-            ``json.load`` call used to deserialize the JSON payload used for
-            the ``.metadata`` attribute for any programs in the QPY file.
-            If this is not specified the circuit metadata will
-            be parsed as JSON with the stdlib ``json.load()`` function using
-            the default ``JSONDecoder`` class.
-
-    Returns:
-        The list of Qiskit programs contained in the QPY data.
-        A list is always returned, even if there is only 1 program in the QPY data.
-
-    Raises:
-        QiskitError: if ``file_obj`` is not a valid QPY file
-        TypeError: When invalid data type is loaded.
-    """
-
-    # identify file header version
-    version = struct.unpack("!6sB", file_obj.read(7))[1]
-    file_obj.seek(0)
-
-    if version > common.QPY_VERSION:
-        raise QiskitError(
-            f"The QPY format version being read, {version}, isn't supported by "
-            "this Qiskit version. Please upgrade your version of Qiskit to load this QPY payload"
-        )
-
-    if version < 10:
-        data = formats.FILE_HEADER._make(  # type: ignore[attr-defined]
-            struct.unpack(
-                formats.FILE_HEADER_PACK,  # type: ignore[attr-defined]
-                file_obj.read(formats.FILE_HEADER_SIZE),  # type: ignore[attr-defined]
-            )
-        )
-    else:
-        data = formats.FILE_HEADER_V10._make(  # type: ignore[attr-defined]
-            struct.unpack(
-                formats.FILE_HEADER_V10_PACK,  # type: ignore[attr-defined]
-                file_obj.read(formats.FILE_HEADER_V10_SIZE),  # type: ignore[attr-defined]
-            )
-        )
-    if data.preface.decode(common.ENCODE) != "QISKIT":
-        raise QiskitError("Input file is not a valid QPY file")
-    version_match = VERSION_PATTERN_REGEX.search(__version__)
-    env_qiskit_version = [int(x) for x in version_match.group("release").split(".")]
-
-    qiskit_version = (data.major_version, data.minor_version, data.patch_version)
-    # pylint: disable=too-many-boolean-expressions
-    if (
-        env_qiskit_version[0] < qiskit_version[0]
-        or (
-            env_qiskit_version[0] == qiskit_version[0] and qiskit_version[1] > env_qiskit_version[1]
-        )
-        or (
-            env_qiskit_version[0] == qiskit_version[0]
-            and qiskit_version[1] == env_qiskit_version[1]
-            and qiskit_version[2] > env_qiskit_version[2]
-        )
-    ):
-        warnings.warn(
-            "The qiskit version used to generate the provided QPY "
-            "file, %s, is newer than the current qiskit version %s. "
-            "This may result in an error if the QPY file uses "
-            "instructions not present in this current qiskit "
-            "version" % (".".join([str(x) for x in qiskit_version]), __version__)
-        )
-
-    if data.qpy_version < 5:
-        type_key = type_keys.Program.CIRCUIT
-    else:
-        type_key = common.read_type_key(file_obj)  # type: ignore[no-untyped-call]
-
-    if type_key == type_keys.Program.CIRCUIT:
-        loader = binary_io.read_circuit
-    elif type_key == type_keys.Program.SCHEDULE_BLOCK:
-        loader = binary_io.read_schedule_block
-    else:
-        raise TypeError(f"Invalid payload format data kind '{type_key}'.")
-
-    if data.qpy_version < 10:
-        use_symengine = False
-    else:
-        use_symengine = data.symbolic_encoding == type_keys.SymExprEncoding.SYMENGINE
-
-    programs = []
-    for _ in range(data.num_programs):
-        programs.append(
-            loader(  # type: ignore[no-untyped-call]
-                file_obj,
-                data.qpy_version,
-                metadata_deserializer=metadata_deserializer,
-                use_symengine=use_symengine,
-            )
-        )
-    return programs
diff --git a/qiskit_ibm_runtime/qpy/type_keys.py b/qiskit_ibm_runtime/qpy/type_keys.py
deleted file mode 100644
index 4c5b424d5e..0000000000
--- a/qiskit_ibm_runtime/qpy/type_keys.py
+++ /dev/null
@@ -1,540 +0,0 @@
-# This code is part of Qiskit.
-#
-# (C) Copyright IBM 2022.
-#
-# 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=too-many-return-statements
-
-"""
-QPY Type keys for several namespace.
-"""
-
-from abc import abstractmethod
-from enum import Enum, IntEnum
-
-import numpy as np
-
-from qiskit.circuit import (
-    Gate,
-    Instruction,
-    QuantumCircuit,
-    ControlledGate,
-    CASE_DEFAULT,
-    Clbit,
-    ClassicalRegister,
-)
-from qiskit.circuit.library import PauliEvolutionGate
-from qiskit.circuit.parameter import Parameter
-from qiskit.circuit.classical import expr, types
-from qiskit.circuit.parameterexpression import ParameterExpression
-from qiskit.circuit.parametervector import ParameterVectorElement
-from qiskit.pulse.channels import (
-    Channel,
-    DriveChannel,
-    MeasureChannel,
-    ControlChannel,
-    AcquireChannel,
-    MemorySlot,
-    RegisterSlot,
-)
-from qiskit.pulse.configuration import Discriminator, Kernel
-from qiskit.pulse.instructions import (
-    Acquire,
-    Play,
-    Delay,
-    SetFrequency,
-    ShiftFrequency,
-    SetPhase,
-    ShiftPhase,
-    RelativeBarrier,
-    TimeBlockade,
-    Reference,
-)
-from qiskit.pulse.library import Waveform, SymbolicPulse
-from qiskit.pulse.schedule import ScheduleBlock
-from qiskit.pulse.transforms.alignments import (
-    AlignLeft,
-    AlignRight,
-    AlignSequential,
-    AlignEquispaced,
-)
-from . import exceptions
-
-
-class TypeKeyBase(bytes, Enum):
-    """Abstract baseclass for type key Enums."""
-
-    @classmethod
-    @abstractmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        """Assign type key to given object.
-        Args:
-            obj (any): Arbitrary object to evaluate.
-        Returns:
-            TypeKey: Corresponding key object.
-        """
-        pass
-
-    @classmethod
-    @abstractmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        """Get a class from given type key.
-        Args:
-            type_key (bytes): Object type key.
-        Returns:
-            any: Corresponding class.
-        """
-        pass
-
-
-class Value(TypeKeyBase):
-    """Type key enum for value object."""
-
-    INTEGER = b"i"
-    FLOAT = b"f"
-    COMPLEX = b"c"
-    NUMPY_OBJ = b"n"
-    PARAMETER = b"p"
-    PARAMETER_VECTOR = b"v"
-    PARAMETER_EXPRESSION = b"e"
-    STRING = b"s"
-    NULL = b"z"
-    EXPRESSION = b"x"
-    CASE_DEFAULT = b"d"
-    REGISTER = b"R"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, int):
-            return cls.INTEGER
-        if isinstance(obj, float):
-            return cls.FLOAT
-        if isinstance(obj, complex):
-            return cls.COMPLEX
-        if isinstance(obj, (np.integer, np.floating, np.complexfloating, np.ndarray)):
-            return cls.NUMPY_OBJ
-        if isinstance(obj, ParameterVectorElement):
-            return cls.PARAMETER_VECTOR
-        if isinstance(obj, Parameter):
-            return cls.PARAMETER
-        if isinstance(obj, ParameterExpression):
-            return cls.PARAMETER_EXPRESSION
-        if isinstance(obj, str):
-            return cls.STRING
-        if isinstance(obj, (Clbit, ClassicalRegister)):
-            return cls.REGISTER
-        if obj is None:
-            return cls.NULL
-        if obj is CASE_DEFAULT:
-            return cls.CASE_DEFAULT
-        if isinstance(obj, expr.Expr):
-            return cls.EXPRESSION
-
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class Condition(IntEnum):
-    """Type keys for the ``conditional_key`` field of the INSTRUCTION struct."""
-
-    # This class is deliberately raw integers and not in terms of ASCII characters for backwards
-    # compatiblity in the form as an old Boolean value was expanded; `NONE` and `TWO_TUPLE` must
-    # have the enumeration values 0 and 1.
-
-    NONE = 0
-    TWO_TUPLE = 1
-    EXPRESSION = 2
-
-
-class Container(TypeKeyBase):
-    """Typle key enum for container-like object."""
-
-    RANGE = b"r"
-    TUPLE = b"t"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, range):
-            return cls.RANGE
-        if isinstance(obj, tuple):
-            return cls.TUPLE
-
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class CircuitInstruction(TypeKeyBase):
-    """Type key enum for circuit instruction object."""
-
-    INSTRUCTION = b"i"
-    GATE = b"g"
-    PAULI_EVOL_GATE = b"p"
-    CONTROLLED_GATE = b"c"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, PauliEvolutionGate):
-            return cls.PAULI_EVOL_GATE
-        if isinstance(obj, ControlledGate):
-            return cls.CONTROLLED_GATE
-        if isinstance(obj, Gate):
-            return cls.GATE
-        if isinstance(obj, Instruction):
-            return cls.INSTRUCTION
-
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class ScheduleAlignment(TypeKeyBase):
-    """Type key enum for schedule block alignment context object."""
-
-    LEFT = b"l"
-    RIGHT = b"r"
-    SEQUENTIAL = b"s"
-    EQUISPACED = b"e"
-
-    # AlignFunc is not serializable due to the callable in context parameter
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, AlignLeft):
-            return cls.LEFT
-        if isinstance(obj, AlignRight):
-            return cls.RIGHT
-        if isinstance(obj, AlignSequential):
-            return cls.SEQUENTIAL
-        if isinstance(obj, AlignEquispaced):
-            return cls.EQUISPACED
-
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        if type_key == cls.LEFT:
-            return AlignLeft
-        if type_key == cls.RIGHT:
-            return AlignRight
-        if type_key == cls.SEQUENTIAL:
-            return AlignSequential
-        if type_key == cls.EQUISPACED:
-            return AlignEquispaced
-
-        raise exceptions.QpyError(
-            f"A class corresponding to type key '{type_key}' is not found in {cls.__name__} namespace."
-        )
-
-
-class ScheduleInstruction(TypeKeyBase):
-    """Type key enum for schedule instruction object."""
-
-    ACQUIRE = b"a"
-    PLAY = b"p"
-    DELAY = b"d"
-    SET_FREQUENCY = b"f"
-    SHIFT_FREQUENCY = b"g"
-    SET_PHASE = b"q"
-    SHIFT_PHASE = b"r"
-    BARRIER = b"b"
-    TIME_BLOCKADE = b"t"
-    REFERENCE = b"y"
-
-    # 's' is reserved by ScheduleBlock, i.e. block can be nested as an element.
-    # Call instructon is not supported by QPY.
-    # This instruction has been excluded from ScheduleBlock instructions with
-    # qiskit-terra/#8005 and new instruction Reference will be added instead.
-    # Call is only applied to Schedule which is not supported by QPY.
-    # Also snapshot is not suppored because of its limited usecase.
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, Acquire):
-            return cls.ACQUIRE
-        if isinstance(obj, Play):
-            return cls.PLAY
-        if isinstance(obj, Delay):
-            return cls.DELAY
-        if isinstance(obj, SetFrequency):
-            return cls.SET_FREQUENCY
-        if isinstance(obj, ShiftFrequency):
-            return cls.SHIFT_FREQUENCY
-        if isinstance(obj, SetPhase):
-            return cls.SET_PHASE
-        if isinstance(obj, ShiftPhase):
-            return cls.SHIFT_PHASE
-        if isinstance(obj, RelativeBarrier):
-            return cls.BARRIER
-        if isinstance(obj, TimeBlockade):
-            return cls.TIME_BLOCKADE
-        if isinstance(obj, Reference):
-            return cls.REFERENCE
-
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        if type_key == cls.ACQUIRE:
-            return Acquire
-        if type_key == cls.PLAY:
-            return Play
-        if type_key == cls.DELAY:
-            return Delay
-        if type_key == cls.SET_FREQUENCY:
-            return SetFrequency
-        if type_key == cls.SHIFT_FREQUENCY:
-            return ShiftFrequency
-        if type_key == cls.SET_PHASE:
-            return SetPhase
-        if type_key == cls.SHIFT_PHASE:
-            return ShiftPhase
-        if type_key == cls.BARRIER:
-            return RelativeBarrier
-        if type_key == cls.TIME_BLOCKADE:
-            return TimeBlockade
-        if type_key == cls.REFERENCE:
-            return Reference
-
-        raise exceptions.QpyError(
-            f"A class corresponding to type key '{type_key}' is not found in {cls.__name__} namespace."
-        )
-
-
-class ScheduleOperand(TypeKeyBase):
-    """Type key enum for schedule instruction operand object."""
-
-    WAVEFORM = b"w"
-    SYMBOLIC_PULSE = b"s"
-    CHANNEL = b"c"
-    KERNEL = b"k"
-    DISCRIMINATOR = b"d"
-
-    # We need to have own string type definition for operands of schedule instruction.
-    # Note that string type is already defined in the Value namespace,
-    # but its key "s" conflicts with the SYMBOLIC_PULSE in the ScheduleOperand namespace.
-    # New in QPY version 7.
-    OPERAND_STR = b"o"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, Waveform):
-            return cls.WAVEFORM
-        if isinstance(obj, SymbolicPulse):
-            return cls.SYMBOLIC_PULSE
-        if isinstance(obj, Channel):
-            return cls.CHANNEL
-        if isinstance(obj, str):
-            return cls.OPERAND_STR
-        if isinstance(obj, Kernel):
-            return cls.KERNEL
-        if isinstance(obj, Discriminator):
-            return cls.DISCRIMINATOR
-
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class ScheduleChannel(TypeKeyBase):
-    """Type key enum for schedule channel object."""
-
-    DRIVE = b"d"
-    CONTROL = b"c"
-    MEASURE = b"m"
-    ACQURE = b"a"
-    MEM_SLOT = b"e"
-    REG_SLOT = b"r"
-
-    # SnapShot channel is not defined because of its limited usecase.
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, DriveChannel):
-            return cls.DRIVE
-        if isinstance(obj, ControlChannel):
-            return cls.CONTROL
-        if isinstance(obj, MeasureChannel):
-            return cls.MEASURE
-        if isinstance(obj, AcquireChannel):
-            return cls.ACQURE
-        if isinstance(obj, MemorySlot):
-            return cls.MEM_SLOT
-        if isinstance(obj, RegisterSlot):
-            return cls.REG_SLOT
-
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        if type_key == cls.DRIVE:
-            return DriveChannel
-        if type_key == cls.CONTROL:
-            return ControlChannel
-        if type_key == cls.MEASURE:
-            return MeasureChannel
-        if type_key == cls.ACQURE:
-            return AcquireChannel
-        if type_key == cls.MEM_SLOT:
-            return MemorySlot
-        if type_key == cls.REG_SLOT:
-            return RegisterSlot
-
-        raise exceptions.QpyError(
-            f"A class corresponding to type key '{type_key}' is not found in {cls.__name__} namespace."
-        )
-
-
-class Program(TypeKeyBase):
-    """Typle key enum for program that QPY supports."""
-
-    CIRCUIT = b"q"
-    SCHEDULE_BLOCK = b"s"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, QuantumCircuit):
-            return cls.CIRCUIT
-        if isinstance(obj, ScheduleBlock):
-            return cls.SCHEDULE_BLOCK
-
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class Expression(TypeKeyBase):
-    """Type keys for the ``EXPRESSION`` QPY item."""
-
-    VAR = b"x"
-    VALUE = b"v"
-    CAST = b"c"
-    UNARY = b"u"
-    BINARY = b"b"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if (
-            isinstance(obj, expr.Expr)
-            and (key := getattr(cls, obj.__class__.__name__.upper(), None)) is not None
-        ):
-            return key
-        raise exceptions.QpyError(f"Object '{obj}' is not supported in {cls.__name__} namespace.")
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class ExprType(TypeKeyBase):
-    """Type keys for the ``EXPR_TYPE`` QPY item."""
-
-    BOOL = b"b"
-    UINT = b"u"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if (
-            isinstance(obj, types.Type)
-            and (key := getattr(cls, obj.__class__.__name__.upper(), None)) is not None
-        ):
-            return key
-        raise exceptions.QpyError(f"Object '{obj}' is not supported in {cls.__name__} namespace.")
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class ExprVar(TypeKeyBase):
-    """Type keys for the ``EXPR_VAR`` QPY item."""
-
-    CLBIT = b"C"
-    REGISTER = b"R"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, Clbit):
-            return cls.CLBIT
-        if isinstance(obj, ClassicalRegister):
-            return cls.REGISTER
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class ExprValue(TypeKeyBase):
-    """Type keys for the ``EXPR_VALUE`` QPY item."""
-
-    BOOL = b"b"
-    INT = b"i"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if isinstance(obj, bool):
-            return cls.BOOL
-        if isinstance(obj, int):
-            return cls.INT
-        raise exceptions.QpyError(
-            f"Object type '{type(obj)}' is not supported in {cls.__name__} namespace."
-        )
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
-
-
-class SymExprEncoding(TypeKeyBase):
-    """Type keys for the symbolic encoding field in the file header."""
-
-    SYMPY = b"p"
-    SYMENGINE = b"e"
-
-    @classmethod
-    def assign(cls, obj):  # type: ignore[no-untyped-def]
-        if obj is True:
-            return cls.SYMENGINE
-        else:
-            return cls.SYMPY
-
-    @classmethod
-    def retrieve(cls, type_key):  # type: ignore[no-untyped-def]
-        raise NotImplementedError
diff --git a/qiskit_ibm_runtime/utils/json.py b/qiskit_ibm_runtime/utils/json.py
index b31246a593..1c2e7c2878 100644
--- a/qiskit_ibm_runtime/utils/json.py
+++ b/qiskit_ibm_runtime/utils/json.py
@@ -55,17 +55,15 @@
 from qiskit.circuit.parametertable import ParameterView
 from qiskit.result import Result
 from qiskit.version import __version__ as _terra_version_string
-from qiskit.qpy import load
 from qiskit.utils import optionals
-
-from ..qpy import (
-    _write_parameter,
+from qiskit.qpy import (
     _write_parameter_expression,
     _read_parameter_expression,
     _read_parameter_expression_v3,
-    _read_parameter,
+    load,
     dump,
 )
+from qiskit.qpy.binary_io.value import _write_parameter, _read_parameter
 
 _TERRA_VERSION = tuple(
     int(x) for x in re.match(r"\d+\.\d+\.\d", _terra_version_string).group(0).split(".")[:3]
@@ -217,10 +215,15 @@ def default(self, obj: Any) -> Any:  # pylint: disable=arguments-differ
         if hasattr(obj, "to_json"):
             return {"__type__": "to_json", "__value__": obj.to_json()}
         if isinstance(obj, QuantumCircuit):
+            kwargs = {"use_symengine": optionals.HAS_SYMENGINE}
+            if _TERRA_VERSION[0] >= 1:
+                # NOTE: This can be updated only after the server side has
+                # updated to a newer qiskit version.
+                kwargs["version"] = 10
             value = _serialize_and_encode(
                 data=obj,
                 serializer=lambda buff, data: dump(
-                    data, buff, RuntimeEncoder, use_symengine=optionals.HAS_SYMENGINE
+                    data, buff, RuntimeEncoder, **kwargs
                 ),  # type: ignore[no-untyped-call]
             )
             return {"__type__": "QuantumCircuit", "__value__": value}
@@ -236,18 +239,26 @@ def default(self, obj: Any) -> Any:  # pylint: disable=arguments-differ
                 data=obj,
                 serializer=_write_parameter_expression,
                 compress=False,
+                use_symengine=optionals.HAS_SYMENGINE,
             )
             return {"__type__": "ParameterExpression", "__value__": value}
         if isinstance(obj, ParameterView):
             return obj.data
         if isinstance(obj, Instruction):
+            kwargs = {"use_symengine": optionals.HAS_SYMENGINE}
+            if _TERRA_VERSION[0] >= 1:
+                # NOTE: This can be updated only after the server side has
+                # updated to a newer qiskit version.
+                kwargs["version"] = 10
             # Append instruction to empty circuit
             quantum_register = QuantumRegister(obj.num_qubits)
             quantum_circuit = QuantumCircuit(quantum_register)
             quantum_circuit.append(obj, quantum_register)
             value = _serialize_and_encode(
                 data=quantum_circuit,
-                serializer=lambda buff, data: dump(data, buff),  # type: ignore[no-untyped-call]
+                serializer=lambda buff, data: dump(
+                    data, buff, **kwargs
+                ),  # type: ignore[no-untyped-call]
             )
             return {"__type__": "Instruction", "__value__": value}
         if HAS_AER and isinstance(obj, qiskit_aer.noise.NoiseModel):