From a4c20fdf7c02cbc5d855f2501fbd90f0119cdae2 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 17 Nov 2023 18:55:23 -0500 Subject: [PATCH] Fix issue with ScheduleBlock qpy serialization and use_symengine (#11261) * Fix issue with ScheduleBlock qpy serialization and use_symengine This commit fixes an issue with the use_symengine flag on the qpy.dump() function. Previously in certain conditions a symengine expression that was listed in the qpy header as being encoded via symengine was incorrectly being serialized using sympy. This would cause a failure on deserialialization as the qpy payload was invalid and symengine could not parse a symengine representation. This was originally caught during the development of #10902 as that switches the default of use_symengine to ``True`` as it makes symengine a hard requirement. This commit splits it out so the isolated fix can be backported to 0.45.1. * Skip symengine test if symengine isn't installed * Fix skip syntax * Update releasenotes/notes/fix-schedule-qpy-use-symengine-05ae1dfab73e3ff8.yaml Co-authored-by: Jake Lishman --------- Co-authored-by: Jake Lishman (cherry picked from commit 1dd4f5495e62b873327a7b8ad1c9616f6c672aa9) --- qiskit/qpy/binary_io/schedules.py | 2 +- ...le-qpy-use-symengine-05ae1dfab73e3ff8.yaml | 6 ++++ test/python/qpy/test_block_load_from_qpy.py | 29 +++++++++++++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/fix-schedule-qpy-use-symengine-05ae1dfab73e3ff8.yaml diff --git a/qiskit/qpy/binary_io/schedules.py b/qiskit/qpy/binary_io/schedules.py index 83a023b9527b..25ca9388bb4e 100644 --- a/qiskit/qpy/binary_io/schedules.py +++ b/qiskit/qpy/binary_io/schedules.py @@ -484,7 +484,7 @@ def _dumps_operand(operand, use_symengine): def _write_element(file_obj, element, metadata_serializer, use_symengine): if isinstance(element, ScheduleBlock): common.write_type_key(file_obj, type_keys.Program.SCHEDULE_BLOCK) - write_schedule_block(file_obj, element, metadata_serializer) + write_schedule_block(file_obj, element, metadata_serializer, use_symengine) else: type_key = type_keys.ScheduleInstruction.assign(element) common.write_type_key(file_obj, type_key) diff --git a/releasenotes/notes/fix-schedule-qpy-use-symengine-05ae1dfab73e3ff8.yaml b/releasenotes/notes/fix-schedule-qpy-use-symengine-05ae1dfab73e3ff8.yaml new file mode 100644 index 000000000000..3fe71509ea7d --- /dev/null +++ b/releasenotes/notes/fix-schedule-qpy-use-symengine-05ae1dfab73e3ff8.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixed an issue with :func:`.qpy.dump` which would cause the function to + potentially ignore the value of ``use_symengine`` when serializing a + :class:`.ScheduleBlock` object. diff --git a/test/python/qpy/test_block_load_from_qpy.py b/test/python/qpy/test_block_load_from_qpy.py index e68ec48d4a00..8137ef21a6bb 100644 --- a/test/python/qpy/test_block_load_from_qpy.py +++ b/test/python/qpy/test_block_load_from_qpy.py @@ -53,10 +53,10 @@ class QpyScheduleTestCase(QiskitTestCase): """QPY schedule testing platform.""" - def assert_roundtrip_equal(self, block): + def assert_roundtrip_equal(self, block, use_symengine=False): """QPY roundtrip equal test.""" qpy_file = io.BytesIO() - dump(block, qpy_file) + dump(block, qpy_file, use_symengine=use_symengine) qpy_file.seek(0) new_block = load(qpy_file)[0] @@ -278,6 +278,31 @@ def test_bell_schedule(self): self.assert_roundtrip_equal(test_sched) + @unittest.skipUnless(_optional.HAS_SYMENGINE, "Symengine required for this test") + def test_bell_schedule_use_symengine(self): + """Test complex schedule to create a Bell state.""" + with builder.build() as test_sched: + with builder.align_sequential(): + # H + builder.shift_phase(-1.57, DriveChannel(0)) + builder.play(Drag(160, 0.05, 40, 1.3), DriveChannel(0)) + builder.shift_phase(-1.57, DriveChannel(0)) + # ECR + with builder.align_left(): + builder.play(GaussianSquare(800, 0.05, 64, 544), DriveChannel(1)) + builder.play(GaussianSquare(800, 0.22, 64, 544, 2), ControlChannel(0)) + builder.play(Drag(160, 0.1, 40, 1.5), DriveChannel(0)) + with builder.align_left(): + builder.play(GaussianSquare(800, -0.05, 64, 544), DriveChannel(1)) + builder.play(GaussianSquare(800, -0.22, 64, 544, 2), ControlChannel(0)) + builder.play(Drag(160, 0.1, 40, 1.5), DriveChannel(0)) + # Measure + with builder.align_left(): + builder.play(GaussianSquare(8000, 0.2, 64, 7744), MeasureChannel(0)) + builder.acquire(8000, AcquireChannel(0), MemorySlot(0)) + + self.assert_roundtrip_equal(test_sched, True) + def test_with_acquire_instruction_with_kernel(self): """Test a schedblk with acquire instruction with kernel.""" kernel = Kernel(