diff --git a/qiskit/circuit/parameter.py b/qiskit/circuit/parameter.py index c7a8228dd463..8df6f86c8372 100644 --- a/qiskit/circuit/parameter.py +++ b/qiskit/circuit/parameter.py @@ -87,6 +87,7 @@ def __init__( self._hash = hash((self._parameter_keys, self._symbol_expr)) self._parameter_symbols = {self: symbol} self._name_map = None + self._qpy_replay = [] def assign(self, parameter, value): if parameter != self: @@ -172,3 +173,4 @@ def __setstate__(self, state): self._hash = hash((self._parameter_keys, self._symbol_expr)) self._parameter_symbols = {self: self._symbol_expr} self._name_map = None + self._qpy_replay = [] diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 16b691480d26..1cc1155896dc 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -14,6 +14,9 @@ """ from __future__ import annotations + +from dataclasses import dataclass +from enum import IntEnum from typing import Callable, Union import numbers @@ -30,12 +33,79 @@ ParameterValueType = Union["ParameterExpression", float] +class _OPCode(IntEnum): + ADD = 0 + SUB = 1 + MUL = 2 + DIV = 3 + POW = 4 + SIN = 5 + COS = 6 + TAN = 7 + ASIN = 8 + ACOS = 9 + EXP = 10 + LOG = 11 + SIGN = 12 + DERIV = 13 + CONJ = 14 + SUBSTITUTE = 15 + ABS = 16 + ATAN = 17 + + +_OP_CODE_MAP = ( + "__add__", + "__sub__", + "__mul__", + "__truediv__", + "__pow__", + "sin", + "cos", + "tan", + "arcsin", + "arccos", + "exp", + "log", + "sign", + "gradient", + "conjugate", + "subs", + "abs", + "arctan", +) + + +def op_code_to_method(op_code: _OPCode): + """Return the method name for a given op_code.""" + return _OP_CODE_MAP[op_code] + + +@dataclass +class _INSTRUCTION: + op: _OPCode + lhs: ParameterValueType + rhs: ParameterValueType | None = None + + +@dataclass +class _SUBS: + binds: dict + op: _OPCode = _OPCode.SUBSTITUTE + + class ParameterExpression: """ParameterExpression class to enable creating expressions of Parameters.""" - __slots__ = ["_parameter_symbols", "_parameter_keys", "_symbol_expr", "_name_map"] + __slots__ = [ + "_parameter_symbols", + "_parameter_keys", + "_symbol_expr", + "_name_map", + "_qpy_replay", + ] - def __init__(self, symbol_map: dict, expr): + def __init__(self, symbol_map: dict, expr, *, _qpy_replay=None): """Create a new :class:`ParameterExpression`. Not intended to be called directly, but to be instantiated via operations @@ -54,6 +124,10 @@ def __init__(self, symbol_map: dict, expr): self._parameter_keys = frozenset(p._hash_key() for p in self._parameter_symbols) self._symbol_expr = expr self._name_map: dict | None = None + if _qpy_replay is not None: + self._qpy_replay = _qpy_replay + else: + self._qpy_replay = [] @property def parameters(self) -> set: @@ -69,8 +143,11 @@ def _names(self) -> dict: def conjugate(self) -> "ParameterExpression": """Return the conjugate.""" + new_op = _INSTRUCTION(_OPCode.CONJ, self) + new_replay = self._qpy_replay.copy() + new_replay.append(new_op) conjugated = ParameterExpression( - self._parameter_symbols, symengine.conjugate(self._symbol_expr) + self._parameter_symbols, symengine.conjugate(self._symbol_expr), _qpy_replay=new_replay ) return conjugated @@ -117,6 +194,7 @@ def bind( self._raise_if_passed_unknown_parameters(parameter_values.keys()) self._raise_if_passed_nan(parameter_values) + new_op = _SUBS(parameter_values) symbol_values = {} for parameter, value in parameter_values.items(): if (param_expr := self._parameter_symbols.get(parameter)) is not None: @@ -143,7 +221,12 @@ def bind( f"(Expression: {self}, Bindings: {parameter_values})." ) - return ParameterExpression(free_parameter_symbols, bound_symbol_expr) + new_replay = self._qpy_replay.copy() + new_replay.append(new_op) + + return ParameterExpression( + free_parameter_symbols, bound_symbol_expr, _qpy_replay=new_replay + ) def subs( self, parameter_map: dict, allow_unknown_parameters: bool = False @@ -175,6 +258,7 @@ def subs( for p in replacement_expr.parameters } self._raise_if_parameter_names_conflict(inbound_names, parameter_map.keys()) + new_op = _SUBS(parameter_map) # Include existing parameters in self not set to be replaced. new_parameter_symbols = { @@ -192,8 +276,12 @@ def subs( new_parameter_symbols[p] = symbol_type(p.name) substituted_symbol_expr = self._symbol_expr.subs(symbol_map) + new_replay = self._qpy_replay.copy() + new_replay.append(new_op) - return ParameterExpression(new_parameter_symbols, substituted_symbol_expr) + return ParameterExpression( + new_parameter_symbols, substituted_symbol_expr, _qpy_replay=new_replay + ) def _raise_if_passed_unknown_parameters(self, parameters): unknown_parameters = parameters - self.parameters @@ -231,7 +319,11 @@ def _raise_if_parameter_names_conflict(self, inbound_parameters, outbound_parame ) def _apply_operation( - self, operation: Callable, other: ParameterValueType, reflected: bool = False + self, + operation: Callable, + other: ParameterValueType, + reflected: bool = False, + op_code: _OPCode = None, ) -> "ParameterExpression": """Base method implementing math operations between Parameters and either a constant or a second ParameterExpression. @@ -253,7 +345,6 @@ def _apply_operation( A new expression describing the result of the operation. """ self_expr = self._symbol_expr - if isinstance(other, ParameterExpression): self._raise_if_parameter_names_conflict(other._names) parameter_symbols = {**self._parameter_symbols, **other._parameter_symbols} @@ -266,10 +357,14 @@ def _apply_operation( if reflected: expr = operation(other_expr, self_expr) + new_op = _INSTRUCTION(op_code, other, self) else: expr = operation(self_expr, other_expr) + new_op = _INSTRUCTION(op_code, self, other) + new_replay = self._qpy_replay.copy() + new_replay.append(new_op) - out_expr = ParameterExpression(parameter_symbols, expr) + out_expr = ParameterExpression(parameter_symbols, expr, _qpy_replay=new_replay) out_expr._name_map = self._names.copy() if isinstance(other, ParameterExpression): out_expr._names.update(other._names.copy()) @@ -313,81 +408,86 @@ def gradient(self, param) -> Union["ParameterExpression", complex]: return float(expr_grad) def __add__(self, other): - return self._apply_operation(operator.add, other) + return self._apply_operation(operator.add, other, op_code=_OPCode.ADD) def __radd__(self, other): - return self._apply_operation(operator.add, other, reflected=True) + return self._apply_operation(operator.add, other, reflected=True, op_code=_OPCode.ADD) def __sub__(self, other): - return self._apply_operation(operator.sub, other) + return self._apply_operation(operator.sub, other, op_code=_OPCode.SUB) def __rsub__(self, other): - return self._apply_operation(operator.sub, other, reflected=True) + return self._apply_operation(operator.sub, other, reflected=True, op_code=_OPCode.SUB) def __mul__(self, other): - return self._apply_operation(operator.mul, other) + return self._apply_operation(operator.mul, other, op_code=_OPCode.MUL) def __pos__(self): - return self._apply_operation(operator.mul, 1) + return self._apply_operation(operator.mul, 1, op_code=_OPCode.MUL) def __neg__(self): - return self._apply_operation(operator.mul, -1) + return self._apply_operation(operator.mul, -1, op_code=_OPCode.MUL) def __rmul__(self, other): - return self._apply_operation(operator.mul, other, reflected=True) + return self._apply_operation(operator.mul, other, reflected=True, op_code=_OPCode.MUL) def __truediv__(self, other): if other == 0: raise ZeroDivisionError("Division of a ParameterExpression by zero.") - return self._apply_operation(operator.truediv, other) + return self._apply_operation(operator.truediv, other, op_code=_OPCode.DIV) def __rtruediv__(self, other): - return self._apply_operation(operator.truediv, other, reflected=True) + return self._apply_operation(operator.truediv, other, reflected=True, op_code=_OPCode.DIV) def __pow__(self, other): - return self._apply_operation(pow, other) + return self._apply_operation(pow, other, op_code=_OPCode.POW) def __rpow__(self, other): - return self._apply_operation(pow, other, reflected=True) - - def _call(self, ufunc): - return ParameterExpression(self._parameter_symbols, ufunc(self._symbol_expr)) + return self._apply_operation(pow, other, reflected=True, op_code=_OPCode.POW) + + def _call(self, ufunc, op_code): + new_op = _INSTRUCTION(op_code, self) + new_replay = self._qpy_replay.copy() + new_replay.append(new_op) + return ParameterExpression( + self._parameter_symbols, ufunc(self._symbol_expr), _qpy_replay=new_replay + ) def sin(self): """Sine of a ParameterExpression""" - return self._call(symengine.sin) + return self._call(symengine.sin, op_code=_OPCode.SIN) def cos(self): """Cosine of a ParameterExpression""" - return self._call(symengine.cos) + return self._call(symengine.cos, op_code=_OPCode.COS) def tan(self): """Tangent of a ParameterExpression""" - return self._call(symengine.tan) + return self._call(symengine.tan, op_code=_OPCode.TAN) def arcsin(self): """Arcsin of a ParameterExpression""" - return self._call(symengine.asin) + return self._call(symengine.asin, op_code=_OPCode.ASIN) def arccos(self): """Arccos of a ParameterExpression""" - return self._call(symengine.acos) + return self._call(symengine.acos, op_code=_OPCode.ACOS) def arctan(self): """Arctan of a ParameterExpression""" - return self._call(symengine.atan) + return self._call(symengine.atan, op_code=_OPCode.ATAN) def exp(self): """Exponential of a ParameterExpression""" - return self._call(symengine.exp) + return self._call(symengine.exp, op_code=_OPCode.EXP) def log(self): """Logarithm of a ParameterExpression""" - return self._call(symengine.log) + return self._call(symengine.log, op_code=_OPCode.LOG) def sign(self): """Sign of a ParameterExpression""" - return self._call(symengine.sign) + return self._call(symengine.sign, op_code=_OPCode.SIGN) def __repr__(self): return f"{self.__class__.__name__}({str(self)})" @@ -455,7 +555,7 @@ def __deepcopy__(self, memo=None): def __abs__(self): """Absolute of a ParameterExpression""" - return self._call(symengine.Abs) + return self._call(symengine.Abs, _OPCode.ABS) def abs(self): """Absolute of a ParameterExpression""" diff --git a/qiskit/qpy/binary_io/value.py b/qiskit/qpy/binary_io/value.py index 5b82e14d15cd..819be0161374 100644 --- a/qiskit/qpy/binary_io/value.py +++ b/qiskit/qpy/binary_io/value.py @@ -15,6 +15,7 @@ from __future__ import annotations import collections.abc +import io import struct import uuid @@ -25,7 +26,12 @@ 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.parameterexpression import ( + ParameterExpression, + op_code_to_method, + _OPCode, + _SUBS, +) from qiskit.circuit.parametervector import ParameterVector, ParameterVectorElement from qiskit.qpy import common, formats, exceptions, type_keys @@ -50,20 +56,136 @@ def _write_parameter_vec(file_obj, obj): file_obj.write(name_bytes) -def _write_parameter_expression(file_obj, obj, use_symengine, *, version): - if use_symengine: - expr_bytes = obj._symbol_expr.__reduce__()[1][0] +def _encode_replay_entry(inst, expression_tracking, file_obj, version, side=False): + inst_type = None + inst_data = None + if inst is None: + inst_type = "n" + inst_data = b"\x00" + elif isinstance(inst, Parameter): + inst_type = "p" + inst_data = inst.uuid.bytes + elif isinstance(inst, complex): + inst_type = "c" + inst_data = struct.pack("!dd", inst.real, inst.imag) + elif isinstance(inst, (float, int)): + inst_type = "f" + inst_data = struct.pack("!Qd", 0, inst) + elif isinstance(inst, ParameterExpression): + if inst not in expression_tracking: + if not side: + entry = struct.pack( + formats.PARAM_EXPR_ELEM_V4_PACK, + 255, + "s".encode("utf8"), + b"\x00", + "n".encode("utf8"), + b"\x00", + ) + else: + entry = struct.pack( + formats.PARAM_EXPR_ELEM_V4_PACK, + 255, + "n".encode("utf8"), + b"\x00", + "s".encode("utf8"), + b"\x00", + ) + + file_obj.write(entry) + _write_parameter_expression_v13(file_obj, inst, version) + if not side: + entry = struct.pack( + formats.PARAM_EXPR_ELEM_V4_PACK, + 255, + "e".encode("utf8"), + b"\x00", + "n".encode("utf8"), + b"\x00", + ) + else: + entry = struct.pack( + formats.PARAM_EXPR_ELEM_V4_PACK, + 255, + "n".encode("utf8"), + b"\x00", + "e".encode("utf8"), + b"\x00", + ) + inst_type = "n" + inst_data = b"\x00" + else: + inst_type = "n" + inst_data = b"\x00" else: - from sympy import srepr, sympify + raise exceptions.QpyError("Invalid parameter expression type") + return inst_type, inst_data + - expr_bytes = srepr(sympify(obj._symbol_expr)).encode(common.ENCODE) +def _encode_replay_subs(subs, file_obj, version): + with io.BytesIO() as mapping_buf: + subs_dict = {k.name: v for k, v in subs.binds.items()} + common.write_mapping( + mapping_buf, mapping=subs_dict, serializer=dumps_value, version=version + ) + data = mapping_buf.getvalue() + entry = struct.pack( + formats.PARAM_EXPR_ELEM_V4_PACK, + subs.op, + "u".encode("utf8"), + struct.pack("!QQ", len(data), 0), + "n".encode("utf8"), + b"\x00", + ) + file_obj.write(entry) + file_obj.write(data) + return subs.binds + + +def _write_parameter_expression_v13(file_obj, obj, version): + expression_tracking = { + obj, + } + symbol_map = {} + for inst in obj._qpy_replay: + if isinstance(inst, _SUBS): + symbol_map.update(_encode_replay_subs(inst, file_obj, version)) + continue + lhs_type, lhs = _encode_replay_entry(inst.lhs, expression_tracking, file_obj, version) + rhs_type, rhs = _encode_replay_entry(inst.rhs, expression_tracking, file_obj, version, True) + entry = struct.pack( + formats.PARAM_EXPR_ELEM_V4_PACK, + inst.op, + lhs_type.encode("utf8"), + lhs, + rhs_type.encode("utf8"), + rhs, + ) + file_obj.write(entry) + return symbol_map + + +def _write_parameter_expression(file_obj, obj, use_symengine, *, version): + extra_symbols = None + if version < 13: + if use_symengine: + expr_bytes = obj._symbol_expr.__reduce__()[1][0] + else: + from sympy import srepr, sympify + expr_bytes = srepr(sympify(obj._symbol_expr)).encode(common.ENCODE) + else: + with io.BytesIO() as buf: + extra_symbols = _write_parameter_expression_v13(buf, obj, version) + expr_bytes = buf.getvalue() + symbol_table_len = len(obj._parameter_symbols) + if extra_symbols: + symbol_table_len += 2 * len(extra_symbols) param_expr_header_raw = struct.pack( - formats.PARAMETER_EXPR_PACK, len(obj._parameter_symbols), len(expr_bytes) + formats.PARAMETER_EXPR_PACK, symbol_table_len, 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) @@ -89,6 +211,49 @@ def _write_parameter_expression(file_obj, obj, use_symengine, *, version): file_obj.write(elem_header) file_obj.write(symbol_data) file_obj.write(value_data) + if extra_symbols: + for symbol in extra_symbols: + 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 + value_key, value_data = dumps_value( + symbol, version=version, 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) + for symbol in extra_symbols.values(): + 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 + value_key, value_data = dumps_value( + symbol, version=version, 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]): @@ -334,6 +499,163 @@ def _read_parameter_expression_v3(file_obj, vectors, use_symengine): return ParameterExpression(symbol_map, expr_) +def _read_parameter_expression_v4(file_obj, vectors, version): + data = formats.PARAMETER_EXPR( + *struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE)) + ) + + payload = file_obj.read(data.expr_size) + + 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(f"Invalid parameter expression map type: {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_v4, + vectors=vectors, + ) + else: + raise exceptions.QpyError(f"Invalid parameter expression map type: {elem_key}") + symbol_map[symbol] = value + with io.BytesIO(payload) as buf: + return _read_parameter_expr_v13(buf, symbol_map, version, vectors) + + +def _read_parameter_expr_v13(buf, symbol_map, version, vectors): + param_uuid_map = {symbol.uuid: symbol for symbol in symbol_map if isinstance(symbol, Parameter)} + name_map = {str(v): k for k, v in symbol_map.items()} + expression = None + data = buf.read(formats.PARAM_EXPR_ELEM_V4_SIZE) + rhs = None + lhs = None + while data: + expression_data = formats.PARAM_EXPR_ELEM_V4._make( + struct.unpack(formats.PARAM_EXPR_ELEM_V4_PACK, data) + ) + if lhs is None: + if expression_data.LHS_TYPE == b"p": + lhs = param_uuid_map[uuid.UUID(bytes=expression_data.LHS)] + elif expression_data.LHS_TYPE == b"f": + lhs = struct.unpack("!Qd", expression_data.LHS)[1] + elif expression_data.LHS_TYPE == b"n": + lhs = None + elif expression_data.LHS_TYPE == b"c": + lhs = complex(*struct.unpack("!dd", expression_data.LHS)) + elif expression_data.LHS_TYPE == b"s": + lhs = _read_parameter_expr_v13(buf, symbol_map, version, vectors) + data = buf.read(formats.PARAM_EXPR_ELEM_V4_SIZE) + if expression is not None: + expression = lhs + continue + elif expression_data.LHS_TYPE == b"e": + return expression + elif expression_data.LHS_TYPE == b"u": + size = struct.unpack_from("!QQ", expression_data.LHS)[0] + subs_map_data = buf.read(size) + with io.BytesIO(subs_map_data) as mapping_buf: + mapping = common.read_mapping( + mapping_buf, deserializer=loads_value, version=version, vectors=vectors + ) + expression = expression.subs( + {name_map[k]: v for k, v in mapping.items()}, allow_unknown_parameters=True + ) + data = buf.read(formats.PARAM_EXPR_ELEM_V4_SIZE) + continue + else: + raise exceptions.QpyError( + "Unknown ParameterExpression operation type {expression_data.LHS_TYPE}" + ) + if rhs is None: + if expression_data.RHS_TYPE == b"p": + rhs = param_uuid_map[uuid.UUID(bytes=expression_data.RHS)] + elif expression_data.RHS_TYPE == b"f": + rhs = struct.unpack("!Qd", expression_data.RHS)[1] + elif expression_data.RHS_TYPE == b"n": + rhs = None + elif expression_data.RHS_TYPE == b"c": + rhs = complex(*struct.unpack("!dd", expression_data.LHS)) + elif expression_data.RHS_TYPE == b"s": + rhs = _read_parameter_expr_v13(buf, symbol_map, version, vectors) + data = buf.read(formats.PARAM_EXPR_ELEM_V4_SIZE) + if expression is not None: + expression = rhs + continue + elif expression_data.RHS_TYPE == b"e": + return expression + else: + raise exceptions.QpyError( + f"Unknown ParameterExpression operation type {expression_data.RHS_TYPE}" + ) + reverse_op = False + if expression is None: + if isinstance(lhs, ParameterExpression): + expression = lhs + elif isinstance(rhs, ParameterExpression): + expression = rhs + reverse_op = True + rhs = lhs + else: + raise exceptions.QpyError("Invalid ParameterExpression payload construction") + method_str = op_code_to_method(_OPCode(expression_data.OP_CODE)) + # Handle reverse operators + if rhs is None and expression is not None: + reverse_op = True + rhs = lhs + if expression_data.OP_CODE in {0, 1, 2, 3, 4, 13, 15}: + if reverse_op: + # Map arithmetic operators to reverse methods + if expression_data.OP_CODE == 0: + method_str = "__radd__" + elif expression_data.OP_CODE == 1: + method_str = "__rsub__" + elif expression_data.OP_CODE == 2: + method_str = "__rmul__" + elif expression_data.OP_CODE == 3: + method_str = "__rtruediv__" + + expression = getattr(expression, method_str)(rhs) + else: + expression = getattr(expression, method_str)() + lhs = None + rhs = None + data = buf.read(formats.PARAM_EXPR_ELEM_V4_SIZE) + if expression is None: + if isinstance(lhs, ParameterExpression): + expression = lhs + elif isinstance(rhs, ParameterExpression): + expression = rhs + reverse_op = True + rhs = lhs + else: + raise exceptions.QpyError("Invalid ParameterExpression payload construction") + return expression + + def _read_expr( file_obj, clbits: collections.abc.Sequence[Clbit], @@ -664,13 +986,17 @@ def loads_value( if type_key == type_keys.Value.PARAMETER_EXPRESSION: if version < 3: return common.data_from_binary(binary_data, _read_parameter_expression) - else: + elif version < 13: return common.data_from_binary( binary_data, _read_parameter_expression_v3, vectors=vectors, use_symengine=use_symengine, ) + else: + return common.data_from_binary( + binary_data, _read_parameter_expression_v4, vectors=vectors, version=version + ) if type_key == type_keys.Value.EXPRESSION: return common.data_from_binary( binary_data, diff --git a/qiskit/qpy/common.py b/qiskit/qpy/common.py index 8d8a57b7404f..c2585d5be4d3 100644 --- a/qiskit/qpy/common.py +++ b/qiskit/qpy/common.py @@ -25,7 +25,7 @@ from qiskit.qpy import formats, exceptions -QPY_VERSION = 12 +QPY_VERSION = 13 QPY_COMPATIBILITY_VERSION = 10 ENCODE = "utf8" diff --git a/qiskit/qpy/formats.py b/qiskit/qpy/formats.py index a48a9ea777fa..3a4343b684c4 100644 --- a/qiskit/qpy/formats.py +++ b/qiskit/qpy/formats.py @@ -259,6 +259,18 @@ PARAMETER_PACK = "!H16s" PARAMETER_SIZE = struct.calcsize(PARAMETER_PACK) +## PARAMETEREXPRESSION +# PARAM_EXPR_V2 = namedtuple("PARAM_EXPR_V2", ["size"]) +# PARAM_EXPR_V2_PACK = "!H" +# PARAM_EXPR_V2_SIZE = struct.calcsize(PARAM_EXPR_V2_PACK) + +# PARAMETEREXPRESSION_ENTRY +PARAM_EXPR_ELEM_V4 = namedtuple( + "PARAM_EXPR_ELEM_V4", ["OP_CODE", "LHS_TYPE", "LHS", "RHS_TYPE", "RHS"] +) +PARAM_EXPR_ELEM_V4_PACK = "!Bc16sc16s" +PARAM_EXPR_ELEM_V4_SIZE = struct.calcsize(PARAM_EXPR_ELEM_V4_PACK) + # COMPLEX COMPLEX = namedtuple("COMPLEX", ["real", "imag"]) COMPLEX_PACK = "!dd"