Repository: Qiskit/qiskit — pull request #10904 (python)
Title: Fix Clifford.from_operator to fail with non-Clifford diagonal operators

<!--\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

Resolved issues:
title: Clifford.from_operator does not fail with non-Clifford diagonal operator
body: ### 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_

Hints:

Fix patch:
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

Test patch:
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