diff --git a/qiskit/qpy/binary_io/circuits.py b/qiskit/qpy/binary_io/circuits.py index a50f202afa2a..5266bbb0346b 100644 --- a/qiskit/qpy/binary_io/circuits.py +++ b/qiskit/qpy/binary_io/circuits.py @@ -540,7 +540,6 @@ def _write_instruction(file_obj, instruction, custom_operations, index_map): ) or gate_class_name == "Gate" or gate_class_name == "Instruction" - or gate_class_name == "ControlledGate" or isinstance(instruction.operation, library.BlueprintCircuit) ): if instruction.operation.name not in custom_operations: @@ -548,6 +547,11 @@ def _write_instruction(file_obj, instruction, custom_operations, index_map): custom_operations_list.append(instruction.operation.name) gate_class_name = instruction.operation.name + elif gate_class_name == "ControlledGate": + gate_class_name = instruction.operation.name + "_" + str(uuid.uuid4()) + custom_operations[gate_class_name] = instruction.operation + custom_operations_list.append(gate_class_name) + elif isinstance(instruction.operation, library.PauliEvolutionGate): gate_class_name = r"###PauliEvolutionGate_" + str(uuid.uuid4()) custom_operations[gate_class_name] = instruction.operation diff --git a/releasenotes/notes/fix-qpy-repeated-controlled-gates-e19fc4ee65a22756.yaml b/releasenotes/notes/fix-qpy-repeated-controlled-gates-e19fc4ee65a22756.yaml new file mode 100644 index 000000000000..bb6b90245614 --- /dev/null +++ b/releasenotes/notes/fix-qpy-repeated-controlled-gates-e19fc4ee65a22756.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixed a bug in QPY serialization (:mod:`qiskit.qpy`) where if a circuit contained + multiple instances of parametrized controlled gates of the same class (not custom), + the parameter values from the first instance were used to build the gate definitions + of subsequent instances. The gates were rendered correctly despite this bug because + the correct parameter values were stored, but not used to build the gates. Fixed + `#10735 `__. diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 2e07157b8898..5057105cb5ca 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -29,6 +29,7 @@ from qiskit.circuit.gate import Gate from qiskit.circuit.library import ( XGate, + RYGate, QFT, QAOAAnsatz, PauliEvolutionGate, @@ -1310,6 +1311,25 @@ def _define(self) -> None: self.assertEqual(qc.decompose(), new_circ.decompose()) self.assertDeprecatedBitProperties(qc, new_circ) + def test_multiple_controlled_gates(self): + """Test multiple controlled gates with same name but different + parameter values. + + Reproduce from: https://github.com/Qiskit/qiskit-terra/issues/10735 + """ + + qc = QuantumCircuit(3) + for i in range(3): + c2ry = RYGate(i + 1).control(2) + qc.append(c2ry, [i % 3, (i + 1) % 3, (i + 2) % 3]) + qpy_file = io.BytesIO() + dump(qc, qpy_file) + qpy_file.seek(0) + new_circ = load(qpy_file)[0] + self.assertEqual(qc, new_circ) + self.assertEqual(qc.decompose(), new_circ.decompose()) + self.assertDeprecatedBitProperties(qc, new_circ) + def test_load_with_loose_bits(self): """Test that loading from a circuit with loose bits works.""" qc = QuantumCircuit([Qubit(), Qubit(), Clbit()])