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.

(cherry picked from commit 21be91c)
  • Loading branch information
mtreinish authored and mergify[bot] committed Feb 7, 2024
1 parent 224488e commit ed9341f
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 ed9341f

Please sign in to comment.