Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions qiskit/circuit/qpy_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@
_write_parameter_expression,
_read_parameter_expression,
_read_parameter_expression_v3,
_read_parameter_expression_v6,
)
1 change: 1 addition & 0 deletions qiskit/qpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -954,4 +954,5 @@ class if it's defined in Qiskit. Otherwise it falls back to the custom
_write_parameter_expression,
_read_parameter_expression,
_read_parameter_expression_v3,
_read_parameter_expression_v6,
)
1 change: 1 addition & 0 deletions qiskit/qpy/binary_io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
_write_parameter_expression,
_read_parameter_expression,
_read_parameter_expression_v3,
_read_parameter_expression_v6,
)

from .circuits import (
Expand Down
126 changes: 92 additions & 34 deletions qiskit/qpy/binary_io/schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"""Read and write schedule and schedule instructions."""
import json
import struct
import zlib

import numpy as np

Expand Down Expand Up @@ -55,29 +54,30 @@ def _read_waveform(file_obj, version):
)


def _loads_symbolic_expr(expr_bytes):
from sympy import parse_expr

if expr_bytes == b"":
return None

expr_txt = zlib.decompress(expr_bytes).decode(common.ENCODE)
expr = parse_expr(expr_txt)

if _optional.HAS_SYMENGINE:
from symengine import sympify

return sympify(expr)
return expr


def _read_symbolic_pulse(file_obj, version):
header = formats.SYMBOLIC_PULSE._make(
struct.unpack(
formats.SYMBOLIC_PULSE_PACK,
file_obj.read(formats.SYMBOLIC_PULSE_SIZE),
)
)

def _loads_symbolic_expr(expr_bytes):
from sympy import parse_expr
import zlib

if expr_bytes == b"":
return None

expr_txt = zlib.decompress(expr_bytes).decode(common.ENCODE)
expr = parse_expr(expr_txt)

if _optional.HAS_SYMENGINE:
from symengine import sympify

return sympify(expr)
return expr

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))
Expand Down Expand Up @@ -119,6 +119,69 @@ def _read_symbolic_pulse(file_obj, version):
return instance


def _read_symbolic_pulse_v6(file_obj, version):
header = formats.SYMBOLIC_PULSE_V6._make(
struct.unpack(
formats.SYMBOLIC_PULSE_V6_PACK,
file_obj.read(formats.SYMBOLIC_PULSE_V6_SIZE),
)
)
pulse_type = file_obj.read(header.type_size).decode(common.ENCODE)
envelope = value.loads_value(
type_key=header.envelope_type,
binary_data=file_obj.read(header.envelope_size),
version=version,
vectors={},
)
constraints = value.loads_value(
type_key=header.constraints_type,
binary_data=file_obj.read(header.constraints_size),
version=version,
vectors={},
)
valid_amp_conditions = value.loads_value(
type_key=header.valid_amp_conditions_type,
binary_data=file_obj.read(header.valid_amp_conditions_size),
version=version,
vectors={},
)
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, {})

# TODO remove this and merge subclasses into a single kind of SymbolicPulse
# We need some refactoring of our codebase,
# mainly removal of isinstance check and name access with self.__class__.__name__.
if pulse_type == "Gaussian":
pulse_cls = library.Gaussian
elif pulse_type == "GaussianSquare":
pulse_cls = library.GaussianSquare
elif pulse_type == "Drag":
pulse_cls = library.Drag
elif pulse_type == "Constant":
pulse_cls = library.Constant
else:
pulse_cls = library.SymbolicPulse

# Skip calling constructor to absorb signature mismatch in subclass.
instance = object.__new__(pulse_cls)
instance.duration = duration
instance.name = name
instance._limit_amplitude = header.amp_limited
instance._pulse_type = pulse_type
instance._params = parameters
instance._envelope = envelope
instance._constraints = constraints
instance._valid_amp_conditions = valid_amp_conditions

return instance


def _read_alignment_context(file_obj, version):
type_key = common.read_type_key(file_obj)

Expand All @@ -140,7 +203,9 @@ def _loads_operand(type_key, data_bytes, version):
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:
return common.data_from_binary(data_bytes, _read_symbolic_pulse, version=version)
if version < 6:
return common.data_from_binary(data_bytes, _read_symbolic_pulse, version=version)
return common.data_from_binary(data_bytes, _read_symbolic_pulse_v6, version=version)
if type_key == type_keys.ScheduleOperand.CHANNEL:
return common.data_from_binary(data_bytes, _read_channel, version=version)

Expand Down Expand Up @@ -188,35 +253,28 @@ def _write_waveform(file_obj, data):
value.write_value(file_obj, data.name)


def _dumps_symbolic_expr(expr):
from sympy import srepr, sympify

if expr is None:
return b""

expr_bytes = srepr(sympify(expr)).encode(common.ENCODE)
return zlib.compress(expr_bytes)


def _write_symbolic_pulse(file_obj, data):
pulse_type_bytes = data.pulse_type.encode(common.ENCODE)
envelope_bytes = _dumps_symbolic_expr(data.envelope)
constraints_bytes = _dumps_symbolic_expr(data.constraints)
valid_amp_conditions_bytes = _dumps_symbolic_expr(data.valid_amp_conditions)
envelope_type, envelope_bytes = value.dumps_value(data.envelope)
constraints_type, constraints_bytes = value.dumps_value(data.constraints)
valid_amp_conds_type, valid_amp_conds_bytes = value.dumps_value(data.valid_amp_conditions)

header_bytes = struct.pack(
formats.SYMBOLIC_PULSE_PACK,
formats.SYMBOLIC_PULSE_V6_PACK,
len(pulse_type_bytes),
envelope_type,
len(envelope_bytes),
constraints_type,
len(constraints_bytes),
len(valid_amp_conditions_bytes),
valid_amp_conds_type,
len(valid_amp_conds_bytes),
data.limit_amplitude,
)
file_obj.write(header_bytes)
file_obj.write(pulse_type_bytes)
file_obj.write(envelope_bytes)
file_obj.write(constraints_bytes)
file_obj.write(valid_amp_conditions_bytes)
file_obj.write(valid_amp_conds_bytes)
common.write_mapping(
file_obj,
mapping=data._params,
Expand Down
101 changes: 84 additions & 17 deletions qiskit/qpy/binary_io/value.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,9 @@ def _write_parameter_vec(file_obj, obj):


def _write_parameter_expression(file_obj, obj):
from sympy import srepr, sympify

expr_bytes = srepr(sympify(obj._symbol_expr)).encode(common.ENCODE)
expr_type, expr_bytes = dumps_value(obj._symbol_expr)
param_expr_header_raw = struct.pack(
formats.PARAMETER_EXPR_PACK, len(obj._parameter_symbols), len(expr_bytes)
formats.PARAMETER_EXPR_V6_PACK, len(obj._parameter_symbols), expr_type, len(expr_bytes)
)
file_obj.write(param_expr_header_raw)
file_obj.write(expr_bytes)
Expand Down Expand Up @@ -82,8 +80,8 @@ def _write_parameter_expression(file_obj, obj):


def _read_parameter(file_obj):
data = formats.PARAMETER(
*struct.unpack(formats.PARAMETER_PACK, file_obj.read(formats.PARAMETER_SIZE))
data = formats.PARAMETER._make(
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)
Expand All @@ -93,8 +91,8 @@ def _read_parameter(file_obj):


def _read_parameter_vec(file_obj, vectors):
data = formats.PARAMETER_VECTOR_ELEMENT(
*struct.unpack(
data = formats.PARAMETER_VECTOR_ELEMENT._make(
struct.unpack(
formats.PARAMETER_VECTOR_ELEMENT_PACK,
file_obj.read(formats.PARAMETER_VECTOR_ELEMENT_SIZE),
),
Expand All @@ -114,8 +112,8 @@ def _read_parameter_vec(file_obj, vectors):


def _read_parameter_expression(file_obj):
data = formats.PARAMETER_EXPR(
*struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE))
data = formats.PARAMETER_EXPR._make(
struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE))
)
from sympy.parsing.sympy_parser import parse_expr

Expand All @@ -127,8 +125,8 @@ def _read_parameter_expression(file_obj):
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(
elem_data = formats.PARAM_EXPR_MAP_ELEM._make(
struct.unpack(
formats.PARAM_EXPR_MAP_ELEM_PACK,
file_obj.read(formats.PARAM_EXPR_MAP_ELEM_SIZE),
)
Expand All @@ -155,8 +153,8 @@ def _read_parameter_expression(file_obj):


def _read_parameter_expression_v3(file_obj, vectors):
data = formats.PARAMETER_EXPR(
*struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE))
data = formats.PARAMETER_EXPR._make(
struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE))
)
from sympy.parsing.sympy_parser import parse_expr

Expand All @@ -168,8 +166,8 @@ def _read_parameter_expression_v3(file_obj, vectors):
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_V3(
*struct.unpack(
elem_data = formats.PARAM_EXPR_MAP_ELEM_V3._make(
struct.unpack(
formats.PARAM_EXPR_MAP_ELEM_V3_PACK,
file_obj.read(formats.PARAM_EXPR_MAP_ELEM_V3_SIZE),
)
Expand Down Expand Up @@ -204,6 +202,54 @@ def _read_parameter_expression_v3(file_obj, vectors):
return ParameterExpression(symbol_map, expr)


def _read_parameter_expression_v6(file_obj, version, vectors):
data = formats.PARAMETER_EXPR_V6._make(
struct.unpack(formats.PARAMETER_EXPR_V6_PACK, file_obj.read(formats.PARAMETER_EXPR_V6_SIZE))
)
expr = loads_value(
type_key=data.expr_type,
binary_data=file_obj.read(data.expr_size),
version=version,
vectors=vectors,
)
symbol_map = {}
for _ in range(data.map_elements):
elem_data = formats.PARAM_EXPR_MAP_ELEM_V3._make(
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_v6, version=version, vectors=vectors
)
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):
"""Serialize input value object.

Expand Down Expand Up @@ -236,6 +282,13 @@ def dumps_value(obj):
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)
elif type_key == type_keys.Value.SYMPY_EXPR:
from sympy import srepr
binary_data = srepr(obj).encode(common.ENCODE)
elif type_key == type_keys.Value.SYMENGINE_EXPR:
import zlib
# This is implemented in C++, which is faster than serializing through sympy.
binary_data = zlib.compress(obj.__reduce__()[1][0])
else:
raise exceptions.QpyError(f"Serialization for {type_key} is not implemented in value I/O.")

Expand Down Expand Up @@ -292,10 +345,24 @@ def loads_value(type_key, binary_data, version, vectors):
if type_key == type_keys.Value.PARAMETER_EXPRESSION:
if version < 3:
return common.data_from_binary(binary_data, _read_parameter_expression)
else:
if version < 6:
return common.data_from_binary(
binary_data, _read_parameter_expression_v3, vectors=vectors
)
return common.data_from_binary(
binary_data, _read_parameter_expression_v6, version=version, vectors=vectors
)
if type_key == type_keys.Value.SYMPY_EXPR:
from sympy import parse_expr
return parse_expr(binary_data.decode(common.ENCODE))
if type_key == type_keys.Value.SYMENGINE_EXPR:
if not _optional.HAS_SYMENGINE:
raise exceptions.QpyError(
"This data is serialized with symengine but symengine is not installed."
)
from symengine.lib.symengine_wrapper import load_basic
import zlib
return load_basic(zlib.decompress(binary_data))

raise exceptions.QpyError(f"Serialization for {type_key} is not implemented in value I/O.")

Expand Down
2 changes: 1 addition & 1 deletion qiskit/qpy/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from qiskit.qpy import formats

QPY_VERSION = 5
QPY_VERSION = 6
ENCODE = "utf8"


Expand Down
Loading