Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Commit

Permalink
QPY Terra 0.25 updates (#684)
Browse files Browse the repository at this point in the history
* copy terra qpy 0.25 updates

* fix black

* fix lint

* fix lint/typing again
  • Loading branch information
kt474 authored Jul 31, 2023
1 parent e53a67b commit be70612
Show file tree
Hide file tree
Showing 9 changed files with 578 additions and 47 deletions.
59 changes: 39 additions & 20 deletions qiskit_ibm_provider/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from qiskit import circuit as circuit_mod
from qiskit import extensions
from qiskit.circuit import library, controlflow, CircuitInstruction
from qiskit.circuit.classical import expr
from qiskit.circuit.classicalregister import ClassicalRegister, Clbit
from qiskit.circuit.gate import Gate
from qiskit.circuit.controlledgate import ControlledGate
Expand Down Expand Up @@ -157,7 +158,14 @@ def _loads_instruction_parameter( # type: ignore[no-untyped-def]
data_bytes.decode(common.ENCODE), circuit, registers
)
else:
param = value.loads_value(type_key, data_bytes, version, vectors)
param = value.loads_value(
type_key,
data_bytes,
version,
vectors,
clbits=circuit.clbits,
cregs=registers["c"],
)

return param

Expand Down Expand Up @@ -195,13 +203,18 @@ def _read_instruction( # type: ignore[no-untyped-def]
qargs = []
cargs = []
params = []
condition_tuple = None
if instruction.has_condition:
# If register name prefixed with null character it's a clbit index for single bit condition.
condition_tuple = (
condition = None
if (version < 5 and instruction.has_condition) or (
version >= 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"]
)
if circuit is not None:
qubit_indices = dict(enumerate(circuit.qubits))
clbit_indices = dict(enumerate(circuit.clbits))
Expand Down Expand Up @@ -245,7 +258,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
inst_obj = _parse_custom_operation(
custom_operations, gate_name, params, version, vectors, registers
)
inst_obj.condition = condition_tuple
inst_obj.condition = condition
if instruction.label_size > 0:
inst_obj.label = label
if circuit is None:
Expand All @@ -256,7 +269,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
inst_obj = _parse_custom_operation(
custom_operations, gate_name, params, version, vectors, registers
)
inst_obj.condition = condition_tuple
inst_obj.condition = condition
if instruction.label_size > 0:
inst_obj.label = label
if circuit is None:
Expand All @@ -277,7 +290,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
raise AttributeError("Invalid instruction type: %s" % gate_name)

if gate_name in {"IfElseOp", "WhileLoopOp"}:
gate = gate_class(condition_tuple, *params)
gate = gate_class(condition, *params)
elif version >= 5 and issubclass(gate_class, ControlledGate):
if gate_name in {
"MCPhaseGate",
Expand All @@ -292,7 +305,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
gate = gate_class(*params)
gate.num_ctrl_qubits = instruction.num_ctrl_qubits
gate.ctrl_state = instruction.ctrl_state
gate.condition = condition_tuple
gate.condition = condition
else:
if gate_name in {
"Initialize",
Expand All @@ -309,7 +322,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
elif gate_name in {"BreakLoopOp", "ContinueLoopOp"}:
params = [len(qargs), len(cargs)]
gate = gate_class(*params)
gate.condition = condition_tuple
gate.condition = condition
if instruction.label_size > 0:
gate.label = label
if circuit is None:
Expand Down Expand Up @@ -546,7 +559,7 @@ def _dumps_instruction_parameter(param, index_map): # type: ignore[no-untyped-d
type_key = type_keys.Value.REGISTER
data_bytes = _dumps_register(param, index_map)
else:
type_key, data_bytes = value.dumps_value(param)
type_key, data_bytes = value.dumps_value(param, index_map=index_map)

return type_key, data_bytes

Expand Down Expand Up @@ -580,15 +593,18 @@ def _write_instruction( # type: ignore[no-untyped-def]
custom_operations[gate_class_name] = instruction.operation
custom_operations_list.append(gate_class_name)

has_condition = False
condition_type = type_keys.Condition.NONE
condition_register = b""
condition_value = 0
if getattr(instruction.operation, "condition", None):
has_condition = True
condition_register = _dumps_register(
instruction.operation.condition[0], index_map
)
condition_value = int(instruction.operation.condition[1])
if (op_condition := getattr(instruction.operation, "condition", None)) is not None:
if isinstance(op_condition, expr.Expr):
condition_type = type_keys.Condition.EXPRESSION
else:
condition_type = type_keys.Condition.TWO_TUPLE
condition_register = _dumps_register(
instruction.operation.condition[0], index_map
)
condition_value = int(instruction.operation.condition[1])

gate_class_name = gate_class_name.encode(common.ENCODE)
label = getattr(instruction.operation, "label")
Expand Down Expand Up @@ -616,7 +632,7 @@ def _write_instruction( # type: ignore[no-untyped-def]
len(instruction_params),
instruction.operation.num_qubits,
instruction.operation.num_clbits,
has_condition,
condition_type.value,
len(condition_register),
condition_value,
num_ctrl_qubits,
Expand All @@ -625,7 +641,10 @@ def _write_instruction( # type: ignore[no-untyped-def]
file_obj.write(instruction_raw)
file_obj.write(gate_class_name)
file_obj.write(label_raw)
file_obj.write(condition_register)
if condition_type is type_keys.Condition.EXPRESSION:
value.write_value(file_obj, op_condition, index_map=index_map)
else:
file_obj.write(condition_register)
# Encode instruciton args
for qbit in instruction.qubits:
instruction_arg_raw = struct.pack(
Expand Down
101 changes: 101 additions & 0 deletions qiskit_ibm_provider/qpy/binary_io/schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
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
Expand Down Expand Up @@ -62,6 +64,52 @@ def _read_waveform(file_obj, version): # type: ignore[no-untyped-def]
)


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): # type: ignore[no-untyped-def]
from sympy import parse_expr # pylint: disable=import-outside-toplevel

Expand Down Expand Up @@ -235,6 +283,7 @@ def _read_alignment_context(file_obj, version): # type: ignore[no-untyped-def]
return instance


# pylint: disable=too-many-return-statements
def _loads_operand(type_key, data_bytes, version): # type: ignore[no-untyped-def]
if type_key == type_keys.ScheduleOperand.WAVEFORM:
return common.data_from_binary(data_bytes, _read_waveform, version=version)
Expand All @@ -251,6 +300,18 @@ def _loads_operand(type_key, data_bytes, version): # type: ignore[no-untyped-de
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, {})

Expand Down Expand Up @@ -314,6 +375,40 @@ def _write_waveform(file_obj, data): # type: ignore[no-untyped-def]
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): # type: ignore[no-untyped-def]
from sympy import srepr, sympify # pylint: disable=import-outside-toplevel

Expand Down Expand Up @@ -378,6 +473,12 @@ def _dumps_operand(operand): # type: ignore[no-untyped-def]
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)

Expand Down
Loading

0 comments on commit be70612

Please sign in to comment.