We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`MatrixGate` names don't survive serialization
**Description of the issue**

`_name` is excluded from `MatrixGate._json_dict_()` and so is reset to `None` by serialization+deserialization

**How to reproduce the issue**

```python
gate = cirq.MatrixGate(np.eye(2), name="name")
gate_after_serialization = cirq.read_json(json_text=cirq.to_json(gate))

print(gate._name)  # "name"
print(gate_after_serialization._name)  # None
```

**Cirq version**

```
1.2.0.dev20230105212249
```



--- 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, 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, phased_x_z_gate
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: bool = True,
59         unitary_check_rtol: float = 1e-5,
60         unitary_check_atol: float = 1e-8,
61     ) -> None:
62         """Initializes a matrix gate.
63 
64         Args:
65             matrix: The matrix that defines the gate.
66             name: The optional name of the gate to be displayed.
67             qid_shape: The shape of state tensor that the matrix applies to.
68                 If not specified, this value is inferred by assuming that the
69                 matrix is supposed to apply to qubits.
70             unitary_check: If True, check that the supplied matrix is unitary up to the
71                 given tolerances. This should only be disabled if the matrix has already been
72                 checked for unitarity, in which case we get a slight performance improvement by
73                 not checking again.
74             unitary_check_rtol: The relative tolerance for checking whether the supplied matrix
75                 is unitary. See `cirq.is_unitary`.
76             unitary_check_atol: The absolute tolerance for checking whether the supplied matrix
77                 is unitary. See `cirq.is_unitary`.
78 
79         Raises:
80             ValueError: If the matrix is not a square numpy array, if the matrix does not match
81                 the `qid_shape`, if `qid_shape` is not supplied and the matrix dimension is
82                 not a power of 2, or if the matrix not unitary (to the supplied precisions).
83         """
84         if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]:
85             raise ValueError('`matrix` must be a square 2d numpy array.')
86 
87         if qid_shape is None:
88             n = int(np.round(np.log2(matrix.shape[0] or 1)))
89             if 2**n != matrix.shape[0]:
90                 raise ValueError(
91                     f'Matrix width ({matrix.shape[0]}) is not a power of 2 and '
92                     f'qid_shape is not specified.'
93                 )
94             qid_shape = (2,) * n
95 
96         self._matrix = matrix
97         self._qid_shape = tuple(qid_shape)
98         self._name = name
99         m = int(np.prod(self._qid_shape, dtype=np.int64))
100         if self._matrix.shape != (m, m):
101             raise ValueError(
102                 'Wrong matrix shape for qid_shape.\n'
103                 f'Matrix shape: {self._matrix.shape}\n'
104                 f'qid_shape: {self._qid_shape}\n'
105             )
106 
107         if unitary_check and not linalg.is_unitary(
108             matrix, rtol=unitary_check_rtol, atol=unitary_check_atol
109         ):
110             raise ValueError(f'Not a unitary matrix: {matrix}')
111 
112     def with_name(self, name: str) -> 'MatrixGate':
113         """Creates a new MatrixGate with the same matrix and a new name."""
114         return MatrixGate(self._matrix, name=name, qid_shape=self._qid_shape, unitary_check=False)
115 
116     def _json_dict_(self) -> Dict[str, Any]:
117         return {'matrix': self._matrix.tolist(), 'qid_shape': self._qid_shape}
118 
119     @classmethod
120     def _from_json_dict_(cls, matrix, qid_shape, **kwargs):
121         return cls(matrix=np.array(matrix), qid_shape=qid_shape)
122 
123     def _qid_shape_(self) -> Tuple[int, ...]:
124         return self._qid_shape
125 
126     def __pow__(self, exponent: Any) -> 'MatrixGate':
127         if not isinstance(exponent, (int, float)):
128             return NotImplemented
129         new_mat = linalg.map_eigenvalues(self._matrix, lambda b: b**exponent)
130         return MatrixGate(new_mat, qid_shape=self._qid_shape)
131 
132     def _phase_by_(self, phase_turns: float, qubit_index: int) -> 'MatrixGate':
133         if not isinstance(phase_turns, (int, float)):
134             return NotImplemented
135         if self._qid_shape[qubit_index] != 2:
136             return NotImplemented
137         result = np.copy(self._matrix).reshape(self._qid_shape * 2)
138 
139         p = np.exp(2j * np.pi * phase_turns)
140         i = qubit_index
141         j = qubit_index + len(self._qid_shape)
142         result[linalg.slice_for_qubits_equal_to([i], 1)] *= p
143         result[linalg.slice_for_qubits_equal_to([j], 1)] *= np.conj(p)
144         return MatrixGate(matrix=result.reshape(self._matrix.shape), qid_shape=self._qid_shape)
145 
146     def _decompose_(self, qubits: Tuple['cirq.Qid', ...]) -> 'cirq.OP_TREE':
147         if self._qid_shape == (2,):
148             return [
149                 g.on(qubits[0])
150                 for g in single_qubit_decompositions.single_qubit_matrix_to_gates(self._matrix)
151             ]
152         if self._qid_shape == (2,) * 2:
153             return two_qubit_to_cz.two_qubit_matrix_to_cz_operations(
154                 *qubits, self._matrix, allow_partial_czs=True
155             )
156         if self._qid_shape == (2,) * 3:
157             return three_qubit_decomposition.three_qubit_matrix_to_operations(*qubits, self._matrix)
158         return NotImplemented
159 
160     def _has_unitary_(self) -> bool:
161         return True
162 
163     def _unitary_(self) -> np.ndarray:
164         return np.copy(self._matrix)
165 
166     def _circuit_diagram_info_(
167         self, args: 'cirq.CircuitDiagramInfoArgs'
168     ) -> 'cirq.CircuitDiagramInfo':
169         n_qubits = len(self._qid_shape)
170         if self._name is not None:
171             symbols = (
172                 [self._name] if n_qubits == 1 else [f'{self._name}[{i+1}]' for i in range(n_qubits)]
173             )
174             return protocols.CircuitDiagramInfo(wire_symbols=symbols)
175         main = _matrix_to_diagram_symbol(self._matrix, args)
176         rest = [f'#{i+1}' for i in range(1, n_qubits)]
177         return protocols.CircuitDiagramInfo(wire_symbols=[main, *rest])
178 
179     def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]:
180         args.validate_version('2.0')
181         if self._qid_shape == (2,):
182             return protocols.qasm(
183                 phased_x_z_gate.PhasedXZGate.from_matrix(self._matrix), args=args, qubits=qubits
184             )
185         return None
186 
187     def __hash__(self) -> int:
188         vals = tuple(v for _, v in np.ndenumerate(self._matrix))
189         return hash((MatrixGate, vals))
190 
191     def _approx_eq_(self, other: Any, atol) -> bool:
192         if not isinstance(other, type(self)):
193             return NotImplemented
194         return np.allclose(self._matrix, other._matrix, rtol=0, atol=atol)
195 
196     def __eq__(self, other):
197         if not isinstance(other, type(self)):
198             return NotImplemented
199         return self._qid_shape == other._qid_shape and np.array_equal(self._matrix, other._matrix)
200 
201     def __ne__(self, other):
202         return not self == other
203 
204     def __repr__(self) -> str:
205         if all(e == 2 for e in self._qid_shape):
206             return f'cirq.MatrixGate({proper_repr(self._matrix)})'
207         return f'cirq.MatrixGate({proper_repr(self._matrix)}, qid_shape={self._qid_shape})'
208 
209     def __str__(self) -> str:
210         return str(self._matrix.round(3))
211 
212 
213 def _matrix_to_diagram_symbol(matrix: np.ndarray, args: 'protocols.CircuitDiagramInfoArgs') -> str:
214     if args.precision is not None:
215         matrix = matrix.round(args.precision)
216     result = str(matrix)
217     if args.use_unicode_characters:
218         lines = result.split('\n')
219         for i in range(len(lines)):
220             lines[i] = lines[i].replace('[[', '')
221             lines[i] = lines[i].replace(' [', '')
222             lines[i] = lines[i].replace(']', '')
223         w = max(len(line) for line in lines)
224         for i in range(len(lines)):
225             lines[i] = '│' + lines[i].ljust(w) + '│'
226         lines.insert(0, '┌' + ' ' * w + '┐')
227         lines.append('└' + ' ' * w + '┘')
228         result = '\n'.join(lines)
229     return result
230
```
--- 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
@@ -114,11 +114,15 @@
         return MatrixGate(self._matrix, name=name, qid_shape=self._qid_shape, unitary_check=False)
 
     def _json_dict_(self) -> Dict[str, Any]:
-        return {'matrix': self._matrix.tolist(), 'qid_shape': self._qid_shape}
+        return {
+            'matrix': self._matrix.tolist(),
+            'qid_shape': self._qid_shape,
+            **({'name': self._name} if self._name is not None else {}),
+        }
 
     @classmethod
-    def _from_json_dict_(cls, matrix, qid_shape, **kwargs):
-        return cls(matrix=np.array(matrix), qid_shape=qid_shape)
+    def _from_json_dict_(cls, matrix, qid_shape, name=None, **kwargs):
+        return cls(matrix=np.array(matrix), qid_shape=qid_shape, name=name)
 
     def _qid_shape_(self) -> Tuple[int, ...]:
         return self._qid_shape

{"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@@ -114,11 +114,15 @@\n         return MatrixGate(self._matrix, name=name, qid_shape=self._qid_shape, unitary_check=False)\n \n     def _json_dict_(self) -> Dict[str, Any]:\n-        return {'matrix': self._matrix.tolist(), 'qid_shape': self._qid_shape}\n+        return {\n+            'matrix': self._matrix.tolist(),\n+            'qid_shape': self._qid_shape,\n+            **({'name': self._name} if self._name is not None else {}),\n+        }\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+    def _from_json_dict_(cls, matrix, qid_shape, name=None, **kwargs):\n+        return cls(matrix=np.array(matrix), qid_shape=qid_shape, name=name)\n \n     def _qid_shape_(self) -> Tuple[int, ...]:\n         return self._qid_shape\n", "issue": "`MatrixGate` names don't survive serialization\n**Description of the issue**\r\n\r\n`_name` is excluded from `MatrixGate._json_dict_()` and so is reset to `None` by serialization+deserialization\r\n\r\n**How to reproduce the issue**\r\n\r\n```python\r\ngate = cirq.MatrixGate(np.eye(2), name=\"name\")\r\ngate_after_serialization = cirq.read_json(json_text=cirq.to_json(gate))\r\n\r\nprint(gate._name)  # \"name\"\r\nprint(gate_after_serialization._name)  # None\r\n```\r\n\r\n**Cirq version**\r\n\r\n```\r\n1.2.0.dev20230105212249\r\n```\r\n\r\n\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, 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: bool = True,\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: If True, check that the supplied matrix is unitary up to the\n                given tolerances. This should only be disabled if the matrix has already been\n                checked for unitarity, in which case we get a slight performance improvement by\n                not checking again.\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 unitary_check and not linalg.is_unitary(\n            matrix, rtol=unitary_check_rtol, atol=unitary_check_atol\n        ):\n            raise ValueError(f'Not a unitary matrix: {matrix}')\n\n    def with_name(self, name: str) -> 'MatrixGate':\n        \"\"\"Creates a new MatrixGate with the same matrix and a new name.\"\"\"\n        return MatrixGate(self._matrix, name=name, qid_shape=self._qid_shape, unitary_check=False)\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        new_mat = linalg.map_eigenvalues(self._matrix, lambda b: b**exponent)\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"}], "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, 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: bool = True,\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: If True, check that the supplied matrix is unitary up to the\n                given tolerances. This should only be disabled if the matrix has already been\n                checked for unitarity, in which case we get a slight performance improvement by\n                not checking again.\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 unitary_check and not linalg.is_unitary(\n            matrix, rtol=unitary_check_rtol, atol=unitary_check_atol\n        ):\n            raise ValueError(f'Not a unitary matrix: {matrix}')\n\n    def with_name(self, name: str) -> 'MatrixGate':\n        \"\"\"Creates a new MatrixGate with the same matrix and a new name.\"\"\"\n        return MatrixGate(self._matrix, name=name, qid_shape=self._qid_shape, unitary_check=False)\n\n    def _json_dict_(self) -> Dict[str, Any]:\n        return {\n            'matrix': self._matrix.tolist(),\n            'qid_shape': self._qid_shape,\n            **({'name': self._name} if self._name is not None else {}),\n        }\n\n    @classmethod\n    def _from_json_dict_(cls, matrix, qid_shape, name=None, **kwargs):\n        return cls(matrix=np.array(matrix), qid_shape=qid_shape, name=name)\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        new_mat = linalg.map_eigenvalues(self._matrix, lambda b: b**exponent)\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"}]}