Fix bug in qs_decomposition (Qiskit/qiskit#10850)

<!--\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- [x] I have added the tests to cover my changes.\r\n- [x] I have updated the documentation accordingly.\r\n- [x] I have read the CONTRIBUTING document.\r\n-->\r\n\r\n### Summary\r\nfixes #10787\r\n\r\n\r\n### Details and comments\r\nThe unitary described in this issue was causing a single 2q unitary to be generated in the quantum shannon decomposition, when opt_a2==True, which couldn't take advantage of this diagonal commutation optimization and would error. The fix instead leaves this as an undecomposed 2-qubit unitary gate.\r\n\r\n

```Gate.control``` method throwing error

### Environment\r\n\r\n- **Qiskit Terra version**: 0.24.0\r\n- **Python version**: Python 3.9.6\r\n\r\n### What is happening?\r\n\r\nGiven a quantum circuit object, we execute the ```to_gate()``` method and attempt to call the ```Gate.control()``` method with appropriate parameters and sometimes observe the following error: ```*** UnboundLocalError: local variable 'mat2'``` referenced before assignment\r\n\r\n### How can we reproduce the issue?\r\n\r\n``` \r\nfrom qiskit.extensions import UnitaryGate\r\nfrom qiskit import QuantumCircuit, QuantumRegister\r\n\r\n# initialize a unitary matrix\r\n\r\nA = np.array([[ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,\r\n         0.+0.j],\r\n       [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,\r\n         0.+0.j],\r\n       [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j, -1.+0.j,\r\n         0.+0.j],\r\n       [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\r\n        -1.+0.j],\r\n       [ 1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\r\n         0.+0.j],\r\n       [ 0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\r\n         0.+0.j],\r\n       [ 0.+0.j,  0.+0.j, -1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\r\n         0.+0.j],\r\n       [ 0.+0.j,  0.+0.j,  0.+0.j, -1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,\r\n         0.+0.j]])\r\n\r\ngate = UnitaryGate(A)\r\nqc = QuantumCircuit(3)\r\nqc.append(gate,[0,1,2])\r\n\r\n# This happens in a a different function than the operations above\r\n\r\nCU = qc.to_gate().control(6,28)\r\n```\r\n\r\n### What should happen?\r\n\r\nI would assume that the error is not thrown\r\n\r\n### Any suggestions?\r\n\r\nThe error appears to come from the file ```qsd.py```, and in particular because the list ```ind2q``` (line 234) has only one element. The for loop that starts on line 242 is thus never executed and the the variable ```mat2``` is never initialized.

diff --git a/qiskit/quantum_info/synthesis/qsd.py b/qiskit/quantum_info/synthesis/qsd.py\nindex b2f00a56045d..80892a606d7e 100644\n--- a/qiskit/quantum_info/synthesis/qsd.py\n+++ b/qiskit/quantum_info/synthesis/qsd.py\n@@ -243,7 +243,11 @@ def _apply_a2(circ):\n     for i, instruction in enumerate(ccirc.data):\n         if instruction.operation.name == "qsd2q":\n             ind2q.append(i)\n-    if not ind2q:\n+    if len(ind2q) == 0:\n+        return ccirc\n+    elif len(ind2q) == 1:\n+        # No neighbors to merge diagonal into; revert name\n+        ccirc.data[ind2q[0]].operation.name = "Unitary"\n         return ccirc\n     # rolling over diagonals\n     ind2 = None  # lint\ndiff --git a/releasenotes/notes/10787-078d7caa70fc7de8.yaml b/releasenotes/notes/10787-078d7caa70fc7de8.yaml\nnew file mode 100644\nindex 000000000000..ae22987bb75f\n--- /dev/null\n+++ b/releasenotes/notes/10787-078d7caa70fc7de8.yaml\n@@ -0,0 +1,7 @@\n+---\n+fixes:\n+  - |\n+    Fix the Quantum Shannon Decomposition implemented in :func:`.qs_decomposition`. When a unitary\n+    could not take advantage of the diagonal commutation optimization, it used to error.\n+    Now, it leaves it as undecomposed 2-qubit unitary gate.\n+    Fixes `#10787 <https://github.com/Qiskit/qiskit/issues/10787>`__\n

diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py\nindex 19ec8b8139e9..043abf8c87f8 100644\n--- a/test/python/quantum_info/test_synthesis.py\n+++ b/test/python/quantum_info/test_synthesis.py\n@@ -1548,6 +1548,105 @@ def test_opt_a1a2(self, nqubits):\n         elif nqubits == 2:\n             self.assertLessEqual(ccirc.count_ops().get("cx", 0), 3)\n \n+    def test_a2_opt_single_2q(self):\n+        """\n+        Test a2_opt when a unitary causes a single final 2-qubit unitary for which this optimization\n+        won't help. This came up in issue 10787.\n+        """\n+        # this somewhat unique signed permutation matrix seems to cause the issue\n+        mat = np.array(\n+            [\n+                [\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    1.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                ],\n+                [\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    1.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                ],\n+                [\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    -1.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                ],\n+                [\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    -1.0 + 0.0j,\n+                ],\n+                [\n+                    1.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                ],\n+                [\n+                    0.0 + 0.0j,\n+                    1.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                ],\n+                [\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    -1.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                ],\n+                [\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    -1.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                    0.0 + 0.0j,\n+                ],\n+            ]\n+        )\n+\n+        gate = UnitaryGate(mat)\n+        qc = QuantumCircuit(3)\n+        qc.append(gate, range(3))\n+        try:\n+            qc.to_gate().control(1)\n+        except UnboundLocalError as uerr:\n+            self.fail(str(uerr))\n+\n \n class TestTwoQubitDecomposeUpToDiagonal(QiskitTestCase):\n     """test TwoQubitDecomposeUpToDiagonal class"""\n


Fix Clifford.from_operator to fail with non-Clifford diagonal operators (Qiskit/qiskit#10904)

<!--\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\nFix a bug where `Clifford.from_operator` (`from_matrix`) does not fail with non-Clifford diagonal operators (matrices) and return incorrect Clifford objects. This commit corrects it to raise an error as expected.\r\nFix #10903\r\n\r\n### Details and comments\r\nEach non-zero element of the Pauli matrices computed during the conversion must be one of {1, -1, 1j, -1j} for valid input but that was not fully checked and failed to raise an error against invalid non-Clifford diagonal matrices.\r\n\r\n

Clifford.from_operator does not fail with non-Clifford diagonal operator

### Environment\r\n\r\n- **Qiskit Terra version**: 0.25.1\r\n- **Python version**: 3.10\r\n- **Operating system**: Mac OS\r\n\r\n\r\n### What is happening?\r\n\r\n`Clifford.from_operator` (`from_matrix`) does not fail with non-Clifford diagonal operator (matrix) and returns an incorrect Clifford object.\r\n\r\n### How can we reproduce the issue?\r\n\r\nRun `Clifford.from_operator(Operator(RZZGate(0.2)))`\r\n\r\n### What should happen?\r\n\r\nThe code should raise an error.\r\n\r\n### Any suggestions?\r\n\r\n_No response_

diff --git a/qiskit/quantum_info/operators/symplectic/clifford.py b/qiskit/quantum_info/operators/symplectic/clifford.py\nindex b17c29df4665..30166530337c 100644\n--- a/qiskit/quantum_info/operators/symplectic/clifford.py\n+++ b/qiskit/quantum_info/operators/symplectic/clifford.py\n@@ -1004,9 +1004,10 @@ def _pauli_matrix_to_row(mat, num_qubits):\n         """Generate a binary vector (a row of tableau representation) from a Pauli matrix.\n         Return None if the non-Pauli matrix is supplied."""\n         # pylint: disable=too-many-return-statements\n+        decimals = 6\n \n-        def find_one_index(x, decimals=6):\n-            indices = np.where(np.round(np.abs(x), decimals) == 1)\n+        def find_one_index(x):\n+            indices = np.where(np.round(np.abs(x), decimals=decimals) == 1)\n             return indices[0][0] if len(indices[0]) == 1 else None\n \n         def bitvector(n, num_bits):\n@@ -1018,7 +1019,7 @@ def bitvector(n, num_bits):\n             return None\n         xbits = bitvector(xint, num_qubits)\n \n-        # extract non-zero elements from matrix (rounded to 1, -1, 1j or -1j)\n+        # extract non-zero elements from matrix (each must be 1, -1, 1j or -1j for Pauli matrix)\n         entries = np.empty(len(mat), dtype=complex)\n         for i, row in enumerate(mat):\n             index = find_one_index(row)\n@@ -1027,7 +1028,9 @@ def bitvector(n, num_bits):\n             expected = xint ^ i\n             if index != expected:\n                 return None\n-            entries[i] = np.round(mat[i, index])\n+            entries[i] = np.round(mat[i, index], decimals=decimals)\n+            if entries[i] not in {1, -1, 1j, -1j}:\n+                return None\n \n         # compute z-bits\n         zbits = np.empty(num_qubits, dtype=bool)\ndiff --git a/releasenotes/notes/fix-clifford-from-diagonal-7708654373bd5b8b.yaml b/releasenotes/notes/fix-clifford-from-diagonal-7708654373bd5b8b.yaml\nnew file mode 100644\nindex 000000000000..8340ee33a07d\n--- /dev/null\n+++ b/releasenotes/notes/fix-clifford-from-diagonal-7708654373bd5b8b.yaml\n@@ -0,0 +1,7 @@\n+---\n+fixes:\n+  - |\n+    Fixed a bug where :meth:`~.Clifford.from_matrix` and :meth:`~.Clifford.from_operator`\n+    do not fail with non-Clifford diagonal operators (matrices) and return incorrect\n+    Clifford objects. This has been corrected so that they raise an error in the cases.\n+    Fixed `#10903 <https://github.com/Qiskit/qiskit-terra/issues/10903>`__\n

diff --git a/test/python/quantum_info/operators/symplectic/test_clifford.py b/test/python/quantum_info/operators/symplectic/test_clifford.py\nindex 092d86fea0e6..4dbde8a36e34 100644\n--- a/test/python/quantum_info/operators/symplectic/test_clifford.py\n+++ b/test/python/quantum_info/operators/symplectic/test_clifford.py\n@@ -1068,6 +1068,12 @@ def test_from_matrix_round_trip(self, num_qubits):\n             actual = Clifford.from_matrix(expected.to_matrix())\n             self.assertEqual(expected, actual)\n \n+    def test_from_non_clifford_diagonal_operator(self):\n+        """Test if failing with non-clifford diagonal operator.\n+        See https://github.com/Qiskit/qiskit/issues/10903"""\n+        with self.assertRaises(QiskitError):\n+            Clifford.from_operator(Operator(RZZGate(0.2)))\n+\n     @combine(num_qubits=[1, 2, 3, 4])\n     def test_from_operator_round_trip(self, num_qubits):\n         """Test round trip conversion to and from operator"""\n


Fix split barriers leaking during disjoint layout processing (Qiskit/qiskit#11655)

<!--\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\n\r\nThis commit fixes an issue in the disjoint layout processing caused by a shared reference from the input dag to a layout pass to the internal transformations the layout processing performs. As part of the disjoint layout processing the input dag needs to be split into smaller dags for each connected component in the circuit. To enable this any barriers present in the circuit need to be split up prior to analyzing the connected components in the dag. A multiqubit barrier doesn't represent a real connectivity requirement so they need to be split prior to analysis to ensure they don't factor into the connected component computation. To faciliate not losing the semantic meaning of the barrier for the layout analysis of each connected component, the barrier split is done by first taking an n-qubit barrier and converting it into n parallel 1q barriers with a uuid label applied to it. Then after we split the circuit into the separate connected components the uuid is used to identify any components of the same barrier and combine them together.\r\n\r\nThere were two issues with this approach the first is that the splitting of the barriers was done on the input dag without copying first. This causes the input dag to be modified and because in most cases it's a shared instance which would leak out the barrier splitting if the input dag was returned verbatim from the pass, which in most cases it would be. This issue is fixed by ensuring that we re-combine any split barriers after we've split the dag into it's connected components. The second issue is the uuid label assignment would overwrite any existing labels on barriers. While setting labels on barriers is uncommon for users to do (but still completely valid) this is causing a bigger issues since #10323 because the transpiler is assigning labels to barriers it creates internally so they can be removed before the circuit is returned. This is also fixed in this commit by appending a uuid to the existing label instead of overwriting it, so we're able to restore the original label when we recombine barriers.\r\n\r\n### Details and comments\r\n\r\nFixes #11649

Disjoint handling in transpiler fails to rejoin barriers

### Environment\n\n- **Qiskit Terra version**: `main` @ ce02f06\r\n- **Python version**: 3.10\r\n- **Operating system**: macOS\r\n\n\n### What is happening?\n\nThe preset passmangers for `transpile` are failing to correctly reconstruct barriers temporarily inserted during the compiler routing, which is leaking out invalid `UUID` objects inside the `label` field of those barriers.\n\n### How can we reproduce the issue?\n\nGiven a set up of:\r\n\r\n```python\r\nfrom qiskit import transpile, QuantumCircuit\r\nfrom qiskit.transpiler import CouplingMap\r\n\r\ncm = CouplingMap([\r\n    (0, 1),\r\n    (1, 2),\r\n    #\r\n    (3, 4),\r\n])\r\nqc = QuantumCircuit(cm.size(), cm.size())\r\nqc.cx(0, 1)\r\nqc.cx(1, 2)\r\nqc.cx(2, 0)\r\n#\r\nqc.cx(3, 4)\r\nqc.measure(qc.qubits, qc.clbits)\r\n\r\nout = transpile(qc, coupling_map=cm, seed_transpiler=0)\r\n```\r\n\r\n`out` contains `Barrier` instructions whose `label` attributes are not strings (they're `UUID` objects) and shouldn't exist at all, because the input circuit doesn't have any:\r\n\r\n```python\r\nfor i, ins in enumerate(out.data):\r\n    if ins.operation.name == "barrier":\r\n        print(i, repr(ins.operation.label))\r\n        # Coerce to string so I can draw the circuit after.\r\n        ins.operation.label = str(ins.operation.label)\r\n```\r\n```text\r\n8 UUID('31558989-c21a-4e5c-b014-01639295df7b')\r\n10 UUID('31558989-c21a-4e5c-b014-01639295df7b')\r\n12 UUID('31558989-c21a-4e5c-b014-01639295df7b')\r\n14 UUID('31558989-c21a-4e5c-b014-01639295df7b')\r\n15 UUID('31558989-c21a-4e5c-b014-01639295df7b')\r\n```\r\n\r\nI can't draw the circuit because the visualiser will explode, as will QPY (how this issue was detected).  If I coerce those particular labels just to be string instances, the circuit looks like this:\r\n```text\r\n                                                    ┌───┐                          ┌───┐                  31558989-c21a-4e5c-b014-01639295df7b ┌─┐\r\nq_2 -> 0 ───────────────────────X───────────────────┤ H ├───────■──────────────────┤ H ├───────────────────────────────────░───────────────────┤M├───\r\n                                │                   └───┘       │                  └───┘                                   ░                   └╥┘\r\n                                │                   ┌───┐     ┌─┴─┐                ┌───┐                  31558989-c21a-4e5c-b014-01639295df7b  ║ ┌─┐\r\nq_0 -> 1 ──■────────────────────X───────────────────┤ H ├──■──┤ X ├────────────────┤ H ├───────────────────────────────────░────────────────────╫─┤M├\r\n           │                                        └───┘  │  └───┘                └───┘                                   ░                    ║ └╥┘\r\n         ┌─┴─┐                ┌───┐                      ┌─┴─┐┌───┐ 31558989-c21a-4e5c-b014-01639295df7b                  ┌─┐                   ║  ║\r\nq_1 -> 2 ┤ X ├────────────────┤ H ├──────────────────────┤ X ├┤ H ├──────────────────░────────────────────────────────────┤M├───────────────────╫──╫─\r\n         └───┘                └───┘                      └───┘└───┘                  ░                                    └╥┘                   ║  ║\r\n               31558989-c21a-4e5c-b014-01639295df7b  ┌─┐                                                                   ║                    ║  ║\r\nq_3 -> 3 ──■────────────────────░────────────────────┤M├───────────────────────────────────────────────────────────────────╫────────────────────╫──╫─\r\n           │                    ░                    └╥┘                                                                   ║                    ║  ║\r\n         ┌─┴─┐ 31558989-c21a-4e5c-b014-01639295df7b   ║   ┌─┐                                                              ║                    ║  ║\r\nq_4 -> 4 ┤ X ├──────────────────░─────────────────────╫───┤M├──────────────────────────────────────────────────────────────╫────────────────────╫──╫─\r\n         └───┘                  ░                     ║   └╥┘                                                              ║                    ║  ║\r\n    c: 5/═════════════════════════════════════════════╩════╩═══════════════════════════════════════════════════════════════╩════════════════════╩══╩═\r\n                                                      3    4                                                               1                    0  2\r\n```\r\nso it's clear the temporary barrier we insert before measures isn't being recombined and then deleted.\r\n\n\n### What should happen?\n\n* The temporary barrier should have been recombined after the disjoint handling\r\n* The temporary barrier should have been removed before the circuit exited\r\n* We probably shouldn't use a literal `UUID` in the `Barrier.label` field since that's supposed to be constrained to be a string (everything except `Barrier` seems to enforce that, too).\n\n### Any suggestions?\n\n_No response_

diff --git a/qiskit/transpiler/passes/layout/disjoint_utils.py b/qiskit/transpiler/passes/layout/disjoint_utils.py\nindex ffbb5076c1ef..42f665c473a1 100644\n--- a/qiskit/transpiler/passes/layout/disjoint_utils.py\n+++ b/qiskit/transpiler/passes/layout/disjoint_utils.py\n@@ -107,7 +107,10 @@ def split_barriers(dag: DAGCircuit):\n         num_qubits = len(node.qargs)\n         if num_qubits == 1:\n             continue\n-        barrier_uuid = uuid.uuid4()\n+        if node.op.label:\n+            barrier_uuid = f"{node.op.label}_uuid={uuid.uuid4()}"\n+        else:\n+            barrier_uuid = f"_none_uuid={uuid.uuid4()}"\n         split_dag = DAGCircuit()\n         split_dag.add_qubits([Qubit() for _ in range(num_qubits)])\n         for i in range(num_qubits):\n@@ -124,10 +127,13 @@ def combine_barriers(dag: DAGCircuit, retain_uuid: bool = True):\n     qubit_indices = {bit: index for index, bit in enumerate(dag.qubits)}\n     uuid_map: dict[uuid.UUID, DAGOpNode] = {}\n     for node in dag.op_nodes(Barrier):\n-        if isinstance(node.op.label, uuid.UUID):\n-            barrier_uuid = node.op.label\n+        if node.op.label:\n+            if "_uuid=" in node.op.label:\n+                barrier_uuid = node.op.label\n+            else:\n+                continue\n             if barrier_uuid in uuid_map:\n-                other_node = uuid_map[node.op.label]\n+                other_node = uuid_map[barrier_uuid]\n                 num_qubits = len(other_node.qargs) + len(node.qargs)\n                 new_op = Barrier(num_qubits, label=barrier_uuid)\n                 new_node = dag.replace_block_with_op([node, other_node], new_op, qubit_indices)\n@@ -136,8 +142,11 @@ def combine_barriers(dag: DAGCircuit, retain_uuid: bool = True):\n                 uuid_map[barrier_uuid] = node\n     if not retain_uuid:\n         for node in dag.op_nodes(Barrier):\n-            if isinstance(node.op.label, uuid.UUID):\n+            if isinstance(node.op.label, str) and node.op.label.startswith("_none_uuid="):\n                 node.op.label = None\n+            elif isinstance(node.op.label, str) and "_uuid=" in node.op.label:\n+                original_label = "_uuid=".join(node.op.label.split("_uuid=")[:-1])\n+                node.op.label = original_label\n \n \n def require_layout_isolated_to_component(\n@@ -202,4 +211,7 @@ def separate_dag(dag: DAGCircuit) -> List[DAGCircuit]:\n         new_dag.remove_clbits(*idle_clbits)\n         combine_barriers(new_dag)\n         decomposed_dags.append(new_dag)\n+    # Reverse split barriers on input dag to avoid leaking out internal transformations as\n+    # part of splitting\n+    combine_barriers(dag, retain_uuid=False)\n     return decomposed_dags\ndiff --git a/releasenotes/notes/fix-leaking-split-barrier-7c143d6b13b96ced.yaml b/releasenotes/notes/fix-leaking-split-barrier-7c143d6b13b96ced.yaml\nnew file mode 100644\nindex 000000000000..5cd8f19dc150\n--- /dev/null\n+++ b/releasenotes/notes/fix-leaking-split-barrier-7c143d6b13b96ced.yaml\n@@ -0,0 +1,12 @@\n+---\n+fixes:\n+  - |\n+    Fixed an issue when using :func:`.transpile` or running a preset pass\n+    manager (such as generated by :func:`.generate_preset_pass_manager`) when\n+    targetting a backend that has disjoint connectivity adding extra barriers\n+    to the output :class:`.QuantumCircuit`. In some cases several\n+    single qubit :class:`.Barrier` directives would be included in the output\n+    circuit right before any final measurements in the circuit. This was\n+    internal state generated by the internal processing for disjoint\n+    connectivity that was incorrectly being added into the output circuit.\n+    Fixed `#11649 <https://github.com/Qiskit/qiskit/issues/11649>`__\n

diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py\nindex 6390a6e1c85b..ac1add5e0201 100644\n--- a/test/python/compiler/test_transpiler.py\n+++ b/test/python/compiler/test_transpiler.py\n@@ -3260,6 +3260,19 @@ def test_transpile_target_with_qubits_without_ops_circuit_too_large_disconnected\n         with self.assertRaises(TranspilerError):\n             transpile(qc, target=target, optimization_level=opt_level)\n \n+    @data(0, 1, 2, 3)\n+    def test_barrier_no_leak_disjoint_connectivity(self, opt_level):\n+        """Test that we don't leak an internal labelled barrier from disjoint layout processing."""\n+        cmap = CouplingMap([(0, 1), (1, 2), (3, 4)])\n+        qc = QuantumCircuit(cmap.size(), cmap.size())\n+        qc.cx(0, 1)\n+        qc.cx(1, 2)\n+        qc.cx(2, 0)\n+        qc.cx(3, 4)\n+        qc.measure(qc.qubits, qc.clbits)\n+        out = transpile(qc, coupling_map=cmap, optimization_level=opt_level)\n+        self.assertNotIn("barrier", out.count_ops())\n+\n     @data(0, 1, 2, 3)\n     def test_transpile_does_not_affect_backend_coupling(self, opt_level):\n         """Test that transpiliation of a circuit does not mutate the `CouplingMap` stored by a V2\n


Fix use of custom `dt` in `transpile` when target is provided (Qiskit/qiskit#11995)

<!--\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\nFixes #11994, I am not too sure if I should label it as a feature or a bugfix, I think it's both. This PR is currently blocking #11996.\r\n\r\n\r\n### Details and comments\r\n\r\n

Scheduling stage doesn't use custom dt when target provided

### Environment\r\n\r\n- **Qiskit version**: 1.1.0\r\n- **Python version**: 3.10.0\r\n- **Operating system**: MacOS\r\n\r\n\r\n### What is happening?\r\n\r\nThe expected behavior of `transpile` when a custom `dt` is specified (i.e. sth like `transpile(qc, backend=b, scheduling_method="asap", dt=dt / 2)` is that the scheduling stage will use this custom `dt` instead of the backend's one, but this behavior is not respected if the input backend contains a `target`. I believe that the root of the issue is that `dt` is encoded in the `inst_durations` object that is passed around, but the [`TimeUnitConversion` transformation pass is designed to overwrite `inst_durations` if a target is specified](https://github.com/Qiskit/qiskit/blob/main/qiskit/transpiler/passes/scheduling/time_unit_conversion.py#L43-L46), so the custom dt never gets to be used:\r\n```\r\n# TimeUnitConversion init\r\nself.inst_durations = inst_durations or InstructionDurations()\r\nif target is not None:\r\n     self.inst_durations = target.durations()\r\n```\r\n\r\nIn other words, the priorities are not consistent between the preset passmanager/transpile and the `TimeUnitConversion` pass (one prioritizes the custom durations, the other prioritizes the target).\r\n\r\n### How can we reproduce the issue?\r\n\r\nThis snippet works for the V1 backend and doesn't for the V2 backend:\r\n\r\n```\r\nfrom qiskit import QuantumCircuit\r\nfrom qiskit import transpile\r\nfrom qiskit_ibm_runtime.fake_provider import FakeHanoi, FakeHanoiV2\r\n\r\nbackend_v1 = FakeHanoi()\r\nbackend_v2 = FakeHanoiV2()\r\ndt = 2.2222222222222221e-10\r\n\r\n# from test_change_dt_in_transpile\r\nqc = QuantumCircuit(1, 1)\r\nqc.x(0)\r\nqc.measure(0, 0)\r\n\r\nfor b in [backend_v1, backend_v2]:\r\n  # default case\r\n  scheduled = transpile(qc, backend=b, scheduling_method="asap")\r\n  org_duration = scheduled.duration\r\n\r\n  # halve dt in sec = double duration in dt\r\n  scheduled = transpile(\r\n    qc, backend=b, scheduling_method="asap", dt=dt / 2\r\n  )\r\n  print(b, scheduled.duration == org_duration * 2)\r\n```\r\n\r\n### What should happen?\r\n\r\nThe custom dt shouldn't be bypassed.\r\n\r\n### Any suggestions?\r\n\r\nI think that this issue could be fixed if we establish a consistent priority of the custom `inst_durations` over the target's. A PR with a fix proposal will follow.

diff --git a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py\nindex d53c3fc4ef6a..e0931133c52e 100644\n--- a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py\n+++ b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py\n@@ -42,15 +42,16 @@ def __init__(self, inst_durations: InstructionDurations = None, target: Target =\n         Args:\n             inst_durations (InstructionDurations): A dictionary of durations of instructions.\n             target: The :class:`~.Target` representing the target backend, if both\n-                  ``inst_durations`` and ``target`` are specified then this argument will take\n-                  precedence and ``inst_durations`` will be ignored.\n-\n-\n+                  ``inst_durations`` and ``target`` are specified, the ``inst_durations`` argument\n+                  will take precedence and ``target`` will be ignored.\n         """\n         super().__init__()\n-        self.inst_durations = inst_durations or InstructionDurations()\n-        if target is not None:\n-            self.inst_durations = target.durations()\n+        if inst_durations is not None:\n+            self.inst_durations = inst_durations\n+        else:\n+            self.inst_durations = (\n+                target.durations() if target is not None else InstructionDurations()\n+            )\n \n     def run(self, dag: DAGCircuit):\n         """Run the TimeUnitAnalysis pass on `dag`.\ndiff --git a/releasenotes/notes/transpile-custom-dt-precedence-3c7354e40bbd1107.yaml b/releasenotes/notes/transpile-custom-dt-precedence-3c7354e40bbd1107.yaml\nnew file mode 100644\nindex 000000000000..c1436948f953\n--- /dev/null\n+++ b/releasenotes/notes/transpile-custom-dt-precedence-3c7354e40bbd1107.yaml\n@@ -0,0 +1,7 @@\n+---\n+features:\n+  - |\n+    The :class:`.TimeUnitConversion` transformation pass now gives precedence to the ``inst_durations``\n+    argument over the ``target`` argument. This fixes an unexpected behavior in :meth:`.transpile` where\n+    custom ``dt`` inputs would be ignored if a ``target`` was provided too (directly or as part of a \n+    :class:`.BackendV2` instance).\n\\ No newline at end of file\n

diff --git a/test/python/circuit/test_scheduled_circuit.py b/test/python/circuit/test_scheduled_circuit.py\nindex ec4bc72ffd06..9a3ebd5215da 100644\n--- a/test/python/circuit/test_scheduled_circuit.py\n+++ b/test/python/circuit/test_scheduled_circuit.py\n@@ -20,6 +20,7 @@\n from qiskit.circuit import Parameter\n from qiskit.circuit.duration import convert_durations_to_dt\n from qiskit.providers.fake_provider import Fake27QPulseV1, GenericBackendV2\n+from qiskit.providers.backend_compat import BackendV2Converter\n from qiskit.providers.basic_provider import BasicSimulator\n from qiskit.scheduler import ScheduleConfig\n from qiskit.transpiler.exceptions import TranspilerError\n@@ -44,6 +45,9 @@ def setUp(self):\n         # run when alignment values different to 1 are found.\n         self.backend_with_dt.configuration().timing_constraints = {}\n         self.backend_without_dt.configuration().timing_constraints = {}\n+        # Generate V2 backends\n+        self.backend_with_dt_v2 = BackendV2Converter(self.backend_with_dt)\n+        self.backend_without_dt_v2 = BackendV2Converter(self.backend_without_dt)\n         self.dt = 2.2222222222222221e-10\n         self.simulator_backend = BasicSimulator()\n \n@@ -79,24 +83,27 @@ def test_schedule_circuit_when_transpile_option_tells_dt(self):\n         qc.delay(100, 0, unit="ns")  # 450[dt]\n         qc.h(0)\n         qc.h(1)\n-        sc = transpile(\n-            qc,\n-            self.backend_without_dt,\n-            scheduling_method="alap",\n-            dt=self.dt,\n-            layout_method="trivial",\n-        )\n-        self.assertEqual(sc.duration, 450546)\n-        self.assertEqual(sc.unit, "dt")\n-        self.assertEqual(sc.data[0].operation.name, "delay")\n-        self.assertEqual(sc.data[0].operation.duration, 450450)\n-        self.assertEqual(sc.data[0].operation.unit, "dt")\n-        self.assertEqual(sc.data[1].operation.name, "rz")\n-        self.assertEqual(sc.data[1].operation.duration, 0)\n-        self.assertEqual(sc.data[1].operation.unit, "dt")\n-        self.assertEqual(sc.data[4].operation.name, "delay")\n-        self.assertEqual(sc.data[4].operation.duration, 450450)\n-        self.assertEqual(sc.data[4].operation.unit, "dt")\n+\n+        for backend in [self.backend_without_dt, self.backend_without_dt_v2]:\n+            with self.subTest(backend=backend):\n+                sc = transpile(\n+                    qc,\n+                    backend,\n+                    scheduling_method="alap",\n+                    dt=self.dt,\n+                    layout_method="trivial",\n+                )\n+                self.assertEqual(sc.duration, 450546)\n+                self.assertEqual(sc.unit, "dt")\n+                self.assertEqual(sc.data[0].operation.name, "delay")\n+                self.assertEqual(sc.data[0].operation.duration, 450450)\n+                self.assertEqual(sc.data[0].operation.unit, "dt")\n+                self.assertEqual(sc.data[1].operation.name, "rz")\n+                self.assertEqual(sc.data[1].operation.duration, 0)\n+                self.assertEqual(sc.data[1].operation.unit, "dt")\n+                self.assertEqual(sc.data[4].operation.name, "delay")\n+                self.assertEqual(sc.data[4].operation.duration, 450450)\n+                self.assertEqual(sc.data[4].operation.unit, "dt")\n \n     def test_schedule_circuit_in_sec_when_no_one_tells_dt(self):\n         """dt is unknown and all delays and gate times are in SI"""\n@@ -245,23 +252,26 @@ def test_unit_seconds_when_using_backend_durations(self):\n         qc.h(0)\n         qc.delay(500 * self.dt, 1, "s")\n         qc.cx(0, 1)\n-        # usual case\n-        scheduled = transpile(\n-            qc, backend=self.backend_with_dt, scheduling_method="alap", layout_method="trivial"\n-        )\n-        self.assertEqual(scheduled.duration, 1876)\n \n-        # update durations\n-        durations = InstructionDurations.from_backend(self.backend_with_dt)\n-        durations.update([("cx", [0, 1], 1000 * self.dt, "s")])\n-        scheduled = transpile(\n-            qc,\n-            backend=self.backend_with_dt,\n-            scheduling_method="alap",\n-            instruction_durations=durations,\n-            layout_method="trivial",\n-        )\n-        self.assertEqual(scheduled.duration, 1500)\n+        for backend in [self.backend_with_dt, self.backend_with_dt_v2]:\n+            with self.subTest(backend=backend):\n+                # usual case\n+                scheduled = transpile(\n+                    qc, backend=backend, scheduling_method="alap", layout_method="trivial"\n+                )\n+                self.assertEqual(scheduled.duration, 1876)\n+\n+                # update durations\n+                durations = InstructionDurations.from_backend(self.backend_with_dt)\n+                durations.update([("cx", [0, 1], 1000 * self.dt, "s")])\n+                scheduled = transpile(\n+                    qc,\n+                    backend=backend,\n+                    scheduling_method="alap",\n+                    instruction_durations=durations,\n+                    layout_method="trivial",\n+                )\n+                self.assertEqual(scheduled.duration, 1500)\n \n     def test_per_qubit_durations(self):\n         """See: https://github.com/Qiskit/qiskit-terra/issues/5109"""\n@@ -348,15 +358,15 @@ def test_change_dt_in_transpile(self):\n         qc = QuantumCircuit(1, 1)\n         qc.x(0)\n         qc.measure(0, 0)\n-        # default case\n-        scheduled = transpile(qc, backend=self.backend_with_dt, scheduling_method="asap")\n-        org_duration = scheduled.duration\n \n-        # halve dt in sec = double duration in dt\n-        scheduled = transpile(\n-            qc, backend=self.backend_with_dt, scheduling_method="asap", dt=self.dt / 2\n-        )\n-        self.assertEqual(scheduled.duration, org_duration * 2)\n+        for backend in [self.backend_with_dt, self.backend_with_dt_v2]:\n+            with self.subTest(backend=backend):\n+                # default case\n+                scheduled = transpile(qc, backend=backend, scheduling_method="asap")\n+                org_duration = scheduled.duration\n+                # halve dt in sec = double duration in dt\n+                scheduled = transpile(qc, backend=backend, scheduling_method="asap", dt=self.dt / 2)\n+                self.assertEqual(scheduled.duration, org_duration * 2)\n \n     @data("asap", "alap")\n     def test_duration_on_same_instruction_instance(self, scheduling_method):\n


Support ECR gates in `Pauli.evolve(QuantumCircuit)` (Qiskit/qiskit#12095)

<!--\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- [X] I have added the tests to cover my changes.\r\n- [ ] I have updated the documentation accordingly.\r\n- [X] I have read the CONTRIBUTING document.\r\n-->\r\n\r\n### Summary\r\nShould fix #12086 and also #12093.\r\n\r\n\r\n### Details and comments\r\n\r\n\r\n

`Pauli.evolve()` should recognize ECR gates as Clifford gates

### Environment\n\n- **Qiskit version**: 1.0.2\r\n- **Python version**: 3.11\r\n- **Operating system**: macos\r\n\n\n### What is happening?\n\n```\r\nqc = QuantumCircuit(2)\r\nqc.ecr(0,1)\r\nqi.Pauli('XX').evolve(qc)\r\n```\r\n\r\nThe above call to evolve() raises an error even though qc contains only Clifford gates:\r\n\r\n```\r\nQiskitError: 'Cannot apply Instruction: u'\r\n```\n\n### How can we reproduce the issue?\n\n```\r\nqc = QuantumCircuit(2)\r\nqc.ecr(0,1)\r\nqi.Pauli('XX').evolve(qc)\r\n```\n\n### What should happen?\n\nECR gates should be recognized as Clifford gates, and the Pauli should evolve successfully.\r\n\r\nRelated: #12085 \n\n### Any suggestions?\n\n_No response_

diff --git a/qiskit/quantum_info/operators/symplectic/base_pauli.py b/qiskit/quantum_info/operators/symplectic/base_pauli.py\nindex 28bb647d0364..e43eca4aff21 100644\n--- a/qiskit/quantum_info/operators/symplectic/base_pauli.py\n+++ b/qiskit/quantum_info/operators/symplectic/base_pauli.py\n@@ -541,75 +541,50 @@ def _append_circuit(self, circuit, qargs=None):\n         if qargs is None:\n             qargs = list(range(self.num_qubits))\n \n-        if isinstance(circuit, QuantumCircuit):\n-            gate = circuit.to_instruction()\n-        else:\n+        if not isinstance(circuit, QuantumCircuit):\n             gate = circuit\n \n-        # Basis Clifford Gates\n-        basis_1q = {\n-            "i": _evolve_i,\n-            "id": _evolve_i,\n-            "iden": _evolve_i,\n-            "x": _evolve_x,\n-            "y": _evolve_y,\n-            "z": _evolve_z,\n-            "h": _evolve_h,\n-            "s": _evolve_s,\n-            "sdg": _evolve_sdg,\n-            "sinv": _evolve_sdg,\n-        }\n-        basis_2q = {"cx": _evolve_cx, "cz": _evolve_cz, "cy": _evolve_cy, "swap": _evolve_swap}\n-\n-        # Non-Clifford gates\n-        non_clifford = ["t", "tdg", "ccx", "ccz"]\n-\n-        if isinstance(gate, str):\n-            # Check if gate is a valid Clifford basis gate string\n-            if gate not in basis_1q and gate not in basis_2q:\n-                raise QiskitError(f"Invalid Clifford gate name string {gate}")\n-            name = gate\n-        else:\n-            # Assume gate is an Instruction\n-            name = gate.name\n-\n-        # Apply gate if it is a Clifford basis gate\n-        if name in non_clifford:\n-            raise QiskitError(f"Cannot update Pauli with non-Clifford gate {name}")\n-        if name in basis_1q:\n-            if len(qargs) != 1:\n-                raise QiskitError("Invalid qubits for 1-qubit gate.")\n-            return basis_1q[name](self, qargs[0])\n-        if name in basis_2q:\n-            if len(qargs) != 2:\n-                raise QiskitError("Invalid qubits for 2-qubit gate.")\n-            return basis_2q[name](self, qargs[0], qargs[1])\n-\n-        # If not a Clifford basis gate we try to unroll the gate and\n-        # raise an exception if unrolling reaches a non-Clifford gate.\n-        if gate.definition is None:\n-            raise QiskitError(f"Cannot apply Instruction: {gate.name}")\n-        if not isinstance(gate.definition, QuantumCircuit):\n-            raise QiskitError(\n-                "{} instruction definition is {}; expected QuantumCircuit".format(\n-                    gate.name, type(gate.definition)\n+            if isinstance(gate, str):\n+                # Check if gate is a valid Clifford basis gate string\n+                if gate not in _basis_1q and gate not in _basis_2q:\n+                    raise QiskitError(f"Invalid Clifford gate name string {gate}")\n+                name = gate\n+            else:\n+                # Assume gate is an Instruction\n+                name = gate.name\n+\n+            # Apply gate if it is a Clifford basis gate\n+            if name in _non_clifford:\n+                raise QiskitError(f"Cannot update Pauli with non-Clifford gate {name}")\n+            if name in _basis_1q:\n+                if len(qargs) != 1:\n+                    raise QiskitError("Invalid qubits for 1-qubit gate.")\n+                return _basis_1q[name](self, qargs[0])\n+            if name in _basis_2q:\n+                if len(qargs) != 2:\n+                    raise QiskitError("Invalid qubits for 2-qubit gate.")\n+                return _basis_2q[name](self, qargs[0], qargs[1])\n+\n+            # If not a Clifford basis gate we try to unroll the gate and\n+            # raise an exception if unrolling reaches a non-Clifford gate.\n+            if gate.definition is None:\n+                raise QiskitError(f"Cannot apply Instruction: {gate.name}")\n+            if not isinstance(gate.definition, QuantumCircuit):\n+                raise QiskitError(\n+                    "{} instruction definition is {}; expected QuantumCircuit".format(\n+                        gate.name, type(gate.definition)\n+                    )\n                 )\n-            )\n \n-        flat_instr = gate.definition\n-        bit_indices = {\n-            bit: index\n-            for bits in [flat_instr.qubits, flat_instr.clbits]\n-            for index, bit in enumerate(bits)\n-        }\n+            circuit = gate.definition\n \n-        for instruction in flat_instr:\n+        for instruction in circuit:\n             if instruction.clbits:\n                 raise QiskitError(\n                     f"Cannot apply Instruction with classical bits: {instruction.operation.name}"\n                 )\n             # Get the integer position of the flat register\n-            new_qubits = [qargs[bit_indices[tup]] for tup in instruction.qubits]\n+            new_qubits = [qargs[circuit.find_bit(qb)[0]] for qb in instruction.qubits]\n             self._append_circuit(instruction.operation, new_qubits)\n \n         # Since the individual gate evolution functions don't take mod\n@@ -715,6 +690,42 @@ def _evolve_swap(base_pauli, q1, q2):\n     return base_pauli\n \n \n+def _evolve_ecr(base_pauli, q1, q2):\n+    """Update P -> ECR.P.ECR"""\n+    base_pauli = _evolve_s(base_pauli, q1)\n+    base_pauli = _evolve_h(base_pauli, q2)\n+    base_pauli = _evolve_s(base_pauli, q2)\n+    base_pauli = _evolve_h(base_pauli, q2)\n+    base_pauli = _evolve_cx(base_pauli, q1, q2)\n+    base_pauli = _evolve_x(base_pauli, q1)\n+    return base_pauli\n+\n+\n def _count_y(x, z, dtype=None):\n     """Count the number of I Paulis"""\n     return (x & z).sum(axis=1, dtype=dtype)\n+\n+\n+# Basis Clifford Gates\n+_basis_1q = {\n+    "i": _evolve_i,\n+    "id": _evolve_i,\n+    "iden": _evolve_i,\n+    "x": _evolve_x,\n+    "y": _evolve_y,\n+    "z": _evolve_z,\n+    "h": _evolve_h,\n+    "s": _evolve_s,\n+    "sdg": _evolve_sdg,\n+    "sinv": _evolve_sdg,\n+}\n+_basis_2q = {\n+    "cx": _evolve_cx,\n+    "cz": _evolve_cz,\n+    "cy": _evolve_cy,\n+    "swap": _evolve_swap,\n+    "ecr": _evolve_ecr,\n+}\n+\n+# Non-Clifford gates\n+_non_clifford = ["t", "tdg", "ccx", "ccz"]\ndiff --git a/releasenotes/notes/fix-pauli-evolve-ecr-and-name-bugs.yaml b/releasenotes/notes/fix-pauli-evolve-ecr-and-name-bugs.yaml\nnew file mode 100644\nindex 000000000000..143dfadf31e2\n--- /dev/null\n+++ b/releasenotes/notes/fix-pauli-evolve-ecr-and-name-bugs.yaml\n@@ -0,0 +1,6 @@\n+---\n+fixes:\n+  - |\n+    :meth:`.Pauli.evolve` now correctly handles quantum circuits containing ECR gates. Formerly they were not recognized as Clifford gates, and an error was raised.\n+  - |\n+    Fixed a bug in :meth:`.Pauli.evolve` where evolving by a circuit with a name matching certain Clifford gates ('cx', 'cz', etc) would evolve the Pauli according to the name of the circuit, not by the contents of the circuit. This bug occurred only with the non-default option ``frame='s'``.\n

diff --git a/test/python/quantum_info/operators/symplectic/test_pauli.py b/test/python/quantum_info/operators/symplectic/test_pauli.py\nindex c760407ad3fa..875dd9237810 100644\n--- a/test/python/quantum_info/operators/symplectic/test_pauli.py\n+++ b/test/python/quantum_info/operators/symplectic/test_pauli.py\n@@ -36,6 +36,7 @@\n     CZGate,\n     CYGate,\n     SwapGate,\n+    ECRGate,\n     EfficientSU2,\n )\n from qiskit.circuit.library.generalized_gates import PauliGate\n@@ -410,7 +411,11 @@ def test_evolve_clifford1(self, gate, label):\n         self.assertEqual(value, value_h)\n         self.assertEqual(value_inv, value_s)\n \n-    @data(*it.product((CXGate(), CYGate(), CZGate(), SwapGate()), pauli_group_labels(2, False)))\n+    @data(\n+        *it.product(\n+            (CXGate(), CYGate(), CZGate(), SwapGate(), ECRGate()), pauli_group_labels(2, False)\n+        )\n+    )\n     @unpack\n     def test_evolve_clifford2(self, gate, label):\n         """Test evolve method for 2-qubit Clifford gates."""\n@@ -439,6 +444,7 @@ def test_evolve_clifford2(self, gate, label):\n                 CYGate(),\n                 CZGate(),\n                 SwapGate(),\n+                ECRGate(),\n             ),\n             [int, np.int8, np.uint8, np.int16, np.uint16, np.int32, np.uint32, np.int64, np.uint64],\n         )\n@@ -468,6 +474,13 @@ def test_evolve_clifford_qargs(self):\n         self.assertEqual(value, value_h)\n         self.assertEqual(value_inv, value_s)\n \n+    @data("s", "h")\n+    def test_evolve_with_misleading_name(self, frame):\n+        """Test evolve by circuit contents, not by name (fixed bug)."""\n+        circ = QuantumCircuit(2, name="cx")\n+        p = Pauli("IX")\n+        self.assertEqual(p, p.evolve(circ, frame=frame))\n+\n     def test_barrier_delay_sim(self):\n         """Test barrier and delay instructions can be simulated"""\n         target_circ = QuantumCircuit(2)\ndiff --git a/test/python/quantum_info/operators/symplectic/test_pauli_list.py b/test/python/quantum_info/operators/symplectic/test_pauli_list.py\nindex 7ab6feaefe98..0ef7079f461a 100644\n--- a/test/python/quantum_info/operators/symplectic/test_pauli_list.py\n+++ b/test/python/quantum_info/operators/symplectic/test_pauli_list.py\n@@ -33,6 +33,7 @@\n     XGate,\n     YGate,\n     ZGate,\n+    ECRGate,\n )\n from qiskit.quantum_info.operators import (\n     Clifford,\n@@ -1997,10 +1998,12 @@ def test_evolve_clifford1(self, gate):\n             CYGate(),\n             CZGate(),\n             SwapGate(),\n+            ECRGate(),\n             Clifford(CXGate()),\n             Clifford(CYGate()),\n             Clifford(CZGate()),\n             Clifford(SwapGate()),\n+            Clifford(ECRGate()),\n         )\n     )\n     def test_evolve_clifford2(self, gate):\n@@ -2033,6 +2036,7 @@ def test_phase_dtype_evolve_clifford(self):\n             CYGate(),\n             CZGate(),\n             SwapGate(),\n+            ECRGate(),\n         )\n         dtypes = [\n             int,\n


fix issue/12311 with fractional gate in basis (Qiskit/qiskit#12511)

<!--\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- [x] I have added the tests to cover my changes.\r\n- [x] I have updated the documentation accordingly.\r\n- [x] I have read the CONTRIBUTING document.\r\n-->\r\n\r\n### Summary\r\nThis fixes an issue where an exception would be raised if transpiling a unitary gate to a backend with an RZZGate in its basis however transpiling with only `basis_gates` specified would transpile fine. \r\n\r\nfixes #12311 \r\n\r\n### Details and comments\r\nIn this case, where RZZ is the only entangling gate in the basis, decomposition of a 2q unitary gate wouldn't get synthesized in the `UnitarySynthesis` pass but would instead be synthesized in the `HighLevelSynthesis` pass because this would call the instruction's `_define` method which calls `two_qubit_cnot_decompose`. A subsequent `BasisTranslator` pass ultimately maps the cx to the rzz basis.  I'm not sure whether this indirect path to unitary synthesis is intentional.\r\n\r\nThis pr resolves the specific example posed in the issue.\r\n\r\n

2Q gate synthesis with fractional gates

### Environment\r\n\r\n- **Qiskit version**: main\r\n- **Python version**: doesn't matter\r\n- **Operating system**: doesn't matter\r\n\r\n\r\n### What is happening?\r\n\r\nWhen I build a Qiskit `Target` that supports two qubit basis gates with arbitrary rotation angle, it cannot transpile the circuit if the added basis gate is other than `RZXGate` or `RXXGate`.\r\n\r\n### How can we reproduce the issue?\r\n\r\n```python\r\nfrom qiskit.providers.fake_provider import GenericBackendV2\r\nfrom qiskit import quantum_info as qi, transpile, QuantumCircuit\r\n\r\nbackend = GenericBackendV2(2, basis_gates=["rz", "rx", "rzz"])\r\nqc = QuantumCircuit(2)\r\nqc.unitary(qi.random_unitary(4), [0, 1])\r\nisa = transpile(qc, target=backend.target)\r\n```\r\nthis results in an error\r\n```\r\nTypeError: ParameterExpression with unbound parameters (dict_keys([Parameter(ϴ)])) cannot be cast to a float.\r\n```\r\nbecause it tries to get the unitary matrix of parameterized `RZZGate(Parameter(ϴ))` instruction.\r\n\r\nOn the other hand, it transpiles circuit when I directly specify the basis gates.\r\n```python\r\nisa = transpile(qc, basis_gates=["rz", "rx", "rzz"])\r\n```\r\n\r\n### What should happen?\r\n\r\n- Qiskit transpiler should support arbitrary fractional two qubit basis gates, e.g. `RZX, RXX, RYY, RZZ, RXY, ...`. \r\n- Transpiling with `Target` and giving `basis_gates` should behave identically.\r\n\r\nThis is related but might be a feature request; even if I give `RZXGate(Parameter(ϴ))` in the target, current synthesis algorithm only picks pi/4 rotation. The synthesis pass should consider another angle if there is possibility of reducing the circuit depth.\r\n\r\n### Any suggestions?\r\n\r\nNo. I'm not familiar with synthesis passes.

diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py\nindex ab7c5e04649f..19356adf69c9 100644\n--- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py\n+++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py\n@@ -49,6 +49,7 @@\n     CZGate,\n     RXXGate,\n     RZXGate,\n+    RZZGate,\n     ECRGate,\n     RXGate,\n     SXGate,\n@@ -779,6 +780,8 @@ def _replace_parameterized_gate(op):\n                 op = RXXGate(pi / 2)\n             elif isinstance(op, RZXGate) and isinstance(op.params[0], Parameter):\n                 op = RZXGate(pi / 4)\n+            elif isinstance(op, RZZGate) and isinstance(op.params[0], Parameter):\n+                op = RZZGate(pi / 2)\n             return op\n \n         try:\n

diff --git a/test/python/transpiler/test_basis_translator.py b/test/python/transpiler/test_basis_translator.py\nindex fc933cd8f666..e1c8063a4575 100644\n--- a/test/python/transpiler/test_basis_translator.py\n+++ b/test/python/transpiler/test_basis_translator.py\n@@ -16,6 +16,7 @@\n import os\n \n from numpy import pi\n+import scipy\n \n from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit\n from qiskit import transpile\n@@ -33,13 +34,17 @@\n     XGate,\n     SXGate,\n     CXGate,\n+    RXGate,\n+    RZZGate,\n )\n from qiskit.converters import circuit_to_dag, dag_to_circuit, circuit_to_instruction\n from qiskit.exceptions import QiskitError\n+from qiskit.providers.fake_provider import GenericBackendV2\n from qiskit.quantum_info import Operator\n from qiskit.transpiler.target import Target, InstructionProperties\n from qiskit.transpiler.exceptions import TranspilerError\n from qiskit.transpiler.passes.basis import BasisTranslator, UnrollCustomDefinitions\n+from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n from qiskit.circuit.library.standard_gates.equivalence_library import (\n     StandardEquivalenceLibrary as std_eqlib,\n )\n@@ -1223,3 +1228,48 @@ def __init__(self):\n \n         out = BasisTranslator(eq_lib, {"my_h", "my_cx"}, target)(qc)\n         self.assertEqual(out, expected)\n+\n+    def test_fractional_gate_in_basis_from_string(self):\n+        """Test transpiling with RZZ in basis with only basis_gates option."""\n+        num_qubits = 2\n+        seed = 9169\n+        basis_gates = ["rz", "rx", "rzz"]\n+        qc = QuantumCircuit(num_qubits)\n+        mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed)\n+        qc.unitary(mat, range(num_qubits))\n+        pm = generate_preset_pass_manager(\n+            optimization_level=1, basis_gates=basis_gates, seed_transpiler=134\n+        )\n+        cqc = pm.run(qc)\n+        self.assertEqual(Operator(qc), Operator(cqc))\n+\n+    def test_fractional_gate_in_basis_from_backendv2(self):\n+        """Test transpiling with RZZ in basis of backendv2."""\n+        num_qubits = 2\n+        seed = 9169\n+        basis_gates = ["rz", "rx", "rzz"]\n+        qc = QuantumCircuit(num_qubits)\n+        mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed)\n+        qc.unitary(mat, range(num_qubits))\n+        backend = GenericBackendV2(num_qubits, basis_gates=basis_gates)\n+        target = backend.target\n+        pm = generate_preset_pass_manager(optimization_level=1, target=target, seed_transpiler=134)\n+        cqc = pm.run(qc)\n+        self.assertEqual(Operator(qc), Operator.from_circuit(cqc))\n+\n+    def test_fractional_gate_in_basis_from_custom_target(self):\n+        """Test transpiling with RZZ in basis of custom target."""\n+        num_qubits = 2\n+        seed = 9169\n+        qc = QuantumCircuit(num_qubits)\n+        mat = scipy.stats.unitary_group.rvs(2**num_qubits, random_state=seed)\n+        qc.unitary(mat, range(num_qubits))\n+        target = Target()\n+        target.add_instruction(RZGate(self.theta), {(i,): None for i in range(qc.num_qubits)})\n+        target.add_instruction(RXGate(self.phi), {(i,): None for i in range(qc.num_qubits)})\n+        target.add_instruction(\n+            RZZGate(self.lam), {(i, i + 1): None for i in range(qc.num_qubits - 1)}\n+        )\n+        pm = generate_preset_pass_manager(optimization_level=1, target=target, seed_transpiler=134)\n+        cqc = pm.run(qc)\n+        self.assertEqual(Operator(qc), Operator.from_circuit(cqc))\n


Fix setter so that `SparsePauliOp.paulis.phase` stays zero (Qiskit/qiskit#12884)

<!--\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\nGoal is to fix #12883.\r\n\r\nCurrently several `SPO` methods assume that `SPO.paulis.phase` is all zero. However nonzero phase is allowed by the `paulis()` setter, leading to incorrect results when those other methods are called. \r\n\r\n### Details and comments\r\nIn this small PR we update the `paulis()` setter to absorb the phase into the SPO `coeffs`. \r\n

several `SparsePauliOp` methods ignore `self.paulis.phase`

### Environment\r\n\r\n- **Qiskit version**: 1.1.1\r\n- **Python version**: 3.11.6\r\n- **Operating system**: macos\r\n\r\n\r\n### What is happening?\r\n\r\nIf I understand it right, `SparsePauliOp.simplify()` does not correctly take into account the `phase` attribute of its underlying `PauliList`.\r\n\r\nThe same also seems true for several `SparsePauliOp` methods under the header "LinearOp Methods", like `conjugate`, and `adjoint`, which update `self.coeffs` but AFAIK do not look at `self.paulis.phase`.\r\n\r\n(I haven't looked exhaustively for other methods that do not handle `paulis.phase`.)\r\n\r\n\r\n### How can we reproduce the issue?\r\n\r\nFor example, define a `SparsePauliOp`, and then set its `paulis` attribute to a new value with nonzero `phase`:\r\n```\r\nspo = qi.SparsePauliOp(['X', 'X'])\r\nspo.paulis = ['X','-X']\r\nprint(spo)\r\n```\r\n```\r\nSparsePauliOp(['X', '-X'],\r\n              coeffs=[1.+0.j, 1.+0.j])\r\n```\r\nSo far so good. But now call simplify:\r\n```\r\nspo.simplify()\r\n```\r\n```\r\nSparsePauliOp(['X'],\r\n              coeffs=[2.+0.j])\r\n```\r\nAFAIK this is incorrect. \r\n\r\n### What should happen?\r\n\r\nThe example above should instead match this result where we construct the SPO differently:\r\n```\r\nspo_2 = qi.SparsePauliOp(['X','-X'])\r\nspo_2.simplify()\r\n```\r\n```\r\nSparsePauliOp(['I'],\r\n              coeffs=[0.+0.j])\r\n```\r\n\r\n### Any suggestions?\r\n\r\nEither we need to fix all the methods to consider `paulis.phase`, or (probably better) we need to more strictly require that the `paulis` attribute never has any phase, e.g. updating the `paulis()` setter to move the phase into the `coeffs` (don't know what else would need to change).\r\n\r\nAnother example:\r\n```\r\nspo_1 = qi.SparsePauliOp(['X'])\r\nspo_1.paulis = qi.PauliList(['-1jX'])\r\nspo_1\r\n```\r\n```\r\nSparsePauliOp(['-iX'],\r\n              coeffs=[1.+0.j])\r\n```\r\nNow try `conjugate()`:\r\n```\r\nspo_1.conjugate()\r\n```\r\n```\r\nSparsePauliOp(['-iX'],\r\n              coeffs=[1.-0.j])\r\n```

diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py\nindex 10d76f8719af..4621019a9d81 100644\n--- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py\n+++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py\n@@ -256,6 +256,8 @@ def paulis(self, value):\n             raise ValueError(\n                 f"incorrect number of operators: expected {len(self.paulis)}, got {len(value)}"\n             )\n+        self.coeffs *= (-1j) ** value.phase\n+        value.phase = 0\n         self._pauli_list = value\n \n     @property\ndiff --git a/releasenotes/notes/fix-sparsepauliop-phase-bug-2b24f4b775ca564f.yaml b/releasenotes/notes/fix-sparsepauliop-phase-bug-2b24f4b775ca564f.yaml\nnew file mode 100644\nindex 000000000000..ff1cf96dd711\n--- /dev/null\n+++ b/releasenotes/notes/fix-sparsepauliop-phase-bug-2b24f4b775ca564f.yaml\n@@ -0,0 +1,8 @@\n+---\n+fixes:\n+  - |\n+    Fixed a bug when :attr:`.SparsePauliOp.paulis` is set to be a :class:`.PauliList` with nonzero \n+    phase, where subsequent calls to several :class:`.SparsePauliOp` methods would produce \n+    incorrect results. Now when :attr:`.SparsePauliOp.paulis` is set to a :class:`.PauliList` with \n+    nonzero phase, the phase is absorbed into :attr:`.SparsePauliOp.coeffs`, and the phase of the \n+    input :class:`.PauliList` is set to zero.\n

diff --git a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py\nindex 4964b0c6a609..dedd84279a8d 100644\n--- a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py\n+++ b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py\n@@ -1097,6 +1097,25 @@ def test_paulis_setter_rejects_bad_inputs(self):\n         with self.assertRaisesRegex(ValueError, "incorrect number of operators"):\n             op.paulis = PauliList([Pauli("XY"), Pauli("ZX"), Pauli("YZ")])\n \n+    def test_paulis_setter_absorbs_phase(self):\n+        """Test that the setter for `paulis` absorbs `paulis.phase` to `self.coeffs`."""\n+        coeffs_init = np.array([1, 1j])\n+        op = SparsePauliOp(["XY", "ZX"], coeffs=coeffs_init)\n+        paulis_new = PauliList(["-1jXY", "1jZX"])\n+        op.paulis = paulis_new\n+        # Paulis attribute should have no phase:\n+        self.assertEqual(op.paulis, PauliList(["XY", "ZX"]))\n+        # Coeffs attribute should now include that phase:\n+        self.assertTrue(np.allclose(op.coeffs, coeffs_init * np.array([-1j, 1j])))\n+        # The phase of the input array is now zero:\n+        self.assertTrue(np.allclose(paulis_new.phase, np.array([0, 0])))\n+\n+    def test_paulis_setter_absorbs_phase_2(self):\n+        """Test that `paulis` setter followed by `simplify()` handle phase OK."""\n+        spo = SparsePauliOp(["X", "X"])\n+        spo.paulis = ["X", "-X"]\n+        self.assertEqual(spo.simplify(), SparsePauliOp(["I"], coeffs=[0.0 + 0.0j]))\n+\n     def test_apply_layout_with_transpile(self):\n         """Test the apply_layout method with a transpiler layout."""\n         psi = EfficientSU2(4, reps=4, entanglement="circular")\n


Fix `UnitaryGate.repeat()` method (Qiskit/qiskit#12986)

### Summary\r\nCloses #11990 : error when calling the `UnitaryGate.repeat()` method (inherited from the `Gate` class).\r\n\r\n### Details and comments\r\nThe method `repeat()` internally calls the private method `_return_repeat()` which is inherited from the `Gate` class.\r\nIt creates a new `Gate` object with arguments `params=self.params`, which is expected to be of type `ParameterExpression` or `float`. However the parameters are of type `numpy.ndarray` for a `UnitaryGate`. \r\nA simple fix is to omit these parameters (`params=[]`) as they are not directly used by the returned `Gate`. Actually these parameters are still stored in the `repeat()` method and used only there.\r\n

### Environment\n\n- **Qiskit version**: 1.0.2\r\n- **Python version**: 3.11.8\r\n- **Operating system**: Ubuntu 22.04.3 LTS\n\n### What is happening?\n\nRunning the below code triggered a qiskit.circuit.exceptions.CircuitError: "Invalid param type <class 'numpy.ndarray'> for gate unitary*1." I found this bug happens for some other Gates as well. Below is a minimal reproduction example using CUGate.\n\n### How can we reproduce the issue?\n\n```\r\nfrom qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister\r\nfrom qiskit.circuit.library.standard_gates import CUGate\r\n\r\nqr = QuantumRegister(2, name='qr')\r\ncr = ClassicalRegister(2, name='cr')\r\nqc = QuantumCircuit(qr, cr, name='qc')\r\n\r\nqc.append(CUGate(0, 0, 0, 0).power(1).repeat(1), qargs=qr)\r\n```\n\n### What should happen?\n\n```\r\nTraceback (most recent call last):\r\n  File "/mnt/tests/reproduction/reproduce_invalid_param_type.py", line 8, in <module>\r\n    qc.append(CUGate(0, 0, 0, 0).power(1).repeat(1), qargs=qr)\r\n              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File "/root/anaconda3/envs/qiskit1.0.2/lib/python3.11/site-packages/qiskit/circuit/instruction.py", line 591, in repeat\r\n    instruction = self._return_repeat(n)\r\n                  ^^^^^^^^^^^^^^^^^^^^^^\r\n  File "/root/anaconda3/envs/qiskit1.0.2/lib/python3.11/site-packages/qiskit/circuit/gate.py", line 87, in _return_repeat\r\n    return Gate(name=f"{self.name}*{exponent}", num_qubits=self.num_qubits, params=self.params)\r\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File "/root/anaconda3/envs/qiskit1.0.2/lib/python3.11/site-packages/qiskit/circuit/gate.py", line 46, in __init__\r\n    super().__init__(name, num_qubits, 0, params, label=label, duration=duration, unit=unit)\r\n  File "/root/anaconda3/envs/qiskit1.0.2/lib/python3.11/site-packages/qiskit/circuit/instruction.py", line 108, in __init__\r\n    self.params = params  # must be at last (other properties may be required for validation)\r\n    ^^^^^^^^^^^\r\n  File "/root/anaconda3/envs/qiskit1.0.2/lib/python3.11/site-packages/qiskit/circuit/instruction.py", line 287, in params\r\n    self._params.append(self.validate_parameter(single_param))\r\n                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File "/root/anaconda3/envs/qiskit1.0.2/lib/python3.11/site-packages/qiskit/circuit/gate.py", line 242, in validate_parameter\r\n    raise CircuitError(f"Invalid param type {type(parameter)} for gate {self.name}.")\r\nqiskit.circuit.exceptions.CircuitError: "Invalid param type <class 'numpy.ndarray'> for gate unitary*1."\r\n```\n\n### Any suggestions?\n\n_No response_

diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py\nindex 37fd19e2022a..2f1c09668252 100644\n--- a/qiskit/circuit/gate.py\n+++ b/qiskit/circuit/gate.py\n@@ -97,7 +97,10 @@ def __pow__(self, exponent: float) -> "Gate":\n         return self.power(exponent)\n \n     def _return_repeat(self, exponent: float) -> "Gate":\n-        return Gate(name=f"{self.name}*{exponent}", num_qubits=self.num_qubits, params=self.params)\n+        gate = Gate(name=f"{self.name}*{exponent}", num_qubits=self.num_qubits, params=[])\n+        gate.validate_parameter = self.validate_parameter\n+        gate.params = self.params\n+        return gate\n \n     def control(\n         self,\ndiff --git a/releasenotes/notes/fix_11990-8551c7250207fc76.yaml b/releasenotes/notes/fix_11990-8551c7250207fc76.yaml\nnew file mode 100644\nindex 000000000000..68912403cd39\n--- /dev/null\n+++ b/releasenotes/notes/fix_11990-8551c7250207fc76.yaml\n@@ -0,0 +1,6 @@\n+---\n+fixes:\n+  - |\n+    Fixes an error when calling the method :meth:`.UnitaryGate.repeat`. \n+    Refer to `#11990 <https://github.com/Qiskit/qiskit/issues/11990>`_ for more\n+    details.\n\\ No newline at end of file\n

diff --git a/test/python/circuit/library/test_linear_function.py b/test/python/circuit/library/test_linear_function.py\nindex a3df1e9664a2..b7756faa7aa7 100644\n--- a/test/python/circuit/library/test_linear_function.py\n+++ b/test/python/circuit/library/test_linear_function.py\n@@ -502,6 +502,19 @@ def test_clifford_linear_function_equivalence(self, num_qubits):\n         self.assertEqual(Clifford(qc_to_linear_function), qc_to_clifford)\n         self.assertEqual(qc_to_linear_function, LinearFunction(qc_to_clifford))\n \n+    @data(2, 3)\n+    def test_repeat_method(self, num_qubits):\n+        """Test the repeat() method."""\n+        rng = np.random.default_rng(127)\n+        for num_gates, seed in zip(\n+            [0, 5, 5 * num_qubits], rng.integers(100000, size=10, dtype=np.uint64)\n+        ):\n+            # create a random linear circuit\n+            linear_circuit = random_linear_circuit(num_qubits, num_gates, seed=seed)\n+            operator = Operator(linear_circuit)\n+            linear_function = LinearFunction(linear_circuit)\n+            self.assertTrue(Operator(linear_function.repeat(2)), operator @ operator)\n+\n \n if __name__ == "__main__":\n     unittest.main()\ndiff --git a/test/python/circuit/library/test_permutation.py b/test/python/circuit/library/test_permutation.py\nindex 30d504d7dc8f..a2ec59431fe3 100644\n--- a/test/python/circuit/library/test_permutation.py\n+++ b/test/python/circuit/library/test_permutation.py\n@@ -104,6 +104,12 @@ def test_inverse(self):\n         expected_inverse_perm = PermutationGate([3, 0, 5, 1, 4, 2])\n         self.assertTrue(np.array_equal(inverse_perm.pattern, expected_inverse_perm.pattern))\n \n+    def test_repeat(self):\n+        """Test the ``repeat`` method."""\n+        pattern = [2, 4, 1, 3, 0]\n+        perm = PermutationGate(pattern)\n+        self.assertTrue(np.allclose(Operator(perm.repeat(2)), Operator(perm) @ Operator(perm)))\n+\n \n class TestPermutationGatesOnCircuit(QiskitTestCase):\n     """Tests for quantum circuits containing permutations."""\ndiff --git a/test/python/circuit/test_diagonal_gate.py b/test/python/circuit/test_diagonal_gate.py\nindex de8d53e12d3b..bb9259999576 100644\n--- a/test/python/circuit/test_diagonal_gate.py\n+++ b/test/python/circuit/test_diagonal_gate.py\n@@ -83,6 +83,19 @@ def test_npcomplex_params_conversion(self):\n                 all(isinstance(p, complex) and not isinstance(p, np.number) for p in params)\n             )\n \n+    def test_repeat(self):\n+        """Test the repeat() method."""\n+        for phases in [\n+            [0, 0],\n+            np.array([0, 0.8, 1, 0]),\n+            (2 * np.pi * np.random.rand(2**3)).tolist(),\n+        ]:\n+            with self.subTest(phases=phases):\n+                diag = [np.exp(1j * ph) for ph in phases]\n+                gate = DiagonalGate(diag)\n+                operator = Operator(gate)\n+                self.assertTrue(np.allclose(Operator(gate.repeat(2)), operator @ operator))\n+\n \n def _get_diag_gate_matrix(diag):\n     return np.diagflat(diag)\ndiff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py\nindex 950b0c478ed8..914ac8bab985 100644\n--- a/test/python/circuit/test_gate_definitions.py\n+++ b/test/python/circuit/test_gate_definitions.py\n@@ -54,6 +54,8 @@\n     CZGate,\n     RYYGate,\n     PhaseGate,\n+    PauliGate,\n+    UCPauliRotGate,\n     CPhaseGate,\n     UGate,\n     CUGate,\n@@ -184,6 +186,18 @@ def test_xx_minus_yy_definition(self):\n         self.assertTrue(len(decomposed_circuit) > len(circuit))\n         self.assertTrue(Operator(circuit).equiv(Operator(decomposed_circuit), atol=1e-7))\n \n+    def test_pauligate_repeat(self):\n+        """Test `repeat` method for `PauliGate`."""\n+        gate = PauliGate("XYZ")\n+        operator = Operator(gate)\n+        self.assertTrue(np.allclose(Operator(gate.repeat(2)), operator @ operator))\n+\n+    def test_ucpaulirotgate_repeat(self):\n+        """Test `repeat` method for `UCPauliRotGate`."""\n+        gate = UCPauliRotGate([0.3, 0.5], "X")\n+        operator = Operator(gate)\n+        self.assertTrue(np.allclose(Operator(gate.repeat(2)), operator @ operator))\n+\n \n @ddt\n class TestStandardGates(QiskitTestCase):\ndiff --git a/test/python/circuit/test_hamiltonian_gate.py b/test/python/circuit/test_hamiltonian_gate.py\nindex 60fb1e9a90a9..e65e2f07ba5e 100644\n--- a/test/python/circuit/test_hamiltonian_gate.py\n+++ b/test/python/circuit/test_hamiltonian_gate.py\n@@ -62,6 +62,12 @@ def test_adjoint(self):\n             ham.adjoint().to_matrix(), np.transpose(np.conj(ham.to_matrix()))\n         )\n \n+    def test_repeat(self):\n+        """test repeat operation"""\n+        ham = HamiltonianGate(np.array([[1, 0.5 + 4j], [0.5 - 4j, -0.2]]), np.pi * 0.143)\n+        operator = Operator(ham)\n+        self.assertTrue(np.allclose(Operator(ham.repeat(2)), operator @ operator))\n+\n \n class TestHamiltonianCircuit(QiskitTestCase):\n     """Hamiltonian gate circuit tests."""\ndiff --git a/test/python/circuit/test_initializer.py b/test/python/circuit/test_initializer.py\nindex 2507fbc6c139..990df5755d59 100644\n--- a/test/python/circuit/test_initializer.py\n+++ b/test/python/circuit/test_initializer.py\n@@ -473,6 +473,16 @@ def test_gates_to_uncompute(self):\n         vec = Statevector(qc)\n         self.assertTrue(vec == Statevector(desired_vector))\n \n+    def test_repeat(self):\n+        """Test the repeat() method."""\n+        desired_vector = np.array([0.5, 0.5, 0.5, 0.5])\n+        initialize = Initialize(desired_vector)\n+        qr = QuantumRegister(2)\n+        qc = QuantumCircuit(qr)\n+        qc.append(initialize.repeat(2), qr)\n+        statevector = Statevector(qc)\n+        self.assertTrue(np.allclose(statevector, desired_vector))\n+\n \n class TestInstructionParam(QiskitTestCase):\n     """Test conversion of numpy type parameters."""\ndiff --git a/test/python/circuit/test_isometry.py b/test/python/circuit/test_isometry.py\nindex 35ff639cedd5..ff497c76f48a 100644\n--- a/test/python/circuit/test_isometry.py\n+++ b/test/python/circuit/test_isometry.py\n@@ -133,6 +133,21 @@ def test_isometry_inverse(self, iso):\n         result = Operator(qc)\n         np.testing.assert_array_almost_equal(result.data, np.identity(result.dim[0]))\n \n+    @data(\n+        np.eye(2, 2),\n+        random_unitary(2, seed=297102).data,\n+        np.eye(4, 4),\n+        random_unitary(4, seed=123642).data,\n+        random_unitary(8, seed=568288).data,\n+    )\n+    def test_isometry_repeat(self, iso):\n+        """Tests for the repeat of isometries from n to n qubits"""\n+        iso_gate = Isometry(iso, 0, 0)\n+\n+        op = Operator(iso_gate)\n+        op_double = Operator(iso_gate.repeat(2))\n+        np.testing.assert_array_almost_equal(op @ op, op_double)\n+\n \n if __name__ == "__main__":\n     unittest.main()\ndiff --git a/test/python/circuit/test_uc.py b/test/python/circuit/test_uc.py\nindex 87541d349c3f..0277c4afb43d 100644\n--- a/test/python/circuit/test_uc.py\n+++ b/test/python/circuit/test_uc.py\n@@ -100,6 +100,13 @@ def test_inverse_ucg(self):\n \n         self.assertTrue(np.allclose(unitary_desired, unitary))\n \n+    def test_repeat(self):\n+        """test repeat operation"""\n+        gates = [random_unitary(2, seed=seed).data for seed in [124435, 876345, 687462, 928365]]\n+\n+        uc = UCGate(gates, up_to_diagonal=False)\n+        self.assertTrue(np.allclose(Operator(uc.repeat(2)), Operator(uc) @ Operator(uc)))\n+\n \n def _get_ucg_matrix(squs):\n     return block_diag(*squs)\ndiff --git a/test/python/circuit/test_unitary.py b/test/python/circuit/test_unitary.py\nindex 63edbdaffbf9..6ab4a93c3f7d 100644\n--- a/test/python/circuit/test_unitary.py\n+++ b/test/python/circuit/test_unitary.py\n@@ -67,6 +67,11 @@ def test_adjoint(self):\n         uni = UnitaryGate([[0, 1j], [-1j, 0]])\n         self.assertTrue(numpy.array_equal(uni.adjoint().to_matrix(), uni.to_matrix()))\n \n+    def test_repeat(self):\n+        """test repeat operation"""\n+        uni = UnitaryGate([[1, 0], [0, 1j]])\n+        self.assertTrue(numpy.array_equal(Operator(uni.repeat(2)), Operator(uni) @ Operator(uni)))\n+\n \n class TestUnitaryCircuit(QiskitTestCase):\n     """Matrix gate circuit tests."""\n


Fix `StatePreparation`'s ``normalize`` argument (Qiskit/qiskit#12988)

<!--\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\n\r\nFixes #12984. The definition didn't use the normalized parameters, if the input is synthesized by isometry.\r\n\r\n\r\n\r\n

Passmanager fails to transpile a basic StatePreparation gate

### Environment\n\n- **Qiskit version**:  1.2.0\r\n- **Python version**: 3.12\r\n- **Operating system**: Ubuntu 24.04\r\n\n\n### What is happening?\n\nI am trying to execute the following circuit:\r\n\r\n```python\r\nfrom qiskit import QuantumCircuit, __version__ as qversion\r\nfrom qiskit.circuit.library import StatePreparation\r\nfrom qiskit_aer import AerSimulator, __version__ as qaversion\r\nfrom qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\r\n\r\nprint(qversion, '/', qaversion) # 1.2.0 / 0.14.2\r\n\r\npm = generate_preset_pass_manager(backend=AerSimulator(), optimization_level=3)\r\n\r\nqc =  QuantumCircuit(1)\r\nqc.compose(StatePreparation([1, 1], normalize=True), range(1), inplace=True)\r\n\r\npm.run(qc)\r\n```\r\n\r\nAnd I am getting the following error:\r\n\r\n```\r\n---------------------------------------------------------------------------\r\nQiskitError                               Traceback (most recent call last)\r\nCell In[21], line 13\r\n     10 qc =  QuantumCircuit(1)\r\n     11 qc.compose(StatePreparation([1, 1], normalize=True), range(1), inplace=True)\r\n---> 13 pm.run(qc)\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py:441, in StagedPassManager.run(self, circuits, output_name, callback, num_processes)\r\n    433 def run(\r\n    434     self,\r\n    435     circuits: _CircuitsT,\r\n   (...)\r\n    438     num_processes: int = None,\r\n    439 ) -> _CircuitsT:\r\n    440     self._update_passmanager()\r\n--> 441     return super().run(circuits, output_name, callback, num_processes=num_processes)\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py:464, in _replace_error.<locals>.wrapper(*meth_args, **meth_kwargs)\r\n    461 @wraps(meth)\r\n    462 def wrapper(*meth_args, **meth_kwargs):\r\n    463     try:\r\n--> 464         return meth(*meth_args, **meth_kwargs)\r\n    465     except PassManagerError as ex:\r\n    466         raise TranspilerError(ex.message) from ex\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passmanager.py:226, in PassManager.run(self, circuits, output_name, callback, num_processes)\r\n    223 if callback is not None:\r\n    224     callback = _legacy_style_callback(callback)\r\n--> 226 return super().run(\r\n    227     in_programs=circuits,\r\n    228     callback=callback,\r\n    229     output_name=output_name,\r\n    230     num_processes=num_processes,\r\n    231 )\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/passmanager/passmanager.py:232, in BasePassManager.run(self, in_programs, callback, num_processes, **kwargs)\r\n    228 # If we're not going to run in parallel, we want to avoid spending time `dill` serializing\r\n    229 # ourselves, since that can be quite expensive.\r\n    230 if len(in_programs) == 1 or not should_run_in_parallel(num_processes):\r\n    231     out = [\r\n--> 232         _run_workflow(program=program, pass_manager=self, callback=callback, **kwargs)\r\n    233         for program in in_programs\r\n    234     ]\r\n    235     if len(in_programs) == 1 and not is_list:\r\n    236         return out[0]\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/passmanager/passmanager.py:292, in _run_workflow(program, pass_manager, **kwargs)\r\n    286 initial_status = WorkflowStatus()\r\n    288 passmanager_ir = pass_manager._passmanager_frontend(\r\n    289     input_program=program,\r\n    290     **kwargs,\r\n    291 )\r\n--> 292 passmanager_ir, final_state = flow_controller.execute(\r\n    293     passmanager_ir=passmanager_ir,\r\n    294     state=PassManagerState(\r\n    295         workflow_status=initial_status,\r\n    296         property_set=PropertySet(),\r\n    297     ),\r\n    298     callback=kwargs.get("callback", None),\r\n    299 )\r\n    300 # The `property_set` has historically been returned as a mutable attribute on `PassManager`\r\n    301 # This makes us non-reentrant (though `PassManager` would be dependent on its internal tasks to\r\n    302 # be re-entrant if that was required), but is consistent with previous interfaces.  We're still\r\n    303 # safe to be called in a serial loop, again assuming internal tasks are re-runnable.  The\r\n    304 # conversion to the backend language is also allowed to use the property set, so it must be set\r\n    305 # before calling it.\r\n    306 pass_manager.property_set = final_state.property_set\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py:218, in BaseController.execute(self, passmanager_ir, state, callback)\r\n    216     return passmanager_ir, state\r\n    217 while True:\r\n--> 218     passmanager_ir, state = next_task.execute(\r\n    219         passmanager_ir=passmanager_ir,\r\n    220         state=state,\r\n    221         callback=callback,\r\n    222     )\r\n    223     try:\r\n    224         # Sending the object through the generator implies the custom controllers\r\n    225         # can always rely on the latest data to choose the next task to run.\r\n    226         next_task = task_generator.send(state)\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/basepasses.py:195, in TransformationPass.execute(self, passmanager_ir, state, callback)\r\n    189 def execute(\r\n    190     self,\r\n    191     passmanager_ir: PassManagerIR,\r\n    192     state: PassManagerState,\r\n    193     callback: Callable = None,\r\n    194 ) -> tuple[PassManagerIR, PassManagerState]:\r\n--> 195     new_dag, state = super().execute(\r\n    196         passmanager_ir=passmanager_ir,\r\n    197         state=state,\r\n    198         callback=callback,\r\n    199     )\r\n    201     if state.workflow_status.previous_run == RunState.SUCCESS:\r\n    202         if isinstance(new_dag, DAGCircuit):\r\n    203             # Copy calibration data from the original program\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/passmanager/base_tasks.py:98, in GenericPass.execute(self, passmanager_ir, state, callback)\r\n     96 try:\r\n     97     if self not in state.workflow_status.completed_passes:\r\n---> 98         ret = self.run(passmanager_ir)\r\n     99         run_state = RunState.SUCCESS\r\n    100     else:\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passes/synthesis/high_level_synthesis.py:443, in HighLevelSynthesis.run(self, dag)\r\n    440 if self._definitely_skip_node(node, qubits):\r\n    441     continue\r\n--> 443 decomposition, modified = self._recursively_handle_op(node.op, qubits)\r\n    445 if not modified:\r\n    446     continue\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/transpiler/passes/synthesis/high_level_synthesis.py:551, in HighLevelSynthesis._recursively_handle_op(self, op, qubits)\r\n    547         return op, False\r\n    549 try:\r\n    550     # extract definition\r\n--> 551     definition = op.definition\r\n    552 except TypeError as err:\r\n    553     raise TranspilerError(\r\n    554         f"HighLevelSynthesis was unable to extract definition for {op.name}: {err}"\r\n    555     ) from err\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/circuit/instruction.py:312, in Instruction.definition(self)\r\n    310 """Return definition in terms of other basic gates."""\r\n    311 if self._definition is None:\r\n--> 312     self._define()\r\n    313 return self._definition\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/circuit/library/data_preparation/state_preparation.py:122, in StatePreparation._define(self)\r\n    120     self.definition = self._define_from_int()\r\n    121 else:\r\n--> 122     self.definition = self._define_synthesis_isom()\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/circuit/library/data_preparation/state_preparation.py:176, in StatePreparation._define_synthesis_isom(self)\r\n    173 q = QuantumRegister(self.num_qubits, "q")\r\n    174 initialize_circuit = QuantumCircuit(q, name="init_def")\r\n--> 176 isom = Isometry(self._params_arg, 0, 0)\r\n    177 initialize_circuit.append(isom, q[:])\r\n    179 # invert the circuit to create the desired vector from zero (assuming\r\n    180 # the qubits are in the zero state)\r\n\r\nFile ~/anaconda3/envs/building-agi/lib/python3.12/site-packages/qiskit/circuit/library/generalized_gates/isometry.py:112, in Isometry.__init__(self, isometry, num_ancillas_zero, num_ancillas_dirty, epsilon)\r\n    108     raise QiskitError(\r\n    109         "The input matrix has more columns than rows and hence it can't be an isometry."\r\n    110     )\r\n    111 if not is_isometry(isometry, self._epsilon):\r\n--> 112     raise QiskitError(\r\n    113         "The input matrix has non orthonormal columns and hence it is not an isometry."\r\n    114     )\r\n    116 num_qubits = int(n) + num_ancillas_zero + num_ancillas_dirty\r\n    118 super().__init__("isometry", num_qubits, 0, [isometry])\r\n\r\nQiskitError: 'The input matrix has non orthonormal columns and hence it is not an isometry.'\r\n```\r\n\r\n\r\nAs far as I remember, similar code used to work on previous versions of Qiskit.\n\n### How can we reproduce the issue?\n\nHere is a minimal code snippet to reproduce the error:\r\n\r\n```python\r\nfrom qiskit import QuantumCircuit, __version__ as qversion\r\nfrom qiskit.circuit.library import StatePreparation\r\nfrom qiskit_aer import AerSimulator, __version__ as qaversion\r\nfrom qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\r\n\r\nprint(qversion, '/', qaversion) # 1.2.0 / 0.14.2\r\n\r\npm = generate_preset_pass_manager(backend=AerSimulator(), optimization_level=3)\r\n\r\nqc =  QuantumCircuit(1)\r\nqc.compose(StatePreparation([1, 1], normalize=True), range(1), inplace=True)\r\n\r\npm.run(qc)\r\n```\n\n### What should happen?\n\nThe transpilation of the `StatePreparation` gate :) \n\n### Any suggestions?\n\n_No response_

diff --git a/qiskit/circuit/library/data_preparation/state_preparation.py b/qiskit/circuit/library/data_preparation/state_preparation.py\nindex 26c37334cfe4..df69f9eab8c5 100644\n--- a/qiskit/circuit/library/data_preparation/state_preparation.py\n+++ b/qiskit/circuit/library/data_preparation/state_preparation.py\n@@ -173,7 +173,7 @@ def _define_synthesis_isom(self):\n         q = QuantumRegister(self.num_qubits, "q")\n         initialize_circuit = QuantumCircuit(q, name="init_def")\n \n-        isom = Isometry(self._params_arg, 0, 0)\n+        isom = Isometry(self.params, 0, 0)\n         initialize_circuit.append(isom, q[:])\n \n         # invert the circuit to create the desired vector from zero (assuming\ndiff --git a/releasenotes/notes/fix-stateprep-normalize-a8057c339ba619bd.yaml b/releasenotes/notes/fix-stateprep-normalize-a8057c339ba619bd.yaml\nnew file mode 100644\nindex 000000000000..0175f8c1c026\n--- /dev/null\n+++ b/releasenotes/notes/fix-stateprep-normalize-a8057c339ba619bd.yaml\n@@ -0,0 +1,6 @@\n+---\n+fixes:\n+  - |\n+    Fixed a bug in :class:`.StatePreparation` where the ``normalize`` \n+    argument was ignored for input arrays.\n+    Fixed `#12984 <https://github.com/Qiskit/qiskit/issues/12984>`__.\n

diff --git a/test/python/circuit/library/test_state_preparation.py b/test/python/circuit/library/test_state_preparation.py\nindex adad848a2777..b5446ed37e9c 100644\n--- a/test/python/circuit/library/test_state_preparation.py\n+++ b/test/python/circuit/library/test_state_preparation.py\n@@ -113,6 +113,16 @@ def test_repeats(self):\n         qc.append(StatePreparation("01").repeat(2), [0, 1])\n         self.assertEqual(qc.decompose().count_ops()["state_preparation"], 2)\n \n+    def test_normalize(self):\n+        """Test the normalization.\n+\n+        Regression test of #12984.\n+        """\n+        qc = QuantumCircuit(1)\n+        qc.compose(StatePreparation([1, 1], normalize=True), range(1), inplace=True)\n+\n+        self.assertTrue(Statevector(qc).equiv(np.array([1, 1]) / np.sqrt(2)))\n+\n \n if __name__ == "__main__":\n     unittest.main()\n


Fix the matrix representation of `CUGate` in Rust (Qiskit/qiskit#13121)

<!--\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\n\r\nFixed #13118.\r\n\r\n### Details and comments\r\n\r\nThe matrix representation of `CUGate` in Rust accidentally swapped two arguments (`phi` and `lam`). This didn't fail the tests, because there all parameters are set to the same value. The tests are therefore also updated to vary the parameters values within a gate.\r\n

Transpiler changes circuit semantics

### Environment\n\n- **Qiskit version**: 1.3.0\r\n- **Python version**: 3.10.12\r\n- **Operating system**: Ubuntu 22.04 LTS on Windows 11 WSL\r\n\n\n### What is happening?\n\n[Ilan](https://github.com/IlanIwumbwe) and I have found a possible miscompilation caused by the transpiler.\r\n\r\nThe resulting state-vector of the circuit after putting it through `transpile()` changes abruptly after optimization level has been set to above 2, thereby also changing the states of the circuit upon measurement.\n\n### How can we reproduce the issue?\n\n```py\r\nfrom qiskit import QuantumCircuit\r\nfrom qiskit.transpiler import generate_preset_pass_manager\r\nfrom qiskit.quantum_info import Statevector\r\nfrom numpy import vdot\r\n\r\ndef simulate_circuit(qc : QuantumCircuit):\r\n    state = Statevector.from_int(0, 2**qc.num_qubits)\r\n    state = state.evolve(qc)\r\n    return state\r\n\r\ndef compare_statevectors(qc : QuantumCircuit):\r\n    # Need to set the seed for consistent transpiler pass results across optimisation levels\r\n    options = {'seed_transpiler': 1235}\r\n    sv0 = simulate_circuit(qc)\r\n    # Code for checking different levels of optimization levels and if they are the same\r\n    for i in range(4):\r\n        pass_manager = generate_preset_pass_manager(optimization_level=i, **options)\r\n        transpiled_circuit = pass_manager.run(qc)\r\n        sv = simulate_circuit(transpiled_circuit)\r\n        # Uncomment to see statevector\r\n        # print(sv) \r\n        if(sv0 == sv):\r\n            print("Level ", i, " passed")\r\n        else:\r\n            print("Failed level ", i, ". Dot product is: " , abs(vdot(sv0, sv)), "\\n")\r\n\r\nsubcirc2 = QuantumCircuit(2)\r\nsubcirc2.z(0)\r\nsubcirc2.h(0)\r\nsubcirc2.z(0)\r\nsubcirc2.s(1)\r\nsubcirc2 = subcirc2.to_gate().control(1)\r\n\r\nmain_circ = QuantumCircuit(4)\r\nmain_circ.h(0)\r\nmain_circ.append(subcirc2,[0, 2, 3])\r\nmain_circ.append(subcirc2,[0, 2, 3])\r\nmain_circ.append(subcirc2,[0, 2, 3])\r\nmain_circ.h(2)\r\n\r\ncompare_statevectors(main_circ)\r\n```\r\n\r\nRunning the above code will reveal that the state-vectors are different when the optimization level is above 2. The results will also be the same if the circuit was instead run on `AerBackend`, with optimization levels 2 and 3 producing very different measured states than levels 0 and 1.\r\n\r\n\r\nThis is done on the latest version of qiskit (1.3.0), which we have compiled from source to get the latest fixes. However, it seems like this issue is also present in version 1.2.0. \n\n### What should happen?\n\nThe circuit should not have a different state-vector after putting it through the transpiler.\n\n### Any suggestions?\n\n_No response_

diff --git a/crates/circuit/src/gate_matrix.rs b/crates/circuit/src/gate_matrix.rs\nindex a38783ef7e7f..6b04b8512fb0 100644\n--- a/crates/circuit/src/gate_matrix.rs\n+++ b/crates/circuit/src/gate_matrix.rs\n@@ -387,12 +387,12 @@ pub fn cu_gate(theta: f64, phi: f64, lam: f64, gamma: f64) -> GateArray2Q {\n             C_ZERO,\n             c64(0., gamma).exp() * cos_theta,\n             C_ZERO,\n-            c64(0., gamma + phi).exp() * (-1.) * sin_theta,\n+            c64(0., gamma + lam).exp() * (-1.) * sin_theta,\n         ],\n         [C_ZERO, C_ZERO, C_ONE, C_ZERO],\n         [\n             C_ZERO,\n-            c64(0., gamma + lam).exp() * sin_theta,\n+            c64(0., gamma + phi).exp() * sin_theta,\n             C_ZERO,\n             c64(0., gamma + phi + lam).exp() * cos_theta,\n         ],\ndiff --git a/releasenotes/notes/fix-cu-rust-6464b6893ecca1b3.yaml b/releasenotes/notes/fix-cu-rust-6464b6893ecca1b3.yaml\nnew file mode 100644\nindex 000000000000..406d6052a1df\n--- /dev/null\n+++ b/releasenotes/notes/fix-cu-rust-6464b6893ecca1b3.yaml\n@@ -0,0 +1,9 @@\n+---\n+fixes:\n+  - |\n+    Fixed the definition of the :class:`.CUGate` matrix in Rust-space. \n+    While this was not noticable while handling the :class:`.CUGate` purely on \n+    Python side, this had knock-on effects when transpiler passes were using the \n+    Rust representation, such as could happen in :class:`.Consolidate2qBlocks`.\n+    Fixed `#13118 <https://github.com/Qiskit/qiskit/issues/13118>`__.\n+\n

diff --git a/test/python/circuit/test_rust_equivalence.py b/test/python/circuit/test_rust_equivalence.py\nindex b6ec28f82e8d..db4f1441bea3 100644\n--- a/test/python/circuit/test_rust_equivalence.py\n+++ b/test/python/circuit/test_rust_equivalence.py\n@@ -66,7 +66,7 @@ def test_definitions(self):\n                 continue\n \n             with self.subTest(name=name):\n-                params = [pi] * standard_gate._num_params()\n+                params = [0.1 * (i + 1) for i in range(standard_gate._num_params())]\n                 py_def = gate_class.base_class(*params).definition\n                 rs_def = standard_gate._get_definition(params)\n                 if py_def is None:\n@@ -141,7 +141,7 @@ def test_matrix(self):\n                 continue\n \n             with self.subTest(name=name):\n-                params = [0.1] * standard_gate._num_params()\n+                params = [0.1 * (i + 1) for i in range(standard_gate._num_params())]\n                 py_def = gate_class.base_class(*params).to_matrix()\n                 rs_def = standard_gate._to_matrix(params)\n                 np.testing.assert_allclose(rs_def, py_def)\n