We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Qasm output decomposes single qubit MatrixGate into multiple operations instead of one u3 operation
```
import cirq

c = cirq.Circuit(
    cirq.MatrixGate(cirq.unitary(cirq.H) @ cirq.unitary(cirq.T)).on(cirq.LineQubit(0)),
)
print(c.to_qasm())
```

Outputs:

```
OPENQASM 2.0;
include "qelib1.inc";


// Qubits: [q(0)]
qreg q[1];


// Gate: [[ 0.707+0.j   0.5  +0.5j]
 // [ 0.707+0.j  -0.5  -0.5j]]
rz(pi*0.75) q[0];
sx q[0];
s q[0];
```

But should output:

```
// Generated from Cirq v0.15.0

OPENQASM 2.0;
include "qelib1.inc";


// Qubits: [q(0)]
qreg q[1];


u3(pi*1.5,pi*1.0,pi*0.25) q[0];
```

Which I got by using `cirq.PhasedXZGate.from_matrix(...)` and turning that into qasm.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cirq-core/cirq/ops/matrix_gates.py`
Content:
```
1 # Copyright 2018 The Cirq Developers
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     https://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """Quantum gates defined by a matrix."""
16 
17 from typing import Any, cast, Dict, Iterable, Optional, Tuple, TYPE_CHECKING
18 
19 import numpy as np
20 
21 from cirq import linalg, protocols, _import
22 from cirq._compat import proper_repr
23 from cirq.ops import raw_types
24 
25 if TYPE_CHECKING:
26     import cirq
27 
28 single_qubit_decompositions = _import.LazyLoader(
29     'single_qubit_decompositions', globals(), 'cirq.transformers.analytical_decompositions'
30 )
31 two_qubit_to_cz = _import.LazyLoader(
32     'two_qubit_to_cz', globals(), 'cirq.transformers.analytical_decompositions'
33 )
34 three_qubit_decomposition = _import.LazyLoader(
35     'three_qubit_decomposition', globals(), 'cirq.transformers.analytical_decompositions'
36 )
37 
38 
39 class MatrixGate(raw_types.Gate):
40     r"""A unitary qubit or qudit gate defined entirely by its numpy matrix.
41 
42     For example `cirq.MatrixGate(np.array([[0, 1j], [1, 0]]))` has the unitary matrix:
43 
44     $$
45     \begin{bmatrix}
46         0 & i \\
47         1 & 0
48     \end{bmatrix}
49     $$
50     """
51 
52     def __init__(
53         self,
54         matrix: np.ndarray,
55         *,
56         name: str = None,
57         qid_shape: Optional[Iterable[int]] = None,
58         unitary_check_rtol: float = 1e-5,
59         unitary_check_atol: float = 1e-8,
60     ) -> None:
61         """Initializes a matrix gate.
62 
63         Args:
64             matrix: The matrix that defines the gate.
65             name: The optional name of the gate to be displayed.
66             qid_shape: The shape of state tensor that the matrix applies to.
67                 If not specified, this value is inferred by assuming that the
68                 matrix is supposed to apply to qubits.
69             unitary_check_rtol: The relative tolerance for checking whether the supplied matrix
70                 is unitary. See `cirq.is_unitary`.
71             unitary_check_atol: The absolute tolerance for checking whether the supplied matrix
72                 is unitary. See `cirq.is_unitary`.
73 
74         Raises:
75             ValueError: If the matrix is not a square numpy array, if the matrix does not match
76                 the `qid_shape`, if `qid_shape` is not supplied and the matrix dimension is
77                 not a power of 2, or if the matrix not unitary (to the supplied precisions).
78         """
79         if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]:
80             raise ValueError('`matrix` must be a square 2d numpy array.')
81 
82         if qid_shape is None:
83             n = int(np.round(np.log2(matrix.shape[0] or 1)))
84             if 2**n != matrix.shape[0]:
85                 raise ValueError(
86                     f'Matrix width ({matrix.shape[0]}) is not a power of 2 and '
87                     f'qid_shape is not specified.'
88                 )
89             qid_shape = (2,) * n
90 
91         self._matrix = matrix
92         self._qid_shape = tuple(qid_shape)
93         self._name = name
94         m = int(np.prod(self._qid_shape, dtype=np.int64))
95         if self._matrix.shape != (m, m):
96             raise ValueError(
97                 'Wrong matrix shape for qid_shape.\n'
98                 f'Matrix shape: {self._matrix.shape}\n'
99                 f'qid_shape: {self._qid_shape}\n'
100             )
101 
102         if not linalg.is_unitary(matrix, rtol=unitary_check_rtol, atol=unitary_check_atol):
103             raise ValueError(f'Not a unitary matrix: {self._matrix}')
104 
105     def _json_dict_(self) -> Dict[str, Any]:
106         return {'matrix': self._matrix.tolist(), 'qid_shape': self._qid_shape}
107 
108     @classmethod
109     def _from_json_dict_(cls, matrix, qid_shape, **kwargs):
110         return cls(matrix=np.array(matrix), qid_shape=qid_shape)
111 
112     def _qid_shape_(self) -> Tuple[int, ...]:
113         return self._qid_shape
114 
115     def __pow__(self, exponent: Any) -> 'MatrixGate':
116         if not isinstance(exponent, (int, float)):
117             return NotImplemented
118         e = cast(float, exponent)
119         new_mat = linalg.map_eigenvalues(self._matrix, lambda b: b**e)
120         return MatrixGate(new_mat, qid_shape=self._qid_shape)
121 
122     def _phase_by_(self, phase_turns: float, qubit_index: int) -> 'MatrixGate':
123         if not isinstance(phase_turns, (int, float)):
124             return NotImplemented
125         if self._qid_shape[qubit_index] != 2:
126             return NotImplemented
127         result = np.copy(self._matrix).reshape(self._qid_shape * 2)
128 
129         p = np.exp(2j * np.pi * phase_turns)
130         i = qubit_index
131         j = qubit_index + len(self._qid_shape)
132         result[linalg.slice_for_qubits_equal_to([i], 1)] *= p
133         result[linalg.slice_for_qubits_equal_to([j], 1)] *= np.conj(p)
134         return MatrixGate(matrix=result.reshape(self._matrix.shape), qid_shape=self._qid_shape)
135 
136     def _decompose_(self, qubits: Tuple['cirq.Qid', ...]) -> 'cirq.OP_TREE':
137         if self._qid_shape == (2,):
138             return [
139                 g.on(qubits[0])
140                 for g in single_qubit_decompositions.single_qubit_matrix_to_gates(self._matrix)
141             ]
142         if self._qid_shape == (2,) * 2:
143             return two_qubit_to_cz.two_qubit_matrix_to_cz_operations(
144                 *qubits, self._matrix, allow_partial_czs=True
145             )
146         if self._qid_shape == (2,) * 3:
147             return three_qubit_decomposition.three_qubit_matrix_to_operations(*qubits, self._matrix)
148         return NotImplemented
149 
150     def _has_unitary_(self) -> bool:
151         return True
152 
153     def _unitary_(self) -> np.ndarray:
154         return np.copy(self._matrix)
155 
156     def _circuit_diagram_info_(
157         self, args: 'cirq.CircuitDiagramInfoArgs'
158     ) -> 'cirq.CircuitDiagramInfo':
159         n_qubits = len(self._qid_shape)
160         if self._name is not None:
161             symbols = (
162                 [self._name] if n_qubits == 1 else [f'{self._name}[{i+1}]' for i in range(n_qubits)]
163             )
164             return protocols.CircuitDiagramInfo(wire_symbols=symbols)
165         main = _matrix_to_diagram_symbol(self._matrix, args)
166         rest = [f'#{i+1}' for i in range(1, n_qubits)]
167         return protocols.CircuitDiagramInfo(wire_symbols=[main, *rest])
168 
169     def __hash__(self) -> int:
170         vals = tuple(v for _, v in np.ndenumerate(self._matrix))
171         return hash((MatrixGate, vals))
172 
173     def _approx_eq_(self, other: Any, atol) -> bool:
174         if not isinstance(other, type(self)):
175             return NotImplemented
176         return np.allclose(self._matrix, other._matrix, rtol=0, atol=atol)
177 
178     def __eq__(self, other):
179         if not isinstance(other, type(self)):
180             return NotImplemented
181         return self._qid_shape == other._qid_shape and np.array_equal(self._matrix, other._matrix)
182 
183     def __ne__(self, other):
184         return not self == other
185 
186     def __repr__(self) -> str:
187         if all(e == 2 for e in self._qid_shape):
188             return f'cirq.MatrixGate({proper_repr(self._matrix)})'
189         return f'cirq.MatrixGate({proper_repr(self._matrix)}, qid_shape={self._qid_shape})'
190 
191     def __str__(self) -> str:
192         return str(self._matrix.round(3))
193 
194 
195 def _matrix_to_diagram_symbol(matrix: np.ndarray, args: 'protocols.CircuitDiagramInfoArgs') -> str:
196     if args.precision is not None:
197         matrix = matrix.round(args.precision)
198     result = str(matrix)
199     if args.use_unicode_characters:
200         lines = result.split('\n')
201         for i in range(len(lines)):
202             lines[i] = lines[i].replace('[[', '')
203             lines[i] = lines[i].replace(' [', '')
204             lines[i] = lines[i].replace(']', '')
205         w = max(len(line) for line in lines)
206         for i in range(len(lines)):
207             lines[i] = '│' + lines[i].ljust(w) + '│'
208         lines.insert(0, '┌' + ' ' * w + '┐')
209         lines.append('└' + ' ' * w + '┘')
210         result = '\n'.join(lines)
211     return result
212
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/cirq-core/cirq/ops/matrix_gates.py b/cirq-core/cirq/ops/matrix_gates.py
--- a/cirq-core/cirq/ops/matrix_gates.py
+++ b/cirq-core/cirq/ops/matrix_gates.py
@@ -20,7 +20,7 @@
 
 from cirq import linalg, protocols, _import
 from cirq._compat import proper_repr
-from cirq.ops import raw_types
+from cirq.ops import raw_types, phased_x_z_gate
 
 if TYPE_CHECKING:
     import cirq
@@ -166,6 +166,14 @@
         rest = [f'#{i+1}' for i in range(1, n_qubits)]
         return protocols.CircuitDiagramInfo(wire_symbols=[main, *rest])
 
+    def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]:
+        args.validate_version('2.0')
+        if self._qid_shape == (2,):
+            return protocols.qasm(
+                phased_x_z_gate.PhasedXZGate.from_matrix(self._matrix), args=args, qubits=qubits
+            )
+        return None
+
     def __hash__(self) -> int:
         vals = tuple(v for _, v in np.ndenumerate(self._matrix))
         return hash((MatrixGate, vals))

{"golden_diff": "diff --git a/cirq-core/cirq/ops/matrix_gates.py b/cirq-core/cirq/ops/matrix_gates.py\n--- a/cirq-core/cirq/ops/matrix_gates.py\n+++ b/cirq-core/cirq/ops/matrix_gates.py\n@@ -20,7 +20,7 @@\n \n from cirq import linalg, protocols, _import\n from cirq._compat import proper_repr\n-from cirq.ops import raw_types\n+from cirq.ops import raw_types, phased_x_z_gate\n \n if TYPE_CHECKING:\n     import cirq\n@@ -166,6 +166,14 @@\n         rest = [f'#{i+1}' for i in range(1, n_qubits)]\n         return protocols.CircuitDiagramInfo(wire_symbols=[main, *rest])\n \n+    def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]:\n+        args.validate_version('2.0')\n+        if self._qid_shape == (2,):\n+            return protocols.qasm(\n+                phased_x_z_gate.PhasedXZGate.from_matrix(self._matrix), args=args, qubits=qubits\n+            )\n+        return None\n+\n     def __hash__(self) -> int:\n         vals = tuple(v for _, v in np.ndenumerate(self._matrix))\n         return hash((MatrixGate, vals))\n", "issue": "Qasm output decomposes single qubit MatrixGate into multiple operations instead of one u3 operation\n```\r\nimport cirq\r\n\r\nc = cirq.Circuit(\r\n    cirq.MatrixGate(cirq.unitary(cirq.H) @ cirq.unitary(cirq.T)).on(cirq.LineQubit(0)),\r\n)\r\nprint(c.to_qasm())\r\n```\r\n\r\nOutputs:\r\n\r\n```\r\nOPENQASM 2.0;\r\ninclude \"qelib1.inc\";\r\n\r\n\r\n// Qubits: [q(0)]\r\nqreg q[1];\r\n\r\n\r\n// Gate: [[ 0.707+0.j   0.5  +0.5j]\r\n // [ 0.707+0.j  -0.5  -0.5j]]\r\nrz(pi*0.75) q[0];\r\nsx q[0];\r\ns q[0];\r\n```\r\n\r\nBut should output:\r\n\r\n```\r\n// Generated from Cirq v0.15.0\r\n\r\nOPENQASM 2.0;\r\ninclude \"qelib1.inc\";\r\n\r\n\r\n// Qubits: [q(0)]\r\nqreg q[1];\r\n\r\n\r\nu3(pi*1.5,pi*1.0,pi*0.25) q[0];\r\n```\r\n\r\nWhich I got by using `cirq.PhasedXZGate.from_matrix(...)` and turning that into qasm.\n", "before_files": [{"content": "# Copyright 2018 The Cirq Developers\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Quantum gates defined by a matrix.\"\"\"\n\nfrom typing import Any, cast, Dict, Iterable, Optional, Tuple, TYPE_CHECKING\n\nimport numpy as np\n\nfrom cirq import linalg, protocols, _import\nfrom cirq._compat import proper_repr\nfrom cirq.ops import raw_types\n\nif TYPE_CHECKING:\n    import cirq\n\nsingle_qubit_decompositions = _import.LazyLoader(\n    'single_qubit_decompositions', globals(), 'cirq.transformers.analytical_decompositions'\n)\ntwo_qubit_to_cz = _import.LazyLoader(\n    'two_qubit_to_cz', globals(), 'cirq.transformers.analytical_decompositions'\n)\nthree_qubit_decomposition = _import.LazyLoader(\n    'three_qubit_decomposition', globals(), 'cirq.transformers.analytical_decompositions'\n)\n\n\nclass MatrixGate(raw_types.Gate):\n    r\"\"\"A unitary qubit or qudit gate defined entirely by its numpy matrix.\n\n    For example `cirq.MatrixGate(np.array([[0, 1j], [1, 0]]))` has the unitary matrix:\n\n    $$\n    \\begin{bmatrix}\n        0 & i \\\\\n        1 & 0\n    \\end{bmatrix}\n    $$\n    \"\"\"\n\n    def __init__(\n        self,\n        matrix: np.ndarray,\n        *,\n        name: str = None,\n        qid_shape: Optional[Iterable[int]] = None,\n        unitary_check_rtol: float = 1e-5,\n        unitary_check_atol: float = 1e-8,\n    ) -> None:\n        \"\"\"Initializes a matrix gate.\n\n        Args:\n            matrix: The matrix that defines the gate.\n            name: The optional name of the gate to be displayed.\n            qid_shape: The shape of state tensor that the matrix applies to.\n                If not specified, this value is inferred by assuming that the\n                matrix is supposed to apply to qubits.\n            unitary_check_rtol: The relative tolerance for checking whether the supplied matrix\n                is unitary. See `cirq.is_unitary`.\n            unitary_check_atol: The absolute tolerance for checking whether the supplied matrix\n                is unitary. See `cirq.is_unitary`.\n\n        Raises:\n            ValueError: If the matrix is not a square numpy array, if the matrix does not match\n                the `qid_shape`, if `qid_shape` is not supplied and the matrix dimension is\n                not a power of 2, or if the matrix not unitary (to the supplied precisions).\n        \"\"\"\n        if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]:\n            raise ValueError('`matrix` must be a square 2d numpy array.')\n\n        if qid_shape is None:\n            n = int(np.round(np.log2(matrix.shape[0] or 1)))\n            if 2**n != matrix.shape[0]:\n                raise ValueError(\n                    f'Matrix width ({matrix.shape[0]}) is not a power of 2 and '\n                    f'qid_shape is not specified.'\n                )\n            qid_shape = (2,) * n\n\n        self._matrix = matrix\n        self._qid_shape = tuple(qid_shape)\n        self._name = name\n        m = int(np.prod(self._qid_shape, dtype=np.int64))\n        if self._matrix.shape != (m, m):\n            raise ValueError(\n                'Wrong matrix shape for qid_shape.\\n'\n                f'Matrix shape: {self._matrix.shape}\\n'\n                f'qid_shape: {self._qid_shape}\\n'\n            )\n\n        if not linalg.is_unitary(matrix, rtol=unitary_check_rtol, atol=unitary_check_atol):\n            raise ValueError(f'Not a unitary matrix: {self._matrix}')\n\n    def _json_dict_(self) -> Dict[str, Any]:\n        return {'matrix': self._matrix.tolist(), 'qid_shape': self._qid_shape}\n\n    @classmethod\n    def _from_json_dict_(cls, matrix, qid_shape, **kwargs):\n        return cls(matrix=np.array(matrix), qid_shape=qid_shape)\n\n    def _qid_shape_(self) -> Tuple[int, ...]:\n        return self._qid_shape\n\n    def __pow__(self, exponent: Any) -> 'MatrixGate':\n        if not isinstance(exponent, (int, float)):\n            return NotImplemented\n        e = cast(float, exponent)\n        new_mat = linalg.map_eigenvalues(self._matrix, lambda b: b**e)\n        return MatrixGate(new_mat, qid_shape=self._qid_shape)\n\n    def _phase_by_(self, phase_turns: float, qubit_index: int) -> 'MatrixGate':\n        if not isinstance(phase_turns, (int, float)):\n            return NotImplemented\n        if self._qid_shape[qubit_index] != 2:\n            return NotImplemented\n        result = np.copy(self._matrix).reshape(self._qid_shape * 2)\n\n        p = np.exp(2j * np.pi * phase_turns)\n        i = qubit_index\n        j = qubit_index + len(self._qid_shape)\n        result[linalg.slice_for_qubits_equal_to([i], 1)] *= p\n        result[linalg.slice_for_qubits_equal_to([j], 1)] *= np.conj(p)\n        return MatrixGate(matrix=result.reshape(self._matrix.shape), qid_shape=self._qid_shape)\n\n    def _decompose_(self, qubits: Tuple['cirq.Qid', ...]) -> 'cirq.OP_TREE':\n        if self._qid_shape == (2,):\n            return [\n                g.on(qubits[0])\n                for g in single_qubit_decompositions.single_qubit_matrix_to_gates(self._matrix)\n            ]\n        if self._qid_shape == (2,) * 2:\n            return two_qubit_to_cz.two_qubit_matrix_to_cz_operations(\n                *qubits, self._matrix, allow_partial_czs=True\n            )\n        if self._qid_shape == (2,) * 3:\n            return three_qubit_decomposition.three_qubit_matrix_to_operations(*qubits, self._matrix)\n        return NotImplemented\n\n    def _has_unitary_(self) -> bool:\n        return True\n\n    def _unitary_(self) -> np.ndarray:\n        return np.copy(self._matrix)\n\n    def _circuit_diagram_info_(\n        self, args: 'cirq.CircuitDiagramInfoArgs'\n    ) -> 'cirq.CircuitDiagramInfo':\n        n_qubits = len(self._qid_shape)\n        if self._name is not None:\n            symbols = (\n                [self._name] if n_qubits == 1 else [f'{self._name}[{i+1}]' for i in range(n_qubits)]\n            )\n            return protocols.CircuitDiagramInfo(wire_symbols=symbols)\n        main = _matrix_to_diagram_symbol(self._matrix, args)\n        rest = [f'#{i+1}' for i in range(1, n_qubits)]\n        return protocols.CircuitDiagramInfo(wire_symbols=[main, *rest])\n\n    def __hash__(self) -> int:\n        vals = tuple(v for _, v in np.ndenumerate(self._matrix))\n        return hash((MatrixGate, vals))\n\n    def _approx_eq_(self, other: Any, atol) -> bool:\n        if not isinstance(other, type(self)):\n            return NotImplemented\n        return np.allclose(self._matrix, other._matrix, rtol=0, atol=atol)\n\n    def __eq__(self, other):\n        if not isinstance(other, type(self)):\n            return NotImplemented\n        return self._qid_shape == other._qid_shape and np.array_equal(self._matrix, other._matrix)\n\n    def __ne__(self, other):\n        return not self == other\n\n    def __repr__(self) -> str:\n        if all(e == 2 for e in self._qid_shape):\n            return f'cirq.MatrixGate({proper_repr(self._matrix)})'\n        return f'cirq.MatrixGate({proper_repr(self._matrix)}, qid_shape={self._qid_shape})'\n\n    def __str__(self) -> str:\n        return str(self._matrix.round(3))\n\n\ndef _matrix_to_diagram_symbol(matrix: np.ndarray, args: 'protocols.CircuitDiagramInfoArgs') -> str:\n    if args.precision is not None:\n        matrix = matrix.round(args.precision)\n    result = str(matrix)\n    if args.use_unicode_characters:\n        lines = result.split('\\n')\n        for i in range(len(lines)):\n            lines[i] = lines[i].replace('[[', '')\n            lines[i] = lines[i].replace(' [', '')\n            lines[i] = lines[i].replace(']', '')\n        w = max(len(line) for line in lines)\n        for i in range(len(lines)):\n            lines[i] = '\u2502' + lines[i].ljust(w) + '\u2502'\n        lines.insert(0, '\u250c' + ' ' * w + '\u2510')\n        lines.append('\u2514' + ' ' * w + '\u2518')\n        result = '\\n'.join(lines)\n    return result\n", "path": "cirq-core/cirq/ops/matrix_gates.py"}], "after_files": [{"content": "# Copyright 2018 The Cirq Developers\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Quantum gates defined by a matrix.\"\"\"\n\nfrom typing import Any, cast, Dict, Iterable, Optional, Tuple, TYPE_CHECKING\n\nimport numpy as np\n\nfrom cirq import linalg, protocols, _import\nfrom cirq._compat import proper_repr\nfrom cirq.ops import raw_types, phased_x_z_gate\n\nif TYPE_CHECKING:\n    import cirq\n\nsingle_qubit_decompositions = _import.LazyLoader(\n    'single_qubit_decompositions', globals(), 'cirq.transformers.analytical_decompositions'\n)\ntwo_qubit_to_cz = _import.LazyLoader(\n    'two_qubit_to_cz', globals(), 'cirq.transformers.analytical_decompositions'\n)\nthree_qubit_decomposition = _import.LazyLoader(\n    'three_qubit_decomposition', globals(), 'cirq.transformers.analytical_decompositions'\n)\n\n\nclass MatrixGate(raw_types.Gate):\n    r\"\"\"A unitary qubit or qudit gate defined entirely by its numpy matrix.\n\n    For example `cirq.MatrixGate(np.array([[0, 1j], [1, 0]]))` has the unitary matrix:\n\n    $$\n    \\begin{bmatrix}\n        0 & i \\\\\n        1 & 0\n    \\end{bmatrix}\n    $$\n    \"\"\"\n\n    def __init__(\n        self,\n        matrix: np.ndarray,\n        *,\n        name: str = None,\n        qid_shape: Optional[Iterable[int]] = None,\n        unitary_check_rtol: float = 1e-5,\n        unitary_check_atol: float = 1e-8,\n    ) -> None:\n        \"\"\"Initializes a matrix gate.\n\n        Args:\n            matrix: The matrix that defines the gate.\n            name: The optional name of the gate to be displayed.\n            qid_shape: The shape of state tensor that the matrix applies to.\n                If not specified, this value is inferred by assuming that the\n                matrix is supposed to apply to qubits.\n            unitary_check_rtol: The relative tolerance for checking whether the supplied matrix\n                is unitary. See `cirq.is_unitary`.\n            unitary_check_atol: The absolute tolerance for checking whether the supplied matrix\n                is unitary. See `cirq.is_unitary`.\n\n        Raises:\n            ValueError: If the matrix is not a square numpy array, if the matrix does not match\n                the `qid_shape`, if `qid_shape` is not supplied and the matrix dimension is\n                not a power of 2, or if the matrix not unitary (to the supplied precisions).\n        \"\"\"\n        if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]:\n            raise ValueError('`matrix` must be a square 2d numpy array.')\n\n        if qid_shape is None:\n            n = int(np.round(np.log2(matrix.shape[0] or 1)))\n            if 2**n != matrix.shape[0]:\n                raise ValueError(\n                    f'Matrix width ({matrix.shape[0]}) is not a power of 2 and '\n                    f'qid_shape is not specified.'\n                )\n            qid_shape = (2,) * n\n\n        self._matrix = matrix\n        self._qid_shape = tuple(qid_shape)\n        self._name = name\n        m = int(np.prod(self._qid_shape, dtype=np.int64))\n        if self._matrix.shape != (m, m):\n            raise ValueError(\n                'Wrong matrix shape for qid_shape.\\n'\n                f'Matrix shape: {self._matrix.shape}\\n'\n                f'qid_shape: {self._qid_shape}\\n'\n            )\n\n        if not linalg.is_unitary(matrix, rtol=unitary_check_rtol, atol=unitary_check_atol):\n            raise ValueError(f'Not a unitary matrix: {self._matrix}')\n\n    def _json_dict_(self) -> Dict[str, Any]:\n        return {'matrix': self._matrix.tolist(), 'qid_shape': self._qid_shape}\n\n    @classmethod\n    def _from_json_dict_(cls, matrix, qid_shape, **kwargs):\n        return cls(matrix=np.array(matrix), qid_shape=qid_shape)\n\n    def _qid_shape_(self) -> Tuple[int, ...]:\n        return self._qid_shape\n\n    def __pow__(self, exponent: Any) -> 'MatrixGate':\n        if not isinstance(exponent, (int, float)):\n            return NotImplemented\n        e = cast(float, exponent)\n        new_mat = linalg.map_eigenvalues(self._matrix, lambda b: b**e)\n        return MatrixGate(new_mat, qid_shape=self._qid_shape)\n\n    def _phase_by_(self, phase_turns: float, qubit_index: int) -> 'MatrixGate':\n        if not isinstance(phase_turns, (int, float)):\n            return NotImplemented\n        if self._qid_shape[qubit_index] != 2:\n            return NotImplemented\n        result = np.copy(self._matrix).reshape(self._qid_shape * 2)\n\n        p = np.exp(2j * np.pi * phase_turns)\n        i = qubit_index\n        j = qubit_index + len(self._qid_shape)\n        result[linalg.slice_for_qubits_equal_to([i], 1)] *= p\n        result[linalg.slice_for_qubits_equal_to([j], 1)] *= np.conj(p)\n        return MatrixGate(matrix=result.reshape(self._matrix.shape), qid_shape=self._qid_shape)\n\n    def _decompose_(self, qubits: Tuple['cirq.Qid', ...]) -> 'cirq.OP_TREE':\n        if self._qid_shape == (2,):\n            return [\n                g.on(qubits[0])\n                for g in single_qubit_decompositions.single_qubit_matrix_to_gates(self._matrix)\n            ]\n        if self._qid_shape == (2,) * 2:\n            return two_qubit_to_cz.two_qubit_matrix_to_cz_operations(\n                *qubits, self._matrix, allow_partial_czs=True\n            )\n        if self._qid_shape == (2,) * 3:\n            return three_qubit_decomposition.three_qubit_matrix_to_operations(*qubits, self._matrix)\n        return NotImplemented\n\n    def _has_unitary_(self) -> bool:\n        return True\n\n    def _unitary_(self) -> np.ndarray:\n        return np.copy(self._matrix)\n\n    def _circuit_diagram_info_(\n        self, args: 'cirq.CircuitDiagramInfoArgs'\n    ) -> 'cirq.CircuitDiagramInfo':\n        n_qubits = len(self._qid_shape)\n        if self._name is not None:\n            symbols = (\n                [self._name] if n_qubits == 1 else [f'{self._name}[{i+1}]' for i in range(n_qubits)]\n            )\n            return protocols.CircuitDiagramInfo(wire_symbols=symbols)\n        main = _matrix_to_diagram_symbol(self._matrix, args)\n        rest = [f'#{i+1}' for i in range(1, n_qubits)]\n        return protocols.CircuitDiagramInfo(wire_symbols=[main, *rest])\n\n    def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]:\n        args.validate_version('2.0')\n        if self._qid_shape == (2,):\n            return protocols.qasm(\n                phased_x_z_gate.PhasedXZGate.from_matrix(self._matrix), args=args, qubits=qubits\n            )\n        return None\n\n    def __hash__(self) -> int:\n        vals = tuple(v for _, v in np.ndenumerate(self._matrix))\n        return hash((MatrixGate, vals))\n\n    def _approx_eq_(self, other: Any, atol) -> bool:\n        if not isinstance(other, type(self)):\n            return NotImplemented\n        return np.allclose(self._matrix, other._matrix, rtol=0, atol=atol)\n\n    def __eq__(self, other):\n        if not isinstance(other, type(self)):\n            return NotImplemented\n        return self._qid_shape == other._qid_shape and np.array_equal(self._matrix, other._matrix)\n\n    def __ne__(self, other):\n        return not self == other\n\n    def __repr__(self) -> str:\n        if all(e == 2 for e in self._qid_shape):\n            return f'cirq.MatrixGate({proper_repr(self._matrix)})'\n        return f'cirq.MatrixGate({proper_repr(self._matrix)}, qid_shape={self._qid_shape})'\n\n    def __str__(self) -> str:\n        return str(self._matrix.round(3))\n\n\ndef _matrix_to_diagram_symbol(matrix: np.ndarray, args: 'protocols.CircuitDiagramInfoArgs') -> str:\n    if args.precision is not None:\n        matrix = matrix.round(args.precision)\n    result = str(matrix)\n    if args.use_unicode_characters:\n        lines = result.split('\\n')\n        for i in range(len(lines)):\n            lines[i] = lines[i].replace('[[', '')\n            lines[i] = lines[i].replace(' [', '')\n            lines[i] = lines[i].replace(']', '')\n        w = max(len(line) for line in lines)\n        for i in range(len(lines)):\n            lines[i] = '\u2502' + lines[i].ljust(w) + '\u2502'\n        lines.insert(0, '\u250c' + ' ' * w + '\u2510')\n        lines.append('\u2514' + ' ' * w + '\u2518')\n        result = '\\n'.join(lines)\n    return result\n", "path": "cirq-core/cirq/ops/matrix_gates.py"}]}