1{
2 "cells": [
3  {
4   "cell_type": "markdown",
5   "metadata": {
6    "id": "cedf868076a2"
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    "id": "906e07f6e562"
18   },
19   "outputs": [],
20   "source": [
21    "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
22    "# you may not use this file except in compliance with the License.\n",
23    "# You may obtain a copy of the License at\n",
24    "#\n",
25    "# https://www.apache.org/licenses/LICENSE-2.0\n",
26    "#\n",
27    "# Unless required by applicable law or agreed to in writing, software\n",
28    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
29    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
30    "# See the License for the specific language governing permissions and\n",
31    "# limitations under the License."
32   ]
33  },
34  {
35   "cell_type": "markdown",
36   "metadata": {
37    "id": "166af89a7bc3"
38   },
39   "source": [
40    "# Neutral atom device class"
41   ]
42  },
43  {
44   "cell_type": "markdown",
45   "metadata": {
46    "id": "416c50754585"
47   },
48   "source": [
49    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
50    "  <td>\n",
51    "    <a target=\"_blank\" href=\"https://quantumai.google/cirq/tutorials/educators/neutral_atom\"><img src=\"https://quantumai.google/site-assets/images/buttons/quantumai_logo_1x.png\" />View on QuantumAI</a>\n",
52    "  </td>\n",
53    "  <td>\n",
54    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/master/docs/tutorials/educators/neutral_atom.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/colab_logo_1x.png\" />Run in Google Colab</a>\n",
55    "  </td>\n",
56    "  <td>\n",
57    "    <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/master/docs/tutorials/educators/neutral_atom.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/github_logo_1x.png\" />View source on GitHub</a>\n",
58    "  </td>\n",
59    "  <td>\n",
60    "    <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/tutorials/educators/neutral_atom.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/download_icon_1x.png\" />Download notebook</a>\n",
61    "  </td>\n",
62    "</table>"
63   ]
64  },
65  {
66   "cell_type": "markdown",
67   "metadata": {
68    "id": "bNlUyYQcgMF-"
69   },
70   "source": [
71    "This tutorial provides an introduction to making circuits that are compatible with neutral atom devices.\n",
72    "\n",
73    "Neutral atom devices implement quantum gates in one of two ways. One method is by hitting the entire qubit array with microwaves to simultaneously act on every qubit. This method implements global $XY$ gates which take up to $100$ microseconds to perform. Alternatively, we can shine laser light on some fraction of the array. Gates of this type typically take around $1$ microsecond to perform. This method can act on one or more qubits at a time up to some limit dictated by the available laser power and the beam steering system used to address the qubits. Each category in the native gate set has its own limit, discussed more below."
74   ]
75  },
76  {
77   "cell_type": "code",
78   "execution_count": 2,
79   "metadata": {
80    "id": "GTjMbjyAfJCK"
81   },
82   "outputs": [],
83   "source": [
84    "try:\n",
85    "    import cirq\n",
86    "except ImportError:\n",
87    "    print(\"installing cirq...\")\n",
88    "    !pip install cirq --quiet\n",
89    "    print(\"installed cirq.\")"
90   ]
91  },
92  {
93   "cell_type": "code",
94   "execution_count": 3,
95   "metadata": {
96    "id": "4927d86fa122"
97   },
98   "outputs": [],
99   "source": [
100    "from math import pi\n",
101    "\n",
102    "import cirq"
103   ]
104  },
105  {
106   "cell_type": "markdown",
107   "metadata": {
108    "id": "b23c49ab588f"
109   },
110   "source": [
111    "## Defining a `NeutralAtomDevice`"
112   ]
113  },
114  {
115   "cell_type": "markdown",
116   "metadata": {
117    "id": "Z0X2AWrhrcHR"
118   },
119   "source": [
120    "To define a `NeutralAtomDevice`, we specify\n",
121    "\n",
122    "- The set of qubits in the device.\n",
123    "- The maximum duration of gates and measurements.\n",
124    "- `max_parallel_z`: The maximum number of single qubit $Z$ rotations that can be applied in parallel.\n",
125    "- `max_parallel_xy`: The maximum number of single qubit $XY$ rotations that can be applied in parallel.\n",
126    "- `max_parallel_c`: The maximum number of atoms that can be affected by controlled gates simultaneously.\n",
127    "  - Note that `max_parallel_c` must be less than or equal to the minimum of `max_parallel_z` and `max_parallel_xy`.\n",
128    "- `control_radius`: The maximum allowed distance between atoms acted on by controlled gates.\n",
129    "\n",
130    "We show an example of defining a `NeutralAtomDevice` below."
131   ]
132  },
133  {
134   "cell_type": "code",
135   "execution_count": 4,
136   "metadata": {
137    "id": "On6Wrh3XhSPO"
138   },
139   "outputs": [],
140   "source": [
141    "\"\"\"Defining a NeutralAtomDevice.\"\"\"\n",
142    "# Define milliseconds and microseconds for convenience.\n",
143    "ms = cirq.Duration(nanos=10**6)\n",
144    "us = cirq.Duration(nanos=10**3)\n",
145    "\n",
146    "# Create a NeutralAtomDevice\n",
147    "neutral_atom_device = cirq.NeutralAtomDevice(\n",
148    "    qubits=cirq.GridQubit.rect(2, 3),\n",
149    "    measurement_duration=5 * ms,\n",
150    "    gate_duration=100 * us,\n",
151    "    max_parallel_z=3,\n",
152    "    max_parallel_xy=3,\n",
153    "    max_parallel_c=3,\n",
154    "    control_radius=2\n",
155    ")"
156   ]
157  },
158  {
159   "cell_type": "markdown",
160   "metadata": {
161    "id": "6342cf6147b2"
162   },
163   "source": [
164    "Note that all above arguments are required to instantiate a `NeutralAtomDevice`. The example device above has the following properties:\n",
165    "\n",
166    "- The device is defined on a $3 \\times 3$ grid of qubits.\n",
167    "- Measurements take $5$ milliseconds.\n",
168    "- Gates may take as long as $100$ microseconds if we utilize global microwave gates. Otherwise, a more reasonable bound would be $1$ microsecond.\n",
169    "- A maximum of $3$ qubits may be simultaneously acted on by any gate category (`max_parallel_c = 3`).\n",
170    "- Controlled gates have next-nearest neighbor connectivity (`control_radius = 2`).\n",
171    "\n",
172    "We can see some properties of the device as follows."
173   ]
174  },
175  {
176   "cell_type": "code",
177   "execution_count": 5,
178   "metadata": {
179    "id": "de96b2753bec"
180   },
181   "outputs": [
182    {
183     "name": "stdout",
184     "output_type": "stream",
185     "text": [
186      "Neutral atom device:\n",
187      "(0, 0)───(0, 1)───(0, 2)\n",
188      "│        │        │\n",
189      "│        │        │\n",
190      "(1, 0)───(1, 1)───(1, 2)\n",
191      "\n",
192      "Neighbors of qubit (0, 1):\n",
193      "[cirq.GridQubit(1, 1), cirq.GridQubit(0, 2), cirq.GridQubit(0, 0)]\n"
194     ]
195    }
196   ],
197   "source": [
198    "\"\"\"View some properties of the device.\"\"\"\n",
199    "# Display the neutral atom device.\n",
200    "print(\"Neutral atom device:\", neutral_atom_device, sep=\"\\n\")\n",
201    "\n",
202    "# Get the neighbors of a qubit.\n",
203    "qubit = cirq.GridQubit(0, 1)\n",
204    "print(f\"\\nNeighbors of qubit {qubit}:\")\n",
205    "print(neutral_atom_device.neighbors_of(qubit))"
206   ]
207  },
208  {
209   "cell_type": "markdown",
210   "metadata": {
211    "id": "8d75e1232082"
212   },
213   "source": [
214    "## Native gate set"
215   ]
216  },
217  {
218   "cell_type": "markdown",
219   "metadata": {
220    "id": "v3YMaPw1hfV_"
221   },
222   "source": [
223    "The gates supported by the `NeutralAtomDevice` class can be placed into three categories:\n",
224    "\n",
225    "1. Single-qubit rotations about the $Z$ axis.\n",
226    "2. Single-qubit rotations about an arbitrary axis in the $X$-$Y$ plane. We refer to these as $XY$ gates in this tutorial.\n",
227    "3. Controlled gates: CZ, CNOT, CCZ, and CCNOT (TOFFOLI).\n",
228    "\n",
229    "Any rotation angle is allowed for single-qubit rotations. Some examples of valid single-qubit rotations are shown below."
230   ]
231  },
232  {
233   "cell_type": "code",
234   "execution_count": 6,
235   "metadata": {
236    "id": "Hotk4cHCpXCV"
237   },
238   "outputs": [],
239   "source": [
240    "\"\"\"Examples of valid single-qubit gates.\"\"\"\n",
241    "# Single qubit Z rotations with any angle are valid.\n",
242    "neutral_atom_device.validate_gate(cirq.rz(pi / 5))\n",
243    "\n",
244    "# Single qubit rotations about the X-Y axis with any angle are valid.\n",
245    "neutral_atom_device.validate_gate(\n",
246    "    cirq.PhasedXPowGate(phase_exponent=pi / 3, exponent=pi / 7)\n",
247    ")"
248   ]
249  },
250  {
251   "cell_type": "markdown",
252   "metadata": {
253    "id": "239a75b7d5c6"
254   },
255   "source": [
256    "A Hadamard gate is invalid because it is a rotation in the $X$-$Z$ plane instead of the $X$-$Y$ plane."
257   ]
258  },
259  {
260   "cell_type": "code",
261   "execution_count": 7,
262   "metadata": {
263    "id": "a8b877ad0a87"
264   },
265   "outputs": [
266    {
267     "name": "stdout",
268     "output_type": "stream",
269     "text": [
270      "As expected, H is invalid! Unsupported gate: cirq.H\n"
271     ]
272    }
273   ],
274   "source": [
275    "\"\"\"Example of an invalid single-qubit gate.\"\"\"\n",
276    "invalid_gate = cirq.H\n",
277    "\n",
278    "try:\n",
279    "    neutral_atom_device.validate_gate(invalid_gate)\n",
280    "except ValueError as e:\n",
281    "    print(f\"As expected, {invalid_gate} is invalid!\", e)"
282   ]
283  },
284  {
285   "cell_type": "markdown",
286   "metadata": {
287    "id": "c29c2fd3d197"
288   },
289   "source": [
290    "For controlled gates, the rotation must be a multiple of $\\pi$ due to the physical implementation of the gates. In Cirq, this means the exponent of a controlled gate must be an integer. The next cell shows two examples of valid controlled gates."
291   ]
292  },
293  {
294   "cell_type": "code",
295   "execution_count": 8,
296   "metadata": {
297    "id": "a2c2c1a49432"
298   },
299   "outputs": [],
300   "source": [
301    "\"\"\"Examples of valid multi-qubit gates.\"\"\"\n",
302    "# Controlled gates with integer exponents are valid.\n",
303    "neutral_atom_device.validate_gate(cirq.CNOT)\n",
304    "\n",
305    "# Controlled NOT gates with two controls are valid.\n",
306    "neutral_atom_device.validate_gate(cirq.TOFFOLI)"
307   ]
308  },
309  {
310   "cell_type": "markdown",
311   "metadata": {
312    "id": "62c881b20d63"
313   },
314   "source": [
315    "Any controlled gate with non-integer exponent is invalid."
316   ]
317  },
318  {
319   "cell_type": "code",
320   "execution_count": 9,
321   "metadata": {
322    "id": "ChT4QK7TsabR"
323   },
324   "outputs": [
325    {
326     "name": "stdout",
327     "output_type": "stream",
328     "text": [
329      "As expected, CNOT**1.5 is invalid! controlled gates must have integer exponents\n"
330     ]
331    }
332   ],
333   "source": [
334    "\"\"\"Example of an invalid controlled gate.\"\"\"\n",
335    "invalid_gate = cirq.CNOT ** 1.5\n",
336    "\n",
337    "try:\n",
338    "    neutral_atom_device.validate_gate(invalid_gate)\n",
339    "except ValueError as e:\n",
340    "    print(f\"As expected, {invalid_gate} is invalid!\", e)"
341   ]
342  },
343  {
344   "cell_type": "markdown",
345   "metadata": {
346    "id": "533125e85e19"
347   },
348   "source": [
349    "Multiple controls are allowed as long as every pair of atoms (qubits) acted on by the controlled gate are close enough to each other. We can see this by using the `validate_operation` (or `validate_circuit`) method, as follows."
350   ]
351  },
352  {
353   "cell_type": "code",
354   "execution_count": 10,
355   "metadata": {
356    "id": "59ce97fd61ec"
357   },
358   "outputs": [
359    {
360     "name": "stdout",
361     "output_type": "stream",
362     "text": [
363      "As expected, TOFFOLI((0, 0), (1, 0), (0, 2)) is invalid! Qubits cirq.GridQubit(1, 0), cirq.GridQubit(0, 2) are too far away\n"
364     ]
365    }
366   ],
367   "source": [
368    "\"\"\"Examples of valid and invalid multi-controlled gates.\"\"\"\n",
369    "# This TOFFOLI is valid because all qubits involved are close enough to each other.\n",
370    "valid_toffoli = cirq.TOFFOLI.on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1), cirq.GridQubit(0, 2))\n",
371    "neutral_atom_device.validate_operation(valid_toffoli)\n",
372    "\n",
373    "# This TOFFOLI is invalid because all qubits involved are not close enough to each other.\n",
374    "invalid_toffoli = cirq.TOFFOLI.on(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0), cirq.GridQubit(0, 2))\n",
375    "\n",
376    "try:\n",
377    "    neutral_atom_device.validate_operation(invalid_toffoli)\n",
378    "except ValueError as e:\n",
379    "    print(f\"As expected, {invalid_toffoli} is invalid!\", e)"
380   ]
381  },
382  {
383   "cell_type": "markdown",
384   "metadata": {
385    "id": "4ac323a867ad"
386   },
387   "source": [
388    "`NeutralAtomDevice`s do not currently support gates with more than two controls although these are in principle allowed by the physical realizations."
389   ]
390  },
391  {
392   "cell_type": "code",
393   "execution_count": 11,
394   "metadata": {
395    "id": "30d77c374b01"
396   },
397   "outputs": [
398    {
399     "name": "stdout",
400     "output_type": "stream",
401     "text": [
402      "As expected, CTOFFOLI is invalid! Unsupported gate: cirq.ControlledGate(sub_gate=cirq.TOFFOLI)\n"
403     ]
404    }
405   ],
406   "source": [
407    "\"\"\"Any gate with more than two controls is invalid.\"\"\"\n",
408    "invalid_gate = cirq.ControlledGate(cirq.TOFFOLI)\n",
409    "\n",
410    "try:\n",
411    "    neutral_atom_device.validate_gate(invalid_gate)\n",
412    "except ValueError as e:\n",
413    "    print(f\"As expected, {invalid_gate} is invalid!\", e)"
414   ]
415  },
416  {
417   "cell_type": "markdown",
418   "metadata": {
419    "id": "88d8a175bcdd"
420   },
421   "source": [
422    "Finally, we note that the duration of any operation can be determined via the `duration_of` method."
423   ]
424  },
425  {
426   "cell_type": "code",
427   "execution_count": 12,
428   "metadata": {
429    "id": "a8f7e867f019"
430   },
431   "outputs": [
432    {
433     "data": {
434      "text/plain": [
435       "cirq.Duration(micros=100)"
436      ]
437     },
438     "execution_count": 12,
439     "metadata": {},
440     "output_type": "execute_result"
441    }
442   ],
443   "source": [
444    "\"\"\"Example of getting the duration of a valid operation.\"\"\"\n",
445    "neutral_atom_device.duration_of(valid_toffoli)"
446   ]
447  },
448  {
449   "cell_type": "markdown",
450   "metadata": {
451    "id": "7f94b0d4ccdf"
452   },
453   "source": [
454    "### Moment and circuit rules"
455   ]
456  },
457  {
458   "cell_type": "markdown",
459   "metadata": {
460    "id": "Ug-2oBPPtZzU"
461   },
462   "source": [
463    "In addition to consisting of valid operations as discussed above, valid moments on a `NeutralAtomDevice` must satisfy the following criteria:\n",
464    "\n",
465    "1. Only `max_parallel_c` gates of the same category may be performed in the same moment.\n",
466    "2. All instances of gates in the same category in the same moment must be identical.\n",
467    "3. Controlled gates cannot be applied in parallel with other gate types.\n",
468    "  - Physically, this is because controlled gates make use of all types of light used to implement gates.\n",
469    "4. Qubits acted on by different controlled gates in parallel must be farther apart than the `control_radius`.\n",
470    "  - Physically, this is so that the entanglement mechanism doesn't cause the gates to interfere with one another.\n",
471    "5. All measurements must be terminal.\n",
472    "\n",
473    "Moments can be validated with the `validate_moment` method. Some examples are given below."
474   ]
475  },
476  {
477   "cell_type": "code",
478   "execution_count": 13,
479   "metadata": {
480    "id": "Nr-rfUgOtDxE"
481   },
482   "outputs": [
483    {
484     "name": "stdout",
485     "output_type": "stream",
486     "text": [
487      "Example of a valid moment with single-qubit gates:\n",
488      "\n",
489      "(0, 0): ───Z───\n",
490      "\n",
491      "(0, 1): ───Z───\n",
492      "\n",
493      "(0, 2): ───Z───\n",
494      "\n",
495      "(1, 0): ───X───\n",
496      "\n",
497      "(1, 1): ───X───\n",
498      "\n",
499      "(1, 2): ───X───\n"
500     ]
501    }
502   ],
503   "source": [
504    "\"\"\"Example of a valid moment with single qubit gates.\"\"\"\n",
505    "qubits = sorted(neutral_atom_device.qubits)\n",
506    "\n",
507    "# Get a valid moment.\n",
508    "valid_moment = cirq.Moment(cirq.Z.on_each(qubits[:3]) + cirq.X.on_each(qubits[3:6]))\n",
509    "\n",
510    "# Display it.\n",
511    "print(\"Example of a valid moment with single-qubit gates:\", cirq.Circuit(valid_moment), sep=\"\\n\\n\")\n",
512    "\n",
513    "# Verify it is valid.\n",
514    "neutral_atom_device.validate_moment(valid_moment)"
515   ]
516  },
517  {
518   "cell_type": "markdown",
519   "metadata": {
520    "id": "1393b2a6234e"
521   },
522   "source": [
523    "Recall that we defined `max_parallel_z = 3` in our device. Thus, if we tried to do 4 $Z$ gates in the same moment, this would be invalid."
524   ]
525  },
526  {
527   "cell_type": "code",
528   "execution_count": 14,
529   "metadata": {
530    "id": "4b9f3915276b"
531   },
532   "outputs": [
533    {
534     "name": "stdout",
535     "output_type": "stream",
536     "text": [
537      "Example of an invalid moment with single-qubit gates:\n",
538      "\n",
539      "(0, 0): ───Z───\n",
540      "\n",
541      "(0, 1): ───Z───\n",
542      "\n",
543      "(0, 2): ───Z───\n",
544      "\n",
545      "(1, 0): ───Z───\n"
546     ]
547    }
548   ],
549   "source": [
550    "\"\"\"Example of an invalid moment with single qubit gates.\"\"\"\n",
551    "# Get an invalid moment.\n",
552    "invalid_moment = cirq.Moment(cirq.Z.on_each(qubits[:4]))\n",
553    "\n",
554    "# Display it.\n",
555    "print(\"Example of an invalid moment with single-qubit gates:\", cirq.Circuit(invalid_moment), sep=\"\\n\\n\")\n",
556    "\n",
557    "# Uncommenting raises ValueError: Too many simultaneous Z gates.\n",
558    "# neutral_atom_device.validate_moment(invalid_moment)"
559   ]
560  },
561  {
562   "cell_type": "markdown",
563   "metadata": {
564    "id": "5e47c71c4570"
565   },
566   "source": [
567    "This is also true for 4 $XY$ gates since we set `max_parallel_xy = 3`. However, there is an exception for $XY$ gates acting on *every* qubit, as illustrated below."
568   ]
569  },
570  {
571   "cell_type": "code",
572   "execution_count": 15,
573   "metadata": {
574    "id": "fa06394e7497"
575   },
576   "outputs": [],
577   "source": [
578    "\"\"\"An XY gate can be performed on every qubit in the device simultaneously.\n",
579    "\n",
580    "If the XY gate does not act on every qubit, it must act on <= max_parallel_xy qubits.\n",
581    "\"\"\"\n",
582    "valid_moment = cirq.Moment(cirq.X.on_each(qubits))\n",
583    "neutral_atom_device.validate_moment(valid_moment)"
584   ]
585  },
586  {
587   "cell_type": "markdown",
588   "metadata": {
589    "id": "89c05574161d"
590   },
591   "source": [
592    "Although both $Z$ and $Z^{1.5}$ are valid gates, they cannot be performed simultaneously because all gates \"of the same type\" must be identical in the same moment."
593   ]
594  },
595  {
596   "cell_type": "code",
597   "execution_count": 16,
598   "metadata": {
599    "id": "61112be5f754"
600   },
601   "outputs": [
602    {
603     "name": "stdout",
604     "output_type": "stream",
605     "text": [
606      "Example of an invalid moment with single-qubit gates:\n",
607      "\n",
608      "(0, 0): ───Z──────\n",
609      "\n",
610      "(0, 1): ───S^-1───\n"
611     ]
612    }
613   ],
614   "source": [
615    "\"\"\"Example of an invalid moment with single qubit gates.\"\"\"\n",
616    "# Get an invalid moment.\n",
617    "invalid_moment = cirq.Moment(cirq.Z(qubits[0]), cirq.Z(qubits[1]) ** 1.5)\n",
618    "\n",
619    "# Display it.\n",
620    "print(\"Example of an invalid moment with single-qubit gates:\", cirq.Circuit(invalid_moment), sep=\"\\n\\n\")\n",
621    "\n",
622    "# Uncommenting raises ValueError: Non-identical simultaneous Z gates.\n",
623    "# neutral_atom_device.validate_moment(invalid_moment)"
624   ]
625  },
626  {
627   "cell_type": "markdown",
628   "metadata": {
629    "id": "55c867d62797"
630   },
631   "source": [
632    "### Appending operations"
633   ]
634  },
635  {
636   "cell_type": "markdown",
637   "metadata": {
638    "id": "l6jE7NnKtjQU"
639   },
640   "source": [
641    "A common pattern for constructing circuits is to append a sequence of operations instead of explicitly creating moments. For a circuit defined on a `NeutralAtomDevice`, Cirq will respect the above rules for creating valid moments.\n",
642    "\n",
643    "For example, if we append $Z$ and $Z^{1.5}$ from the previous example, Cirq will place them into two moments as shown below."
644   ]
645  },
646  {
647   "cell_type": "code",
648   "execution_count": 17,
649   "metadata": {
650    "id": "b93b0f484852"
651   },
652   "outputs": [
653    {
654     "name": "stdout",
655     "output_type": "stream",
656     "text": [
657      "(0, 0): ───Z──────────\n",
658      "\n",
659      "(0, 1): ───────S^-1───\n"
660     ]
661    }
662   ],
663   "source": [
664    "\"\"\"Cirq satisfies device restrictions automatically when appending operations.\"\"\"\n",
665    "# Create a circuit for a NeutralAtomDevice.\n",
666    "circuit = cirq.Circuit(device=neutral_atom_device)\n",
667    "\n",
668    "# Append two gates which cannot be in the same moment.\n",
669    "circuit.append([cirq.Z(qubits[0]), cirq.Z(qubits[1]) ** 1.5])\n",
670    "\n",
671    "# Display the circuit.\n",
672    "print(circuit)"
673   ]
674  },
675  {
676   "cell_type": "markdown",
677   "metadata": {
678    "id": "2c4661338f4b"
679   },
680   "source": [
681    "This is true for all device rules. As another example, we can see how Cirq separates controlled gates from other gate types (the third rule above)."
682   ]
683  },
684  {
685   "cell_type": "code",
686   "execution_count": 18,
687   "metadata": {
688    "id": "4e5b9a563ec9"
689   },
690   "outputs": [
691    {
692     "name": "stdout",
693     "output_type": "stream",
694     "text": [
695      "(0, 0): ───Z───────\n",
696      "\n",
697      "(0, 1): ───────@───\n",
698      "               │\n",
699      "(0, 2): ───────X───\n"
700     ]
701    }
702   ],
703   "source": [
704    "\"\"\"Cirq satisfies device restrictions automatically when appending operations.\"\"\"\n",
705    "# Create a circuit for a NeutralAtomDevice.\n",
706    "circuit = cirq.Circuit(device=neutral_atom_device)\n",
707    "\n",
708    "# Append two gates which cannot be in the same moment.\n",
709    "circuit.append([cirq.Z(qubits[0]), cirq.CNOT(*qubits[1: 3])])\n",
710    "\n",
711    "# Display the circuit.\n",
712    "print(circuit)"
713   ]
714  },
715  {
716   "cell_type": "markdown",
717   "metadata": {
718    "id": "58bc53b6288a"
719   },
720   "source": [
721    "Without any device restrictions, the `Z` and `CNOT` operations could be in the same moment, but because the circuit is defined on a `NeutralAtomDevice`, the `CNOT` is placed into a new moment."
722   ]
723  },
724  {
725   "cell_type": "markdown",
726   "metadata": {
727    "id": "35e2090ccb60"
728   },
729   "source": [
730    "### Exercise: Multiple controlled gates in the same moment"
731   ]
732  },
733  {
734   "cell_type": "markdown",
735   "metadata": {
736    "id": "4b78afc8053d"
737   },
738   "source": [
739    "Construct a `NeutralAtomDevice` which is capable of implementing two `CNOT`s in the same moment. Verify that these operations can indeed be performed in parallel by calling the `validate_moment` method or showing that Cirq inserts the operations into the same moment."
740   ]
741  },
742  {
743   "cell_type": "code",
744   "execution_count": 19,
745   "metadata": {
746    "id": "f8b1f29f22c9"
747   },
748   "outputs": [],
749   "source": [
750    "# Your code here!"
751   ]
752  },
753  {
754   "cell_type": "markdown",
755   "metadata": {
756    "id": "86a3141cdfb2"
757   },
758   "source": [
759    "#### Solution"
760   ]
761  },
762  {
763   "cell_type": "code",
764   "execution_count": 20,
765   "metadata": {
766    "id": "3231b8c17951"
767   },
768   "outputs": [
769    {
770     "name": "stdout",
771     "output_type": "stream",
772     "text": [
773      "Device:\n",
774      "(0, 0)───(0, 1)───(0, 2)\n",
775      "│        │        │\n",
776      "│        │        │\n",
777      "(1, 0)───(1, 1)───(1, 2)\n",
778      "\n",
779      "Circuit:\n",
780      "           ┌──┐\n",
781      "(0, 0): ────@─────@───────\n",
782      "            │     │\n",
783      "(0, 1): ────┼─────┼───@───\n",
784      "            │     │   │\n",
785      "(0, 2): ────┼@────┼───┼───\n",
786      "            ││    │   │\n",
787      "(1, 0): ────X┼────X───┼───\n",
788      "             │        │\n",
789      "(1, 1): ─────┼────────X───\n",
790      "             │\n",
791      "(1, 2): ─────X────────────\n",
792      "           └──┘\n"
793     ]
794    }
795   ],
796   "source": [
797    "\"\"\"Example solution for creating a device which allows two CNOTs in the same moment.\"\"\"\n",
798    "# Create a NeutralAtomDevice.\n",
799    "device = cirq.NeutralAtomDevice(\n",
800    "    qubits=cirq.GridQubit.rect(2, 3),\n",
801    "    measurement_duration=5 * cirq.Duration(nanos=10**6),\n",
802    "    gate_duration=100 * cirq.Duration(nanos=10**3),\n",
803    "    max_parallel_z=4,\n",
804    "    max_parallel_xy=4,\n",
805    "    max_parallel_c=4,\n",
806    "    control_radius=1\n",
807    ")\n",
808    "print(\"Device:\")\n",
809    "print(device)\n",
810    "\n",
811    "# Create a circuit for a NeutralAtomDevice.\n",
812    "circuit = cirq.Circuit(device=device)\n",
813    "\n",
814    "# Append two CNOTs that can be in the same moment.\n",
815    "circuit.append(\n",
816    "    [cirq.CNOT(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)), \n",
817    "     cirq.CNOT(cirq.GridQubit(0, 2), cirq.GridQubit(1, 2))]\n",
818    ")\n",
819    "\n",
820    "# Append two CNOTs that cannot be in the same moment.\n",
821    "circuit.append(\n",
822    "    [cirq.CNOT(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)), \n",
823    "     cirq.CNOT(cirq.GridQubit(0, 1), cirq.GridQubit(1, 1))]\n",
824    ")\n",
825    "\n",
826    "# Display the circuit.\n",
827    "print(\"\\nCircuit:\")\n",
828    "print(circuit)"
829   ]
830  },
831  {
832   "cell_type": "markdown",
833   "metadata": {
834    "id": "d29db15ac256"
835   },
836   "source": [
837    "Note that the square brackets above/below the circuit indicate the first two `CNOT`s are in the same moment."
838   ]
839  },
840  {
841   "cell_type": "markdown",
842   "metadata": {
843    "id": "49fc5658b4a5"
844   },
845   "source": [
846    "## Decomposing operations and circuits"
847   ]
848  },
849  {
850   "cell_type": "markdown",
851   "metadata": {
852    "id": "2f513654524d"
853   },
854   "source": [
855    "Invalid operations can be decomposed into valid operations via the `decompose_operation` method. For example, we saw above that `cirq.H` was an invalid gate for a `NeutralAtomDevice`. This can be decomposed into valid operations as follows."
856   ]
857  },
858  {
859   "cell_type": "code",
860   "execution_count": 21,
861   "metadata": {
862    "id": "6fe3d596c504"
863   },
864   "outputs": [
865    {
866     "name": "stdout",
867     "output_type": "stream",
868     "text": [
869      "Circuit for H on a NeutralAtomDevice:\n",
870      "\n"
871     ]
872    },
873    {
874     "data": {
875      "text/html": [
876       "<pre style=\"overflow: auto; white-space: pre;\">(0, 0): ───PhX(-0.5)^0.5───Z───</pre>"
877      ],
878      "text/plain": [
879       "(0, 0): ───PhX(-0.5)^0.5───Z───"
880      ]
881     },
882     "execution_count": 21,
883     "metadata": {},
884     "output_type": "execute_result"
885    }
886   ],
887   "source": [
888    "\"\"\"Example of decomposing an operation.\"\"\"\n",
889    "# Decompose a Hadamard operation.\n",
890    "ops = neutral_atom_device.decompose_operation(cirq.H.on(qubits[0]))\n",
891    "\n",
892    "# Display the circuit.\n",
893    "print(\"Circuit for H on a NeutralAtomDevice:\\n\")\n",
894    "cirq.Circuit(ops, device=neutral_atom_device)"
895   ]
896  },
897  {
898   "cell_type": "markdown",
899   "metadata": {
900    "id": "eee4f54bbc65"
901   },
902   "source": [
903    "Two-qubit and other operations can be decomposed in an analogous manner, for example the `FSimGate` below."
904   ]
905  },
906  {
907   "cell_type": "code",
908   "execution_count": 22,
909   "metadata": {
910    "id": "9fb75abc3504"
911   },
912   "outputs": [
913    {
914     "name": "stdout",
915     "output_type": "stream",
916     "text": [
917      "Circuit for FSim on a NeutralAtomDevice:\n",
918      "\n"
919     ]
920    },
921    {
922     "data": {
923      "text/html": [
924       "<pre style=\"overflow: auto; white-space: pre;\">(0, 0): ───PhX(-0.698)^0.5───@───PhX(-0.698)^0.5──────────────────────@───PhX(0.85)^0.5──────────────────────@───PhX(0.85)^0.5───Z^-0.095───\n",
925       "                             │                                        │                                      │\n",
926       "(1, 0): ─────────────────────@─────────────────────PhX(0.802)^0.968───@───────────────────PhX(0.802)^0.968───@───Z^0.452────────────────────</pre>"
927      ],
928      "text/plain": [
929       "(0, 0): ───PhX(-0.698)^0.5───@───PhX(-0.698)^0.5──────────────────────@───PhX(0.85)^0.5──────────────────────@───PhX(0.85)^0.5───Z^-0.095───\n",
930       "                             │                                        │                                      │\n",
931       "(1, 0): ─────────────────────@─────────────────────PhX(0.802)^0.968───@───────────────────PhX(0.802)^0.968───@───Z^0.452────────────────────"
932      ]
933     },
934     "execution_count": 22,
935     "metadata": {},
936     "output_type": "execute_result"
937    }
938   ],
939   "source": [
940    "\"\"\"Another example of decomposing an operation.\"\"\"\n",
941    "# Decompose an FSimGate operation.\n",
942    "ops = neutral_atom_device.decompose_operation(\n",
943    "    cirq.FSimGate(theta=0.1, phi=0.3).on(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0))\n",
944    ")\n",
945    "\n",
946    "# Display the circuit.\n",
947    "print(\"Circuit for FSim on a NeutralAtomDevice:\\n\")\n",
948    "cirq.Circuit(ops, device=neutral_atom_device)"
949   ]
950  },
951  {
952   "cell_type": "markdown",
953   "metadata": {
954    "id": "391928d22b34"
955   },
956   "source": [
957    "> *Note*: As with any proposed architecture for quantum computing, several research groups around the world are working towards a device based on neutral atom qubits. Each research group has a different approach, such as using different atomic species or working with a different number of dimensions of atomic qubit arrays. As such, the `NeutralAtomDevice` class will not accurately reflect all such devices. The class is based on the two dimensional Cesium array at the University of Wisconsin-Madison in the research group of Mark Saffman. Development of this device is being pursued as part of a strategic partnership between the University of Wisconsin-Madison and ColdQuanta."
958   ]
959  }
960 ],
961 "metadata": {
962  "colab": {
963   "collapsed_sections": [],
964   "name": "neutral_atom.ipynb",
965   "toc_visible": true
966  },
967  "kernelspec": {
968   "display_name": "Python 3",
969   "name": "python3"
970  }
971 },
972 "nbformat": 4,
973 "nbformat_minor": 0
974}
975