Skip to content

Commit

Permalink
Fix qpy.dump()'s use_symengine when passed a truthy object (#11730)
Browse files Browse the repository at this point in the history
This commit fixes an issue in the qpy dump() function's use_symengine
kwarg. When a user provided a truthy value that wasn't the literal
`Truth` boolean object the generated qpy payload would be corrupted.
That's because the check that assigns the encoding key in the file
header was unecessarily checking for the literal ``True`` object instead
of a object that evaluated to true. This was causing the header to say
it was using a sympy encoding, but then all the checks that were
encoding the data were evaluating true and setting symengine data. This
would cause a failure on reading the qpy payload when the parameter
expressions were parsed as it would try to load the binary symengine
encoding with the sympy parser.
  • Loading branch information
mtreinish committed Feb 7, 2024
1 parent 2f95629 commit 21be91c
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 3 deletions.
2 changes: 1 addition & 1 deletion qiskit/qpy/type_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ class SymExprEncoding(TypeKeyBase):

@classmethod
def assign(cls, obj):
if obj is True:
if obj:
return cls.SYMENGINE
else:
return cls.SYMPY
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
fixes:
- |
Fixed an issue with the :func:`.qpy.dump` function, when the
``use_symengine`` flag was set to a truthy object that evaluated to
``True`` but was not actually the boolean ``True`` the generated QPY
payload would be corrupt. For example, if you set ``use_symengine`` to
:obj:`.HAS_SYMENGINE` which evaluates to ``True`` when cast as a bool,
but isn't actually ``True``.
35 changes: 33 additions & 2 deletions test/python/qpy/test_circuit_load_from_qpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,21 @@
from qiskit.transpiler import PassManager, TranspileLayout
from qiskit.transpiler import passes
from qiskit.compiler import transpile
from qiskit.utils import optionals
from qiskit.qpy.formats import FILE_HEADER_V10_PACK, FILE_HEADER_V10, FILE_HEADER_V10_SIZE
from test import QiskitTestCase # pylint: disable=wrong-import-order


class QpyCircuitTestCase(QiskitTestCase):
"""QPY schedule testing platform."""

def assert_roundtrip_equal(self, circuit, version=None):
def assert_roundtrip_equal(self, circuit, version=None, use_symengine=None):
"""QPY roundtrip equal test."""
qpy_file = io.BytesIO()
dump(circuit, qpy_file, version=version)
if use_symengine is None:
dump(circuit, qpy_file, version=version)
else:
dump(circuit, qpy_file, version=version, use_symengine=use_symengine)
qpy_file.seek(0)
new_circuit = load(qpy_file)[0]

Expand Down Expand Up @@ -296,3 +301,29 @@ def test_compatibility_version_roundtrip(self):
qc.cx(0, 1)
qc.measure_all()
self.assert_roundtrip_equal(qc, version=QPY_COMPATIBILITY_VERSION)


class TestUseSymengineFlag(QpyCircuitTestCase):
"""Test that the symengine flag works correctly."""

def test_use_symengine_with_bool_like(self):
"""Test that the use_symengine flag is set correctly with a bool-like input."""
theta = Parameter("theta")
two_theta = 2 * theta
qc = QuantumCircuit(1)
qc.rx(two_theta, 0)
qc.measure_all()
# Assert Roundtrip works
self.assert_roundtrip_equal(qc, use_symengine=optionals.HAS_SYMENGINE, version=10)
# Also check the qpy symbolic expression encoding is correct in the
# payload
with io.BytesIO() as file_obj:
dump(qc, file_obj, use_symengine=optionals.HAS_SYMENGINE)
file_obj.seek(0)
header_data = FILE_HEADER_V10._make(
struct.unpack(
FILE_HEADER_V10_PACK,
file_obj.read(FILE_HEADER_V10_SIZE),
)
)
self.assertEqual(header_data.symbolic_encoding, b"e")

0 comments on commit 21be91c

Please sign in to comment.