Upload custom_gates.ipynb

#22
by NicheC - opened
Files changed (1) hide show
  1. custom_gates.ipynb +846 -0
custom_gates.ipynb ADDED
@@ -0,0 +1,846 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {
6
+ "id": "WZ1G8QHhdHZR"
7
+ },
8
+ "source": [
9
+ "##### Copyright 2020 The Cirq Developers"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": 1,
15
+ "metadata": {
16
+ "cellView": "form",
17
+ "execution": {
18
+ "iopub.execute_input": "2025-03-01T10:30:30.446798Z",
19
+ "iopub.status.busy": "2025-03-01T10:30:30.446341Z",
20
+ "iopub.status.idle": "2025-03-01T10:30:30.450288Z",
21
+ "shell.execute_reply": "2025-03-01T10:30:30.449623Z"
22
+ },
23
+ "id": "KQa9t_gadIuR"
24
+ },
25
+ "outputs": [],
26
+ "source": [
27
+ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
28
+ "# you may not use this file except in compliance with the License.\n",
29
+ "# You may obtain a copy of the License at\n",
30
+ "#\n",
31
+ "# https://www.apache.org/licenses/LICENSE-2.0\n",
32
+ "#\n",
33
+ "# Unless required by applicable law or agreed to in writing, software\n",
34
+ "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
35
+ "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
36
+ "# See the License for the specific language governing permissions and\n",
37
+ "# limitations under the License."
38
+ ]
39
+ },
40
+ {
41
+ "cell_type": "markdown",
42
+ "metadata": {
43
+ "id": "xwec7FrkdFmi"
44
+ },
45
+ "source": [
46
+ "# Custom gates"
47
+ ]
48
+ },
49
+ {
50
+ "cell_type": "markdown",
51
+ "metadata": {
52
+ "id": "5KZia7jmdJ3V"
53
+ },
54
+ "source": [
55
+ "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
56
+ " <td>\n",
57
+ " <a target=\"_blank\" href=\"https://quantumai.google/cirq/build/custom_gates\"><img src=\"https://quantumai.google/site-assets/images/buttons/quantumai_logo_1x.png\" />View on QuantumAI</a>\n",
58
+ " </td>\n",
59
+ " <td>\n",
60
+ " <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/main/docs/build/custom_gates.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/colab_logo_1x.png\" />Run in Google Colab</a>\n",
61
+ " </td>\n",
62
+ " <td>\n",
63
+ " <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/main/docs/build/custom_gates.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/github_logo_1x.png\" />View source on GitHub</a>\n",
64
+ " </td>\n",
65
+ " <td>\n",
66
+ " <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/build/custom_gates.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/download_icon_1x.png\" />Download notebook</a>\n",
67
+ " </td>\n",
68
+ "</table>"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "code",
73
+ "execution_count": 2,
74
+ "metadata": {
75
+ "execution": {
76
+ "iopub.execute_input": "2025-03-01T10:30:30.453242Z",
77
+ "iopub.status.busy": "2025-03-01T10:30:30.452740Z",
78
+ "iopub.status.idle": "2025-03-01T10:30:49.820952Z",
79
+ "shell.execute_reply": "2025-03-01T10:30:49.820063Z"
80
+ },
81
+ "id": "bd9529db1c0b"
82
+ },
83
+ "outputs": [
84
+ {
85
+ "name": "stdout",
86
+ "output_type": "stream",
87
+ "text": [
88
+ "installing cirq...\n"
89
+ ]
90
+ },
91
+ {
92
+ "name": "stdout",
93
+ "output_type": "stream",
94
+ "text": [
95
+ "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\r\n",
96
+ "tensorflow-metadata 1.16.1 requires protobuf<4.21,>=3.20.3; python_version < \"3.11\", but you have protobuf 4.25.6 which is incompatible.\u001b[0m\u001b[31m\r\n",
97
+ "\u001b[0m"
98
+ ]
99
+ },
100
+ {
101
+ "name": "stdout",
102
+ "output_type": "stream",
103
+ "text": [
104
+ "installed cirq.\n"
105
+ ]
106
+ }
107
+ ],
108
+ "source": [
109
+ "try:\n",
110
+ " import cirq\n",
111
+ "except ImportError:\n",
112
+ " print(\"installing cirq...\")\n",
113
+ " !pip install --quiet cirq\n",
114
+ " print(\"installed cirq.\")\n",
115
+ " import cirq\n",
116
+ " \n",
117
+ "import numpy as np"
118
+ ]
119
+ },
120
+ {
121
+ "cell_type": "markdown",
122
+ "metadata": {
123
+ "id": "y8P1T6duC-yo"
124
+ },
125
+ "source": [
126
+ "Standard gates such as Pauli gates and `CNOT`s are defined in `cirq.ops` as described [here](gates.ipynb). To use a unitary which is not a standard gate in a circuit, one can create a custom gate as described in this guide."
127
+ ]
128
+ },
129
+ {
130
+ "cell_type": "markdown",
131
+ "metadata": {
132
+ "id": "71ae01d45738"
133
+ },
134
+ "source": [
135
+ "## General pattern"
136
+ ]
137
+ },
138
+ {
139
+ "cell_type": "markdown",
140
+ "metadata": {
141
+ "id": "ce675022b0b4"
142
+ },
143
+ "source": [
144
+ "Gates are classes in Cirq. To define custom gates, we inherit from a base gate class and define a few methods.\n",
145
+ "\n",
146
+ "The general pattern is to:\n",
147
+ "\n",
148
+ " - Inherit from `cirq.Gate`.\n",
149
+ " - Define one of the `_num_qubits_` or `_qid_shape_` methods.\n",
150
+ " - Define one of the `_unitary_` or `_decompose_` methods.\n",
151
+ " \n",
152
+ "\n",
153
+ "> *Note*: Methods beginning and ending with one or more underscores are *magic methods* and are used by Cirq's protocols or built-in Python functions. More information about magic methods is included at the end of this guide.\n",
154
+ "\n",
155
+ "We demonstrate these patterns via the following examples.\n"
156
+ ]
157
+ },
158
+ {
159
+ "cell_type": "markdown",
160
+ "metadata": {
161
+ "id": "38c6a07df259"
162
+ },
163
+ "source": [
164
+ "## From a unitary"
165
+ ]
166
+ },
167
+ {
168
+ "cell_type": "markdown",
169
+ "metadata": {
170
+ "id": "58228b4b49f4"
171
+ },
172
+ "source": [
173
+ "One can create a custom Cirq gate from a unitary matrix in the following manner. Here, we define a gate which corresponds to the unitary\n",
174
+ "\n",
175
+ "\n",
176
+ "$$ U = \\frac{1}{\\sqrt{2}} \\left[ \\begin{matrix} 1 & 1 \\\\ -1 & 1 \\end{matrix} \\right] . $$"
177
+ ]
178
+ },
179
+ {
180
+ "cell_type": "code",
181
+ "execution_count": 3,
182
+ "metadata": {
183
+ "execution": {
184
+ "iopub.execute_input": "2025-03-01T10:30:49.825674Z",
185
+ "iopub.status.busy": "2025-03-01T10:30:49.824765Z",
186
+ "iopub.status.idle": "2025-03-01T10:30:49.830558Z",
187
+ "shell.execute_reply": "2025-03-01T10:30:49.829866Z"
188
+ },
189
+ "id": "66346efdd520"
190
+ },
191
+ "outputs": [],
192
+ "source": [
193
+ "\"\"\"Define a custom single-qubit gate.\"\"\"\n",
194
+ "class MyGate(cirq.Gate):\n",
195
+ " def __init__(self):\n",
196
+ " super(MyGate, self)\n",
197
+ " \n",
198
+ " def _num_qubits_(self):\n",
199
+ " return 1\n",
200
+ " \n",
201
+ " def _unitary_(self):\n",
202
+ " return np.array([\n",
203
+ " [1.0, 1.0],\n",
204
+ " [-1.0, 1.0]\n",
205
+ " ]) / np.sqrt(2)\n",
206
+ " \n",
207
+ " def _circuit_diagram_info_(self, args):\n",
208
+ " return \"G\"\n",
209
+ "\n",
210
+ "my_gate = MyGate()"
211
+ ]
212
+ },
213
+ {
214
+ "cell_type": "markdown",
215
+ "metadata": {
216
+ "id": "873c956ccf0e"
217
+ },
218
+ "source": [
219
+ "In this example, the `_num_qubits_` method tells Cirq that this gate acts on a single-qubit, and the `_unitary_` method defines the unitary of the gate. The `_circuit_diagram_info_` method tells Cirq how to display the gate in a circuit, as we will see below.\n",
220
+ "\n",
221
+ "Once this gate is defined, it can be used like any standard gate in Cirq."
222
+ ]
223
+ },
224
+ {
225
+ "cell_type": "code",
226
+ "execution_count": 4,
227
+ "metadata": {
228
+ "execution": {
229
+ "iopub.execute_input": "2025-03-01T10:30:49.833437Z",
230
+ "iopub.status.busy": "2025-03-01T10:30:49.832925Z",
231
+ "iopub.status.idle": "2025-03-01T10:30:49.838682Z",
232
+ "shell.execute_reply": "2025-03-01T10:30:49.838014Z"
233
+ },
234
+ "id": "ec8550e51178"
235
+ },
236
+ "outputs": [
237
+ {
238
+ "name": "stdout",
239
+ "output_type": "stream",
240
+ "text": [
241
+ "Circuit with custom gates:\n",
242
+ "0: ───G───\n"
243
+ ]
244
+ }
245
+ ],
246
+ "source": [
247
+ "\"\"\"Use the custom gate in a circuit.\"\"\"\n",
248
+ "circ = cirq.Circuit(\n",
249
+ " my_gate.on(cirq.LineQubit(0))\n",
250
+ ")\n",
251
+ "\n",
252
+ "print(\"Circuit with custom gates:\")\n",
253
+ "print(circ)"
254
+ ]
255
+ },
256
+ {
257
+ "cell_type": "markdown",
258
+ "metadata": {
259
+ "id": "dc0e4ee48211"
260
+ },
261
+ "source": [
262
+ "When we print the circuit, we see the symbol we specified in the `_circuit_diagram_info_` method.\n",
263
+ "\n",
264
+ "Circuits with custom gates can be simulated in the same manner as circuits with standard gates."
265
+ ]
266
+ },
267
+ {
268
+ "cell_type": "code",
269
+ "execution_count": 5,
270
+ "metadata": {
271
+ "execution": {
272
+ "iopub.execute_input": "2025-03-01T10:30:49.841511Z",
273
+ "iopub.status.busy": "2025-03-01T10:30:49.841010Z",
274
+ "iopub.status.idle": "2025-03-01T10:30:49.846952Z",
275
+ "shell.execute_reply": "2025-03-01T10:30:49.846263Z"
276
+ },
277
+ "id": "3885c629a1ef"
278
+ },
279
+ "outputs": [
280
+ {
281
+ "name": "stdout",
282
+ "output_type": "stream",
283
+ "text": [
284
+ "measurements: (no measurements)\n",
285
+ "\n",
286
+ "qubits: (cirq.LineQubit(0),)\n",
287
+ "output vector: 0.707|0⟩ - 0.707|1⟩\n",
288
+ "\n",
289
+ "phase:\n",
290
+ "output vector: |⟩\n"
291
+ ]
292
+ }
293
+ ],
294
+ "source": [
295
+ "\"\"\"Simulate a circuit with a custom gate.\"\"\"\n",
296
+ "sim = cirq.Simulator()\n",
297
+ "\n",
298
+ "res = sim.simulate(circ)\n",
299
+ "print(res)"
300
+ ]
301
+ },
302
+ {
303
+ "cell_type": "code",
304
+ "execution_count": 6,
305
+ "metadata": {
306
+ "execution": {
307
+ "iopub.execute_input": "2025-03-01T10:30:49.849662Z",
308
+ "iopub.status.busy": "2025-03-01T10:30:49.849087Z",
309
+ "iopub.status.idle": "2025-03-01T10:30:49.854351Z",
310
+ "shell.execute_reply": "2025-03-01T10:30:49.853689Z"
311
+ },
312
+ "id": "71dd8d4666fc"
313
+ },
314
+ "outputs": [],
315
+ "source": [
316
+ "\"\"\"Define a custom two-qubit gate.\"\"\"\n",
317
+ "class AnotherGate(cirq.Gate):\n",
318
+ " def __init__(self):\n",
319
+ " super(AnotherGate, self)\n",
320
+ "\n",
321
+ " def _num_qubits_(self):\n",
322
+ " return 2\n",
323
+ " \n",
324
+ " def _unitary_(self):\n",
325
+ " return np.array([\n",
326
+ " [1.0, -1.0, 0.0, 0.0],\n",
327
+ " [0.0, 0.0, 1.0, 1.0],\n",
328
+ " [1.0, 1.0, 0.0, 0.0],\n",
329
+ " [0.0, 0.0, 1.0, -1.0]\n",
330
+ " ]) / np.sqrt(2)\n",
331
+ " \n",
332
+ " def _circuit_diagram_info_(self, args):\n",
333
+ " return \"Top wire symbol\", \"Bottom wire symbol\"\n",
334
+ "\n",
335
+ "this_gate = AnotherGate()"
336
+ ]
337
+ },
338
+ {
339
+ "cell_type": "markdown",
340
+ "metadata": {
341
+ "id": "9c79b54f0ab4"
342
+ },
343
+ "source": [
344
+ "Here, the `_circuit_diagram_info_` method returns two symbols (one for each wire) since it is a two-qubit gate."
345
+ ]
346
+ },
347
+ {
348
+ "cell_type": "code",
349
+ "execution_count": 7,
350
+ "metadata": {
351
+ "execution": {
352
+ "iopub.execute_input": "2025-03-01T10:30:49.857084Z",
353
+ "iopub.status.busy": "2025-03-01T10:30:49.856652Z",
354
+ "iopub.status.idle": "2025-03-01T10:30:49.861391Z",
355
+ "shell.execute_reply": "2025-03-01T10:30:49.860709Z"
356
+ },
357
+ "id": "280e34a34bd6"
358
+ },
359
+ "outputs": [
360
+ {
361
+ "name": "stdout",
362
+ "output_type": "stream",
363
+ "text": [
364
+ "Circuit with custom two-qubit gate:\n",
365
+ "0: ───Top wire symbol──────\n",
366
+ " │\n",
367
+ "1: ───Bottom wire symbol───\n"
368
+ ]
369
+ }
370
+ ],
371
+ "source": [
372
+ "\"\"\"Use the custom two-qubit gate in a circuit.\"\"\"\n",
373
+ "circ = cirq.Circuit(\n",
374
+ " this_gate.on(*cirq.LineQubit.range(2))\n",
375
+ ")\n",
376
+ "\n",
377
+ "print(\"Circuit with custom two-qubit gate:\")\n",
378
+ "print(circ)"
379
+ ]
380
+ },
381
+ {
382
+ "cell_type": "markdown",
383
+ "metadata": {
384
+ "id": "45a8342180aa"
385
+ },
386
+ "source": [
387
+ "As above, this circuit can also be simulated in the expected way."
388
+ ]
389
+ },
390
+ {
391
+ "cell_type": "markdown",
392
+ "metadata": {
393
+ "id": "c896c2bb5f23"
394
+ },
395
+ "source": [
396
+ "### With parameters"
397
+ ]
398
+ },
399
+ {
400
+ "cell_type": "markdown",
401
+ "metadata": {
402
+ "id": "ef59ca39c94c"
403
+ },
404
+ "source": [
405
+ "Custom gates can be defined and used with parameters. For example, to define the gate\n",
406
+ "\n",
407
+ "$$ R(\\theta) = \\left[ \\begin{matrix} \\cos \\theta & \\sin \\theta \\\\ \\sin \\theta & - \\cos \\theta \\end{matrix} \\right], $$\n",
408
+ "\n",
409
+ "we can do the following."
410
+ ]
411
+ },
412
+ {
413
+ "cell_type": "code",
414
+ "execution_count": 8,
415
+ "metadata": {
416
+ "execution": {
417
+ "iopub.execute_input": "2025-03-01T10:30:49.864308Z",
418
+ "iopub.status.busy": "2025-03-01T10:30:49.863892Z",
419
+ "iopub.status.idle": "2025-03-01T10:30:49.868943Z",
420
+ "shell.execute_reply": "2025-03-01T10:30:49.868283Z"
421
+ },
422
+ "id": "262d28526fef"
423
+ },
424
+ "outputs": [],
425
+ "source": [
426
+ "\"\"\"Define a custom gate with a parameter.\"\"\"\n",
427
+ "class RotationGate(cirq.Gate):\n",
428
+ " def __init__(self, theta):\n",
429
+ " super(RotationGate, self)\n",
430
+ " self.theta = theta\n",
431
+ " \n",
432
+ " def _num_qubits_(self):\n",
433
+ " return 1\n",
434
+ " \n",
435
+ " def _unitary_(self):\n",
436
+ " return np.array([\n",
437
+ " [np.cos(self.theta), np.sin(self.theta)],\n",
438
+ " [np.sin(self.theta), -np.cos(self.theta)]\n",
439
+ " ]) / np.sqrt(2)\n",
440
+ " \n",
441
+ " def _circuit_diagram_info_(self, args):\n",
442
+ " return f\"R({self.theta})\""
443
+ ]
444
+ },
445
+ {
446
+ "cell_type": "markdown",
447
+ "metadata": {
448
+ "id": "8a10fdb09fca"
449
+ },
450
+ "source": [
451
+ "This gate can be used in a circuit as shown below."
452
+ ]
453
+ },
454
+ {
455
+ "cell_type": "code",
456
+ "execution_count": 9,
457
+ "metadata": {
458
+ "execution": {
459
+ "iopub.execute_input": "2025-03-01T10:30:49.871773Z",
460
+ "iopub.status.busy": "2025-03-01T10:30:49.871205Z",
461
+ "iopub.status.idle": "2025-03-01T10:30:49.875861Z",
462
+ "shell.execute_reply": "2025-03-01T10:30:49.875171Z"
463
+ },
464
+ "id": "485c560f0d25"
465
+ },
466
+ "outputs": [
467
+ {
468
+ "name": "stdout",
469
+ "output_type": "stream",
470
+ "text": [
471
+ "Circuit with a custom rotation gate:\n",
472
+ "0: ───R(0.1)───\n"
473
+ ]
474
+ }
475
+ ],
476
+ "source": [
477
+ "\"\"\"Use the custom gate in a circuit.\"\"\"\n",
478
+ "circ = cirq.Circuit(\n",
479
+ " RotationGate(theta=0.1).on(cirq.LineQubit(0))\n",
480
+ ")\n",
481
+ "\n",
482
+ "print(\"Circuit with a custom rotation gate:\")\n",
483
+ "print(circ)"
484
+ ]
485
+ },
486
+ {
487
+ "cell_type": "markdown",
488
+ "metadata": {
489
+ "id": "baf273b2fe60"
490
+ },
491
+ "source": [
492
+ "## From a known decomposition"
493
+ ]
494
+ },
495
+ {
496
+ "cell_type": "markdown",
497
+ "metadata": {
498
+ "id": "708300eb2c33"
499
+ },
500
+ "source": [
501
+ "Custom gates can also be defined from a known decomposition (of gates). This is useful, for example, when groups of gates appear repeatedly in a circuit, or when a standard decomposition of a gate into primitive gates is known.\n",
502
+ "\n",
503
+ "We show an example below of a custom swap gate defined from a known decomposition of three CNOT gates."
504
+ ]
505
+ },
506
+ {
507
+ "cell_type": "code",
508
+ "execution_count": 10,
509
+ "metadata": {
510
+ "execution": {
511
+ "iopub.execute_input": "2025-03-01T10:30:49.878807Z",
512
+ "iopub.status.busy": "2025-03-01T10:30:49.878256Z",
513
+ "iopub.status.idle": "2025-03-01T10:30:49.883075Z",
514
+ "shell.execute_reply": "2025-03-01T10:30:49.882426Z"
515
+ },
516
+ "id": "2c656362cd95"
517
+ },
518
+ "outputs": [],
519
+ "source": [
520
+ "class MySwap(cirq.Gate):\n",
521
+ " def __init__(self):\n",
522
+ " super(MySwap, self)\n",
523
+ "\n",
524
+ " def _num_qubits_(self):\n",
525
+ " return 2\n",
526
+ "\n",
527
+ " def _decompose_(self, qubits):\n",
528
+ " a, b = qubits\n",
529
+ " yield cirq.CNOT(a, b)\n",
530
+ " yield cirq.CNOT(b, a)\n",
531
+ " yield cirq.CNOT(a, b)\n",
532
+ " \n",
533
+ " def _circuit_diagram_info_(self, args):\n",
534
+ " return [\"CustomSWAP\"] * self.num_qubits()\n",
535
+ "\n",
536
+ "my_swap = MySwap()"
537
+ ]
538
+ },
539
+ {
540
+ "cell_type": "markdown",
541
+ "metadata": {
542
+ "id": "829c4602757a"
543
+ },
544
+ "source": [
545
+ "The `_decompose_` method yields the operations which implement the custom gate. (One can also return a list of operations instead of a generator.)\n",
546
+ "\n",
547
+ "When we use this gate in a circuit, the individual gates in the decomposition do not appear in the circuit. Instead, the `_circuit_diagram_info_` appears in the circuit. As mentioned, this can be useful for interpreting circuits at a higher level than individual (primitive) gates."
548
+ ]
549
+ },
550
+ {
551
+ "cell_type": "code",
552
+ "execution_count": 11,
553
+ "metadata": {
554
+ "execution": {
555
+ "iopub.execute_input": "2025-03-01T10:30:49.885806Z",
556
+ "iopub.status.busy": "2025-03-01T10:30:49.885292Z",
557
+ "iopub.status.idle": "2025-03-01T10:30:49.890896Z",
558
+ "shell.execute_reply": "2025-03-01T10:30:49.890204Z"
559
+ },
560
+ "id": "psYGZcjUEF5V"
561
+ },
562
+ "outputs": [
563
+ {
564
+ "name": "stdout",
565
+ "output_type": "stream",
566
+ "text": [
567
+ "Circuit:\n",
568
+ "0: ───X───CustomSWAP───\n",
569
+ " │\n",
570
+ "1: ───────CustomSWAP───\n"
571
+ ]
572
+ }
573
+ ],
574
+ "source": [
575
+ "\"\"\"Use the custom gate in a circuit.\"\"\"\n",
576
+ "qreg = cirq.LineQubit.range(2)\n",
577
+ "circ = cirq.Circuit(\n",
578
+ " cirq.X(qreg[0]),\n",
579
+ " my_swap.on(*qreg)\n",
580
+ ")\n",
581
+ "\n",
582
+ "print(\"Circuit:\")\n",
583
+ "print(circ)"
584
+ ]
585
+ },
586
+ {
587
+ "cell_type": "markdown",
588
+ "metadata": {
589
+ "id": "856b1cdf0117"
590
+ },
591
+ "source": [
592
+ "We can simulate this circuit and verify it indeed swaps the qubits."
593
+ ]
594
+ },
595
+ {
596
+ "cell_type": "code",
597
+ "execution_count": 12,
598
+ "metadata": {
599
+ "execution": {
600
+ "iopub.execute_input": "2025-03-01T10:30:49.893697Z",
601
+ "iopub.status.busy": "2025-03-01T10:30:49.893128Z",
602
+ "iopub.status.idle": "2025-03-01T10:30:49.902576Z",
603
+ "shell.execute_reply": "2025-03-01T10:30:49.901930Z"
604
+ },
605
+ "id": "0cafcf4c4197"
606
+ },
607
+ "outputs": [
608
+ {
609
+ "data": {
610
+ "text/plain": [
611
+ "measurements: (no measurements)\n",
612
+ "\n",
613
+ "qubits: (cirq.LineQubit(0), cirq.LineQubit(1))\n",
614
+ "output vector: |01⟩\n",
615
+ "\n",
616
+ "phase:\n",
617
+ "output vector: |⟩"
618
+ ]
619
+ },
620
+ "execution_count": 12,
621
+ "metadata": {},
622
+ "output_type": "execute_result"
623
+ }
624
+ ],
625
+ "source": [
626
+ "\"\"\"Simulate the circuit.\"\"\"\n",
627
+ "sim.simulate(circ)"
628
+ ]
629
+ },
630
+ {
631
+ "cell_type": "markdown",
632
+ "metadata": {
633
+ "id": "09f425a61484"
634
+ },
635
+ "source": [
636
+ "## More on magic methods and protocols"
637
+ ]
638
+ },
639
+ {
640
+ "cell_type": "markdown",
641
+ "metadata": {
642
+ "id": "d63f32eb1ac7"
643
+ },
644
+ "source": [
645
+ "As mentioned, methods such as `_unitary_` which we have seen are known as \"magic\n",
646
+ "methods.\" Much of Cirq relies on \"magic methods\", which are methods prefixed with one or\n",
647
+ "two underscores and used by Cirq's protocols or built-in Python methods.\n",
648
+ "For instance, Python translates `cirq.Z**0.25` into\n",
649
+ "`cirq.Z.__pow__(0.25)`. Other uses are specific to cirq and are found in the\n",
650
+ "protocols subdirectory. They are defined below.\n",
651
+ "\n",
652
+ "At minimum, you will need to define either the ``_num_qubits_`` or\n",
653
+ "``_qid_shape_`` magic method to define the number of qubits (or qudits) used\n",
654
+ "in the gate."
655
+ ]
656
+ },
657
+ {
658
+ "cell_type": "markdown",
659
+ "metadata": {
660
+ "id": "d05fa2e8d1ab"
661
+ },
662
+ "source": [
663
+ "### Standard Python magic methods\n",
664
+ "\n",
665
+ "There are many standard magic methods in Python. Here are a few of the most\n",
666
+ "important ones used in Cirq:\n",
667
+ " * `__str__` for user-friendly string output and `__repr__` is the Python-friendly string output, meaning that `eval(repr(y))==y` should always be true.\n",
668
+ " * `__eq__` and `__hash__` which define whether objects are equal or not. You\n",
669
+ " can also use `cirq.value.value_equality` for objects that have a small list\n",
670
+ " of sub-values that can be compared for equality.\n",
671
+ " * Arithmetic functions such as `__pow__`, `__mul__`, `__add__` define the\n",
672
+ " action of `**`, `*`, and `+` respectively.\n",
673
+ " \n",
674
+ "### `cirq.num_qubits` and `def _num_qubits_`\n",
675
+ "\n",
676
+ "A `Gate` must implement the `_num_qubits_` (or `_qid_shape_`) method.\n",
677
+ "This method returns an integer and is used by `cirq.num_qubits` to determine\n",
678
+ "how many qubits this gate operates on.\n",
679
+ "\n",
680
+ "### `cirq.qid_shape` and `def _qid_shape_`\n",
681
+ "\n",
682
+ "A qudit gate or operation must implement the `_qid_shape_` method that returns a\n",
683
+ "tuple of integers. This method is used to determine how many qudits the gate or\n",
684
+ "operation operates on and what dimension each qudit must be. If only the\n",
685
+ "`_num_qubits_` method is implemented, the object is assumed to operate only on\n",
686
+ "qubits. Callers can query the qid shape of the object by calling\n",
687
+ "`cirq.qid_shape` on it. See [qudit documentation](qudits.ipynb) for more\n",
688
+ "information.\n",
689
+ "\n",
690
+ "### `cirq.unitary` and `def _unitary_`\n",
691
+ "\n",
692
+ "When an object can be described by a unitary matrix, it can expose that unitary\n",
693
+ "matrix by implementing a `_unitary_(self) -> np.ndarray` method.\n",
694
+ "Callers can query whether or not an object has a unitary matrix by calling\n",
695
+ "`cirq.unitary` on it.\n",
696
+ "The `_unitary_` method may also return `NotImplemented`, in which case\n",
697
+ "`cirq.unitary` behaves as if the method is not implemented.\n",
698
+ "\n",
699
+ "### `cirq.decompose` and `def _decompose_`\n",
700
+ "\n",
701
+ "Operations and gates can be defined in terms of other operations by implementing\n",
702
+ "a `_decompose_` method that returns those other operations. Operations implement\n",
703
+ "`_decompose_(self)` whereas gates implement `_decompose_(self, qubits)`\n",
704
+ "(since gates don't know their qubits ahead of time).\n",
705
+ "\n",
706
+ "The main requirements on the output of `_decompose_` methods are:\n",
707
+ "\n",
708
+ "1. DO NOT CREATE CYCLES. The `cirq.decompose` method will iterative decompose until it finds values satisfying a `keep` predicate. Cycles cause it to enter an infinite loop.\n",
709
+ "2. Head towards operations defined by Cirq, because these operations have good decomposition methods that terminate in single-qubit and two qubit gates.\n",
710
+ "These gates can be understood by the simulator, optimizers, and other code.\n",
711
+ "3. All that matters is functional equivalence.\n",
712
+ "Don't worry about staying within or reaching a particular gate set; it's too hard to predict what the caller will want. Gate-set-aware decomposition is useful, but *this is not the protocol that does that*.\n",
713
+ "Instead, use features available in the [transformer API](../transform/transformers.ipynb#compiling_to_nisq_targets_cirqcompilationtargetgateset).\n",
714
+ "\n",
715
+ "For example, `cirq.CCZ` decomposes into a series of `cirq.CNOT` and `cirq.T` operations.\n",
716
+ "This allows code that doesn't understand three-qubit operation to work with `cirq.CCZ`; by decomposing it into operations they do understand.\n",
717
+ "As another example, `cirq.TOFFOLI` decomposes into a `cirq.H` followed by a `cirq.CCZ` followed by a `cirq.H`.\n",
718
+ "Although the output contains a three qubit operation (the CCZ), that operation can be decomposed into two qubit and one qubit operations.\n",
719
+ "So code that doesn't understand three qubit operations can deal with Toffolis by decomposing them, and then decomposing the CCZs that result from the initial decomposition.\n",
720
+ "\n",
721
+ "In general, decomposition-aware code consuming operations is expected to recursively decompose unknown operations until the code either hits operations it understands or hits a dead end where no more decomposition is possible.\n",
722
+ "The `cirq.decompose` method implements logic for performing exactly this kind of recursive decomposition.\n",
723
+ "Callers specify a `keep` predicate, and optionally specify intercepting and fallback decomposers, and then `cirq.decompose` will repeatedly decompose whatever operations it was given until the operations satisfy the given `keep`.\n",
724
+ "If `cirq.decompose` hits a dead end, it raises an error.\n",
725
+ "\n",
726
+ "Cirq doesn't make any guarantees about the \"target gate set\" decomposition is heading towards.\n",
727
+ "`cirq.decompose` is not a method\n",
728
+ "Decompositions within Cirq happen to converge towards X, Y, Z, CZ, PhasedX, specified-matrix gates, and others.\n",
729
+ "But this set will vary from release to release, and so it is important for consumers of decompositions to look for generic properties of gates,\n",
730
+ "such as \"two qubit gate with a unitary matrix\", instead of specific gate types such as CZ gates.\n",
731
+ "\n",
732
+ "### `cirq.inverse` and `__pow__`\n",
733
+ "\n",
734
+ "Gates and operations are considered to be *invertible* when they implement a `__pow__` method that returns a result besides `NotImplemented` for an exponent of -1.\n",
735
+ "This inverse can be accessed either directly as `value**-1`, or via the utility method `cirq.inverse(value)`.\n",
736
+ "If you are sure that `value` has an inverse, saying `value**-1` is more convenient than saying `cirq.inverse(value)`.\n",
737
+ "`cirq.inverse` is for cases where you aren't sure if `value` is invertible, or where `value` might be a *sequence* of invertible operations.\n",
738
+ "\n",
739
+ "`cirq.inverse` has a `default` parameter used as a fallback when `value` isn't invertible.\n",
740
+ "For example, `cirq.inverse(value, default=None)` returns the inverse of `value`, or else returns `None` if `value` isn't invertible.\n",
741
+ "(If no `default` is specified and `value` isn't invertible, a `TypeError` is raised.)\n",
742
+ "\n",
743
+ "When you give `cirq.inverse` a list, or any other kind of iterable thing, it will return a sequence of operations that (if run in order) undoes the operations of the original sequence (if run in order).\n",
744
+ "Basically, the items of the list are individually inverted and returned in reverse order.\n",
745
+ "For example, the expression `cirq.inverse([cirq.S(b), cirq.CNOT(a, b)])` will return the tuple `(cirq.CNOT(a, b), cirq.S(b)**-1)`.\n",
746
+ "\n",
747
+ "Gates and operations can also return values beside `NotImplemented` from their `__pow__` method for exponents besides `-1`.\n",
748
+ "This pattern is used often by Cirq.\n",
749
+ "For example, the square root of X gate can be created by raising `cirq.X` to 0.5:"
750
+ ]
751
+ },
752
+ {
753
+ "cell_type": "code",
754
+ "execution_count": 13,
755
+ "metadata": {
756
+ "execution": {
757
+ "iopub.execute_input": "2025-03-01T10:30:49.905640Z",
758
+ "iopub.status.busy": "2025-03-01T10:30:49.905111Z",
759
+ "iopub.status.idle": "2025-03-01T10:30:49.909931Z",
760
+ "shell.execute_reply": "2025-03-01T10:30:49.909287Z"
761
+ },
762
+ "id": "a37d151e71ed"
763
+ },
764
+ "outputs": [
765
+ {
766
+ "name": "stdout",
767
+ "output_type": "stream",
768
+ "text": [
769
+ "[[0.+0.j 1.+0.j]\n",
770
+ " [1.+0.j 0.+0.j]]\n",
771
+ "[[0.5+0.5j 0.5-0.5j]\n",
772
+ " [0.5-0.5j 0.5+0.5j]]\n"
773
+ ]
774
+ }
775
+ ],
776
+ "source": [
777
+ "print(cirq.unitary(cirq.X))\n",
778
+ "# prints\n",
779
+ "# [[0.+0.j 1.+0.j]\n",
780
+ "# [1.+0.j 0.+0.j]]\n",
781
+ "\n",
782
+ "sqrt_x = cirq.X**0.5\n",
783
+ "print(cirq.unitary(sqrt_x))\n",
784
+ "# prints\n",
785
+ "# [[0.5+0.5j 0.5-0.5j]\n",
786
+ "# [0.5-0.5j 0.5+0.5j]]"
787
+ ]
788
+ },
789
+ {
790
+ "cell_type": "markdown",
791
+ "metadata": {
792
+ "id": "6fe65e2eb967"
793
+ },
794
+ "source": [
795
+ "The Pauli gates included in Cirq use the convention ``Z**0.5 ≡ S ≡ np.diag(1, i)``, ``Z**-0.5 ≡ S**-1``, ``X**0.5 ≡ H·S·H``, and the square root of ``Y`` is inferred via the right hand rule.\n"
796
+ ]
797
+ },
798
+ {
799
+ "cell_type": "markdown",
800
+ "metadata": {
801
+ "id": "9d8cecee52e8"
802
+ },
803
+ "source": [
804
+ "### `_circuit_diagram_info_(self, args)` and `cirq.circuit_diagram_info(val, [args], [default])`\n",
805
+ "\n",
806
+ "Circuit diagrams are useful for visualizing the structure of a `Circuit`.\n",
807
+ "Gates can specify compact representations to use in diagrams by implementing a `_circuit_diagram_info_` method.\n",
808
+ "For example, this is why SWAP gates are shown as linked '×' characters in diagrams.\n",
809
+ "\n",
810
+ "The `_circuit_diagram_info_` method takes an `args` parameter of type `cirq.CircuitDiagramInfoArgs` and returns either\n",
811
+ "a string (typically the gate's name), a sequence of strings (a label to use on each qubit targeted by the gate), or an\n",
812
+ "instance of `cirq.CircuitDiagramInfo` (which can specify more advanced properties such as exponents and will expand\n",
813
+ "in the future).\n",
814
+ "\n",
815
+ "You can query the circuit diagram info of a value by passing it into `cirq.circuit_diagram_info`."
816
+ ]
817
+ }
818
+ ],
819
+ "metadata": {
820
+ "colab": {
821
+ "collapsed_sections": [
822
+ "Sh9QBnKbFf_B"
823
+ ],
824
+ "name": "custom_gates.ipynb",
825
+ "toc_visible": true
826
+ },
827
+ "kernelspec": {
828
+ "display_name": "Python 3",
829
+ "name": "python3"
830
+ },
831
+ "language_info": {
832
+ "codemirror_mode": {
833
+ "name": "ipython",
834
+ "version": 3
835
+ },
836
+ "file_extension": ".py",
837
+ "mimetype": "text/x-python",
838
+ "name": "python",
839
+ "nbconvert_exporter": "python",
840
+ "pygments_lexer": "ipython3",
841
+ "version": "3.10.16"
842
+ }
843
+ },
844
+ "nbformat": 4,
845
+ "nbformat_minor": 0
846
+ }