From fb84ca2fd01a83a011e3b68c61254e68fd1a5e2c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 18:45:33 +0000 Subject: [PATCH] Fix global-phase of copied `BlueprintCircuit`s (#11456) (#11485) Using `BlueprintCircuit.copy()` or `.copy_empty_like()` would previously erase the global phase as part of the rebuild operation. This overrides `BlueprintCircuit.copy_empty_like()` to ensure that it is always propagated through. (cherry picked from commit 19767bc1916223b341fd163e368e6889d73258a3) Co-authored-by: Jake Lishman --- qiskit/circuit/library/blueprintcircuit.py | 10 ++++++ ...ueprintcircuit-phase-7102043cf2e47e33.yaml | 6 ++++ .../circuit/library/test_blueprintcircuit.py | 35 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 releasenotes/notes/fix-blueprintcircuit-phase-7102043cf2e47e33.yaml diff --git a/qiskit/circuit/library/blueprintcircuit.py b/qiskit/circuit/library/blueprintcircuit.py index 082249e54b04..c2c555acaf6c 100644 --- a/qiskit/circuit/library/blueprintcircuit.py +++ b/qiskit/circuit/library/blueprintcircuit.py @@ -175,6 +175,16 @@ def num_connected_components(self, unitary_only=False): self._build() return super().num_connected_components(unitary_only=unitary_only) + def copy_empty_like(self, name=None): + if not self._is_built: + self._build() + cpy = super().copy_empty_like(name=name) + # The base `copy_empty_like` will typically trigger code that `BlueprintCircuit` treats as + # an "invalidation", so we have to manually restore properties deleted by that that + # `copy_empty_like` is supposed to propagate. + cpy.global_phase = self.global_phase + return cpy + def copy(self, name=None): if not self._is_built: self._build() diff --git a/releasenotes/notes/fix-blueprintcircuit-phase-7102043cf2e47e33.yaml b/releasenotes/notes/fix-blueprintcircuit-phase-7102043cf2e47e33.yaml new file mode 100644 index 000000000000..f835d8a97477 --- /dev/null +++ b/releasenotes/notes/fix-blueprintcircuit-phase-7102043cf2e47e33.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Calling :meth:`~.QuantumCircuit.copy` or :meth:`~.QuantumCircuit.copy_empty_like` on a + :class:¬.BlueprintCircuit` will now correctly propagate the + :attr:`~.QuantumCircuit.global_phase` to the copy. Previously, the global phase would always be zero after the copy. diff --git a/test/python/circuit/library/test_blueprintcircuit.py b/test/python/circuit/library/test_blueprintcircuit.py index 0ccd5c40ebbc..0935694981e7 100644 --- a/test/python/circuit/library/test_blueprintcircuit.py +++ b/test/python/circuit/library/test_blueprintcircuit.py @@ -12,6 +12,7 @@ """Test the blueprint circuit.""" +import math import unittest from ddt import ddt, data @@ -174,6 +175,40 @@ def _build(self): mock._append(CircuitInstruction(XGate(), (qr[0],), ())) self.assertEqual(expected, mock) + def test_global_phase_copied(self): + """Test that a global-phase parameter is correctly propagated through.""" + + class DummyBlueprint(BlueprintCircuit): + """Dummy circuit.""" + + def _check_configuration(self, raise_on_failure=True): + return True + + def _build(self): + # We don't need to do anything, we just need `_build` to be non-abstract. + # pylint: disable=useless-parent-delegation + return super()._build() + + base = DummyBlueprint() + base.global_phase = math.pi / 2 + + self.assertEqual(base.copy_empty_like().global_phase, math.pi / 2) + self.assertEqual(base.copy().global_phase, math.pi / 2) + + # Verify that a parametric global phase can be assigned after the copy. + a = Parameter("a") + parametric = DummyBlueprint() + parametric.global_phase = a + + self.assertEqual( + parametric.copy_empty_like().assign_parameters({a: math.pi / 2}).global_phase, + math.pi / 2, + ) + self.assertEqual( + parametric.copy().assign_parameters({a: math.pi / 2}).global_phase, + math.pi / 2, + ) + if __name__ == "__main__": unittest.main()