Skip to content

Commit

Permalink
Fix multiple nested custom controlled gates in QPY (#10537) (#10578)
Browse files Browse the repository at this point in the history
* Fix multiple nested custom controlled gates in QPY

When a circuit contained multiple custom controlled gates that in turn
had custom definitions, the QPY serialisation could fail to write out
some of the inner instructions correctly.  This led to an invalid QPY
file that could not be read back.

* Remove local warning-suppression import

(cherry picked from commit 06416d7)

Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
  • Loading branch information
mergify[bot] and jakelishman committed Aug 7, 2023
1 parent a8026fa commit 3ba0b74
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 2 deletions.
7 changes: 5 additions & 2 deletions qiskit/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -970,10 +970,13 @@ def write_circuit(file_obj, circuit, metadata_serializer=None):
new_custom_operations = list(custom_operations.keys())
while new_custom_operations:
operations_to_serialize = new_custom_operations.copy()
new_custom_operations = []
for name in operations_to_serialize:
operation = custom_operations[name]
new_custom_operations = _write_custom_operation(
custom_operations_buffer, name, operation, custom_operations
new_custom_operations.extend(
_write_custom_operation(
custom_operations_buffer, name, operation, custom_operations
)
)

file_obj.write(struct.pack(formats.CUSTOM_CIRCUIT_DEF_HEADER_PACK, len(custom_operations)))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
Fixed a bug in QPY serialization (:mod:`qiskit.qpy`) where multiple controlled custom gates in
a circuit could result in an invalid QPY file that could not be parsed. Fixed `#9746
<https://github.com/Qiskit/qiskit-terra/issues/9746>`__.
25 changes: 25 additions & 0 deletions test/python/circuit/test_circuit_load_from_qpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,31 @@ def test_switch_expr_stress(self):
self.assertEqual(qc.cregs, new_circuit.cregs)
self.assertDeprecatedBitProperties(qc, new_circuit)

def test_multiple_nested_control_custom_definitions(self):
"""Test that circuits with multiple controlled custom gates that in turn depend on custom
gates can be exported successfully when there are several such gates in the outer circuit.
See gh-9746"""
inner_1 = QuantumCircuit(1, name="inner_1")
inner_1.x(0)
inner_2 = QuantumCircuit(1, name="inner_2")
inner_2.y(0)

outer_1 = QuantumCircuit(1, name="outer_1")
outer_1.append(inner_1.to_gate(), [0], [])
outer_2 = QuantumCircuit(1, name="outer_2")
outer_2.append(inner_2.to_gate(), [0], [])

qc = QuantumCircuit(2)
qc.append(outer_1.to_gate().control(1), [0, 1], [])
qc.append(outer_2.to_gate().control(1), [0, 1], [])

with io.BytesIO() as fptr:
dump(qc, fptr)
fptr.seek(0)
new_circuit = load(fptr)[0]
self.assertEqual(qc, new_circuit)
self.assertDeprecatedBitProperties(qc, new_circuit)

def test_qpy_deprecation(self):
"""Test the old import path's deprecations fire."""
with self.assertWarnsRegex(DeprecationWarning, "is deprecated"):
Expand Down

0 comments on commit 3ba0b74

Please sign in to comment.