Repository: Qiskit/qiskit — pull request #10631 (python)
Title:  Removed 2pi wrapping for global phase parameter

It fixes #10569\r\n<!--\r\n⚠️ If you do not respect this template, your pull request will be closed.\r\n⚠️ Your pull request title should be short detailed and understandable for all.\r\n⚠️ Also, please add a release note file using reno if the change needs to be\r\n  documented in the release notes.\r\n⚠️ If your pull request fixes an open issue, please link to the issue.\r\n\r\n- [ ] I have added the tests to cover my changes.\r\n- [ ] I have updated the documentation accordingly.\r\n- [ ] I have read the CONTRIBUTING document.\r\n-->\r\n\r\n### Summary\r\nRemoved `_mod_2pi` function.\r\n\r\n\r\n### Details and comments\r\nIt is similiar to Qiskit/qiskit-ibm-provider#689\r\n\r\n\r\n

Resolved issues:
title: Cannot apply dynamical decoupling on a circuit with parameterized global phase
body: ### Environment\n\n- **Qiskit Terra version**: 0.25 \r\n- **Python version**: 3.11\r\n- **Operating system**: Windows\r\n\n\n### What is happening?\n\nDynamical decoupling cannot be applied on a circuit with a parameterized global phase, due to a failing `_mod_2pi` wrapping \n\n### How can we reproduce the issue?\n\n```python\r\nfrom qiskit.transpiler.passes import ALAPScheduleAnalysis, PadDynamicalDecoupling\r\nfrom qiskit.transpiler import PassManager, InstructionDurations\r\nfrom qiskit.circuit.library import XGate\r\nfrom qiskit.circuit import Parameter\r\ndurations = InstructionDurations.from_backend(backend)\r\n\r\nqc = QuantumCircuit(1,1)\r\nqc.sx(0)\r\nqc.delay(1600, 0)\r\nqc.sx(0)\r\nqc.global_phase=Parameter('a')\r\n\r\npm = PassManager([ALAPScheduleAnalysis(durations), PadDynamicalDecoupling(durations, [XGate(), XGate()])])\r\npm.run(qc)\r\n```\r\n\r\nerrors in\r\nhttps://github.com/Qiskit/qiskit-terra/blob/c29e938c2ba1c359a0a3268679c68f3237b2c1b7/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py#L260\r\nwith:\r\n`TypeError: unsupported operand type(s) for %: 'ParameterExpression' and 'float'`\n\n### What should happen?\n\nRun the DD transpiler pass successfully\n\n### Any suggestions?\n\nDon't wrap global phase if parameterized, see same issue in https://github.com/Qiskit/qiskit-ibm-provider/pull/689

Hints:

Fix patch:
diff --git a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py\nindex 87bbdbff919c..cf8477278372 100644\n--- a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py\n+++ b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py\n@@ -274,7 +274,7 @@ def run(self, dag):\n                 if gate is not None:\n                     new_dag.apply_operation_back(gate, [dag_qubit], check=False)\n \n-            new_dag.global_phase = _mod_2pi(new_dag.global_phase + sequence_gphase)\n+            new_dag.global_phase = new_dag.global_phase + sequence_gphase\n \n         return new_dag\n \n@@ -283,11 +283,3 @@ def __gate_supported(self, gate: Gate, qarg: int) -> bool:\n         if self._target is None or self._target.instruction_supported(gate.name, qargs=(qarg,)):\n             return True\n         return False\n-\n-\n-def _mod_2pi(angle: float, atol: float = 0):\n-    """Wrap angle into interval [-π,π). If within atol of the endpoint, clamp to -π"""\n-    wrapped = (angle + np.pi) % (2 * np.pi) - np.pi\n-    if abs(wrapped - np.pi) < atol:\n-        wrapped = -np.pi\n-    return wrapped\ndiff --git a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py\nindex 2d4114f3cfc7..9137133567ec 100644\n--- a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py\n+++ b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py\n@@ -394,8 +394,7 @@ def _constrained_length(values):\n                 gate_length = self._dd_sequence_lengths[qubit][dd_ind]\n                 self._apply_scheduled_op(dag, idle_after, gate, qubit)\n                 idle_after += gate_length\n-\n-        dag.global_phase = self._mod_2pi(dag.global_phase + sequence_gphase)\n+        dag.global_phase = dag.global_phase + sequence_gphase\n \n     @staticmethod\n     def _resolve_params(gate: Gate) -> tuple:\n@@ -407,11 +406,3 @@ def _resolve_params(gate: Gate) -> tuple:\n             else:\n                 params.append(p)\n         return tuple(params)\n-\n-    @staticmethod\n-    def _mod_2pi(angle: float, atol: float = 0):\n-        """Wrap angle into interval [-π,π). If within atol of the endpoint, clamp to -π"""\n-        wrapped = (angle + np.pi) % (2 * np.pi) - np.pi\n-        if abs(wrapped - np.pi) < atol:\n-            wrapped = -np.pi\n-        return wrapped\ndiff --git a/releasenotes/notes/fix-param-global-phase-31547267f6124552.yaml b/releasenotes/notes/fix-param-global-phase-31547267f6124552.yaml\nnew file mode 100644\nindex 000000000000..13048f626dc4\n--- /dev/null\n+++ b/releasenotes/notes/fix-param-global-phase-31547267f6124552.yaml\n@@ -0,0 +1,7 @@\n+---\n+fixes:\n+  - |\n+    Fixed compatibility of :class:`.DynamicalDecoupling` and\n+    :class:`.PadDynamicalDecoupling` with circuits that have a parameterized\n+    global phase.\n+    Fixed `#10569 <https://github.com/Qiskit/qiskit-terra/issues/10569>`__.\n

Test patch:
diff --git a/test/python/transpiler/test_dynamical_decoupling.py b/test/python/transpiler/test_dynamical_decoupling.py\nindex b74091dd76f8..e53fb9b0226e 100644\n--- a/test/python/transpiler/test_dynamical_decoupling.py\n+++ b/test/python/transpiler/test_dynamical_decoupling.py\n@@ -1031,6 +1031,25 @@ def test_respect_target_instruction_constraints(self):\n         expected.delay(200, [0])\n         self.assertEqual(expected, scheduled)\n \n+    def test_paramaterized_global_phase(self):\n+        """Test paramaterized global phase in DD circuit.\n+        See:https://github.com/Qiskit/qiskit-terra/issues/10569\n+        """\n+        dd_sequence = [XGate(), YGate()] * 2\n+        qc = QuantumCircuit(1, 1)\n+        qc.h(0)\n+        qc.delay(1700, 0)\n+        qc.y(0)\n+        qc.global_phase = Parameter("a")\n+        pm = PassManager(\n+            [\n+                ALAPScheduleAnalysis(self.durations),\n+                PadDynamicalDecoupling(self.durations, dd_sequence),\n+            ]\n+        )\n+\n+        self.assertEqual(qc.global_phase + np.pi, pm.run(qc).global_phase)\n+\n \n if __name__ == "__main__":\n     unittest.main()\n