From ff1d002c8bc52de334cbad3c598caf20e199efb0 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Fri, 21 Feb 2025 22:30:32 +0000 Subject: [PATCH 1/3] Propagate `DAGCircuit.name` in `ApplyLayout` This has always been absent, but `transpile` masked the problem by assigning the output circuit names by overwriting the `output_names` input kwarg if it wasn't set. `generate_preset_pass_manager` didn't have the same logic, so it was observable that `ApplyLayout` didn't propagate the name. We could have added similar name-propagation logic to `generate_preset_pass_manager`, but really the bug is in `ApplyLayout`; passes _should_ propagate the metadata. --- .../transpiler/passes/layout/apply_layout.py | 1 + .../notes/fix-pm-name-36baa550ce5d9c2c.yaml | 6 +++++ test/python/compiler/test_transpiler.py | 24 +++++++++++++++++-- .../transpiler/test_preset_passmanagers.py | 24 ++++++++++++++++++- 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml diff --git a/qiskit/transpiler/passes/layout/apply_layout.py b/qiskit/transpiler/passes/layout/apply_layout.py index 7514ac6b421f..7bd4ab714d83 100644 --- a/qiskit/transpiler/passes/layout/apply_layout.py +++ b/qiskit/transpiler/passes/layout/apply_layout.py @@ -59,6 +59,7 @@ def run(self, dag): q = QuantumRegister(len(layout), "q") new_dag = DAGCircuit() + new_dag.name = dag.name new_dag.add_qreg(q) for var in dag.iter_input_vars(): new_dag.add_input_var(var) diff --git a/releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml b/releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml new file mode 100644 index 000000000000..0384c33e7111 --- /dev/null +++ b/releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Circuits compiled used a preset passmanager constructed by :func:`.generate_preset_pass_manager` + will now correctly retain their :attr:`~.QuantumCircuit.name` attribute, as they do with + :func:`.transpile`. diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 30a18e1fcf03..cf11f3d31562 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -57,6 +57,8 @@ CZGate, ECRGate, HGate, + IGate, + PhaseGate, RXGate, RYGate, RZGate, @@ -1646,7 +1648,10 @@ def test_no_infinite_loop(self, optimization_level): @data(0, 1, 2, 3) def test_transpile_preserves_circuit_metadata(self, optimization_level): """Verify that transpile preserves circuit metadata in the output.""" - circuit = QuantumCircuit(2, metadata={"experiment_id": "1234", "execution_number": 4}) + metadata = {"experiment_id": "1234", "execution_number": 4} + name = "my circuit" + circuit = QuantumCircuit(2, metadata=metadata.copy(), name=name) + circuit.name = "my circuit" circuit.h(0) circuit.cx(0, 1) @@ -1678,7 +1683,22 @@ def test_transpile_preserves_circuit_metadata(self, optimization_level): optimization_level=optimization_level, seed_transpiler=42, ) - self.assertEqual(circuit.metadata, res.metadata) + self.assertEqual(res.metadata, metadata) + self.assertEqual(res.name, name) + + target = Target(14) + for inst in (IGate(), PhaseGate(Parameter("t")), SXGate()): + target.add_instruction(inst, {(i,): None for i in range(14)}) + target.add_instruction(CXGate(), {tuple(pair): None for pair in cmap}) + + res = transpile( + circuit, + target=target, + optimization_level=optimization_level, + seed_transpiler=42, + ) + self.assertEqual(res.metadata, metadata) + self.assertEqual(res.name, name) @data(0, 1, 2, 3) def test_transpile_optional_registers(self, optimization_level): diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index d26df3a24f66..990778891ad4 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -21,7 +21,7 @@ import numpy as np from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister -from qiskit.circuit import Qubit, Gate, ControlFlowOp, ForLoopOp, Measure +from qiskit.circuit import Qubit, Gate, ControlFlowOp, ForLoopOp, Measure, library as lib, Parameter from qiskit.compiler import transpile from qiskit.transpiler import ( CouplingMap, @@ -1530,6 +1530,28 @@ def test_parse_seed_transpiler_raises_value_error(self): ): generate_preset_pass_manager(seed_transpiler=0.1) + @data(0, 1, 2, 3) + def test_preserves_circuit_metadata(self, optimization_level): + """Test that basic metadata is preserved.""" + metadata = {"experiment_id": "1234", "execution_number": 4} + name = "my circuit" + circuit = QuantumCircuit(4, metadata=metadata.copy(), name=name) + circuit.h(0) + circuit.cx(0, 3) + + num_qubits = 10 + target = Target(num_qubits) + for inst in (lib.IGate(), lib.PhaseGate(Parameter("t")), lib.SXGate()): + target.add_instruction(inst, {(i,): None for i in range(num_qubits)}) + target.add_instruction(CXGate(), {pair: None for pair in CouplingMap.from_line(num_qubits)}) + + pm = generate_preset_pass_manager( + optimization_level=optimization_level, target=target, seed_transpiler=42 + ) + res = pm.run(circuit) + self.assertEqual(res.metadata, metadata) + self.assertEqual(res.name, name) + @ddt class TestIntegrationControlFlow(QiskitTestCase): From abef7a4e263ef3708fad57726acfb38ac8fb6fae Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Mon, 24 Feb 2025 12:15:38 +0000 Subject: [PATCH 2/3] Fix typo Co-authored-by: John Lapeyre --- releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml b/releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml index 0384c33e7111..3259712b505d 100644 --- a/releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml +++ b/releasenotes/notes/fix-pm-name-36baa550ce5d9c2c.yaml @@ -1,6 +1,6 @@ --- fixes: - | - Circuits compiled used a preset passmanager constructed by :func:`.generate_preset_pass_manager` + Circuits compiled using a preset passmanager constructed by :func:`.generate_preset_pass_manager` will now correctly retain their :attr:`~.QuantumCircuit.name` attribute, as they do with :func:`.transpile`. From e2d9967ae294eea3c3a71c566249971c90ffe600 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Mon, 24 Feb 2025 12:16:09 +0000 Subject: [PATCH 3/3] Remove redunant set --- test/python/compiler/test_transpiler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index cf11f3d31562..769bf66b0064 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -1651,7 +1651,6 @@ def test_transpile_preserves_circuit_metadata(self, optimization_level): metadata = {"experiment_id": "1234", "execution_number": 4} name = "my circuit" circuit = QuantumCircuit(2, metadata=metadata.copy(), name=name) - circuit.name = "my circuit" circuit.h(0) circuit.cx(0, 1)