Skip to content

Commit 8becb9c

Browse files
augustehirthrht
authored andcommitted
Classical control phrasing (quantumlib#5514)
Some phrasing changes to the new classical control section. Ran a formatter.
1 parent 749d871 commit 8becb9c

File tree

1 file changed

+90
-32
lines changed

1 file changed

+90
-32
lines changed

docs/classical_control.ipynb

+90-32
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"outputs": [],
1010
"source": [
11-
"#@title Copyright 2022 The Cirq Developers\n",
11+
"# @title Copyright 2022 The Cirq Developers\n",
1212
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
1313
"# you may not use this file except in compliance with the License.\n",
1414
"# You may obtain a copy of the License at\n",
@@ -39,16 +39,16 @@
3939
"source": [
4040
"<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
4141
" <td>\n",
42-
" <a target=\"_blank\" href=\"https://quantumai.google/cirq/circuits\"><img src=\"https://quantumai.google/site-assets/images/buttons/quantumai_logo_1x.png\" />View on QuantumAI</a>\n",
42+
" <a target=\"_blank\" href=\"https://quantumai.google/cirq/classical_control\"><img src=\"https://quantumai.google/site-assets/images/buttons/quantumai_logo_1x.png\" />View on QuantumAI</a>\n",
4343
" </td>\n",
4444
" <td>\n",
45-
" <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/master/docs/circuits.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/colab_logo_1x.png\" />Run in Google Colab</a>\n",
45+
" <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/master/docs/classical_control.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/colab_logo_1x.png\" />Run in Google Colab</a>\n",
4646
" </td>\n",
4747
" <td>\n",
48-
" <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/master/docs/circuits.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/github_logo_1x.png\" />View source on GitHub</a>\n",
48+
" <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/master/docs/classical_control.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/github_logo_1x.png\" />View source on GitHub</a>\n",
4949
" </td>\n",
5050
" <td>\n",
51-
" <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/circuits.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/download_icon_1x.png\" />Download notebook</a>\n",
51+
" <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/classical_control.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/download_icon_1x.png\" />Download notebook</a>\n",
5252
" </td>\n",
5353
"</table>"
5454
]
@@ -67,6 +67,7 @@
6767
" print(\"installing cirq...\")\n",
6868
" !pip install --quiet cirq\n",
6969
" import cirq\n",
70+
"\n",
7071
" print(\"installed cirq.\")"
7172
]
7273
},
@@ -89,7 +90,7 @@
8990
"source": [
9091
"## Basic conditions\n",
9192
"\n",
92-
"In the example below, `X` will only be applied to `q1` if the previous measurement \"a\" returns a 1. More generally, providing some string `\"cond\"` to `with_classical_controls` creates a `ClassicallyControlledOperation` with a `KeyCondition` whose key is `\"cond\"`. A `KeyCondition` will only trigger and apply the operation it controls if a preceding measurement with the same key measured one or more qubits in the $|1\\rangle$ state."
93+
"In the example below, `H` will only be applied to `q1` if the previous measurement \"a\" returns a 1. More generally, providing some string `\"cond\"` to `with_classical_controls` creates a `cirq.ClassicallyControlledOperation` with a `cirq.KeyCondition` whose key is `\"cond\"`. A `KeyCondition` will only trigger, and apply the operation it controls, if a preceding measurement with the same key measured **one or more** qubits in the $|1\\rangle$ state."
9394
]
9495
},
9596
{
@@ -100,13 +101,15 @@
100101
},
101102
"outputs": [],
102103
"source": [
103-
"q0, q1, q2 = cirq.LineQubit.range(3)\n",
104+
"q0, q1 = cirq.LineQubit.range(2)\n",
104105
"circuit = cirq.Circuit(\n",
105106
" cirq.H(q0),\n",
106107
" cirq.measure(q0, key='a'),\n",
107-
" cirq.X(q1).with_classical_controls('a'),\n",
108+
" cirq.H(q1).with_classical_controls('a'),\n",
109+
" cirq.measure(q1, key='b'),\n",
108110
")\n",
109-
"print(circuit)"
111+
"print(circuit)\n",
112+
"print(cirq.Simulator().run(circuit, repetitions=1000).histogram(key='b'))"
110113
]
111114
},
112115
{
@@ -115,6 +118,8 @@
115118
"id": "4e416431b695"
116119
},
117120
"source": [
121+
"The results from running the circuit on the simulator match expectation. `H` applied to qubit `q0` means that qubit will be $|1\\rangle$ half of the time on average. When `H` is then applied to qubit `q1`, (half of the time), `q1` will measure $|1\\rangle$ a quarter of the time and $|0\\rangle$ three-quarters of the time.\n",
122+
"\n",
118123
"Using just these conditions, we can construct the [quantum teleportation](/cirq/tutorials/educators/textbook_algorithms#quantum_teleportation) circuit:"
119124
]
120125
},
@@ -130,13 +135,19 @@
130135
"alice = cirq.NamedQubit('alice')\n",
131136
"bob = cirq.NamedQubit('bob')\n",
132137
"message = cirq.NamedQubit('_message')\n",
133-
"circuit = cirq.Circuit(\n",
134-
" # Create Bell state to be shared between Alice and Bob.\n",
135-
" cirq.H(alice),\n",
136-
" cirq.CNOT(alice, bob),\n",
138+
"\n",
139+
"message_circuit = cirq.Circuit(\n",
137140
" # Create the message.\n",
138141
" cirq.X(message) ** 0.371,\n",
139142
" cirq.Y(message) ** 0.882,\n",
143+
")\n",
144+
"\n",
145+
"teleport_circuit = cirq.Circuit(\n",
146+
" # Create Bell state to be shared between Alice and Bob.\n",
147+
" cirq.H(alice),\n",
148+
" cirq.CNOT(alice, bob),\n",
149+
" # Prepare message circuit\n",
150+
" message_circuit,\n",
140151
" # Bell measurement of the message and Alice's entangled qubit.\n",
141152
" cirq.CNOT(message, alice),\n",
142153
" cirq.H(message),\n",
@@ -147,7 +158,28 @@
147158
" cirq.X(bob).with_classical_controls('A'),\n",
148159
" cirq.Z(bob).with_classical_controls('M'),\n",
149160
")\n",
150-
"print(circuit)"
161+
"print(circuit)\n",
162+
"\n",
163+
"# Simulate the message and teleport circuits for Bloch vectors to compare\n",
164+
"# the state of the teleported qubit before and after teleportation.\n",
165+
"sim = cirq.Simulator()\n",
166+
"message_bloch_vector = cirq.bloch_vector_from_state_vector(\n",
167+
" sim.simulate(message_circuit).final_state_vector, index=0\n",
168+
")\n",
169+
"teleport_bloch_vector = cirq.bloch_vector_from_state_vector(\n",
170+
" sim.simulate(teleport_circuit).final_state_vector, index=2\n",
171+
")\n",
172+
"print(f\"Message Qubit State: {message_bloch_vector}\")\n",
173+
"print(f\"Teleported Bob's Qubit state: {teleport_bloch_vector}\")"
174+
]
175+
},
176+
{
177+
"cell_type": "markdown",
178+
"metadata": {
179+
"id": "ca879df5fb2d"
180+
},
181+
"source": [
182+
"This example separately simulated the message qubit after its construction, and Bob's qubit after teleportation of the message. The fact that the Bloch vectors of each respective qubit are the same indicate that the circuit successfully teleported the message qubit's state onto Bob's qubit. "
151183
]
152184
},
153185
{
@@ -160,7 +192,7 @@
160192
"\n",
161193
"Cirq also supports more complex control conditions: providing some `sympy` expression `\"expr\"` to `with_classical_controls` creates a `ClassicallyControlledOperation` with a `SympyCondition`. That condition will only trigger if `\"expr\"` evaluates to a \"truthy\" value (`bool(expr) == True`), and uses measurement results to resolve any variables in the expression.\n",
162194
"\n",
163-
"In this example, `X` will only be applied to `q2` if `a == b`; in other words, $|q_0q_1\\rangle$ must be either $|00\\rangle$ or $|11\\rangle$."
195+
"In this example, `X` will only be applied to `q2` if `a == b`; in other words, $|q_0q_1\\rangle$ must be either $|00\\rangle$ or $|11\\rangle$. This is verifiable with the simulated result data, where the `c` measurement key for qubit `q2` is always `1` when `a` and `b` are `00` or `11`, and `0` otherwise."
164196
]
165197
},
166198
{
@@ -173,15 +205,19 @@
173205
"source": [
174206
"import sympy\n",
175207
"\n",
176-
"a, b, c = sympy.symbols('a b c')\n",
208+
"q0, q1, q2 = cirq.LineQubit.range(3)\n",
209+
"a, b = sympy.symbols('a b')\n",
177210
"sympy_cond = sympy.Eq(a, b)\n",
178211
"circuit = cirq.Circuit(\n",
179212
" cirq.H.on_each(q0, q1),\n",
180213
" cirq.measure(q0, key='a'),\n",
181214
" cirq.measure(q1, key='b'),\n",
182-
" cirq.X(q2).with_classical_controls(sympy_cond)\n",
215+
" cirq.X(q2).with_classical_controls(sympy_cond),\n",
216+
" cirq.measure(q2, key='c'),\n",
183217
")\n",
184-
"print(circuit)"
218+
"print(circuit)\n",
219+
"results = cirq.Simulator(seed=2).run(circuit, repetitions=8)\n",
220+
"print(results.data)"
185221
]
186222
},
187223
{
@@ -203,15 +239,34 @@
203239
},
204240
"outputs": [],
205241
"source": [
242+
"q0, q1, q2, q3, q4 = cirq.LineQubit.range(5)\n",
243+
"a = sympy.symbols('a')\n",
206244
"sympy_cond = sympy.Eq(a, 0)\n",
207245
"circuit = cirq.Circuit(\n",
208246
" cirq.H.on_each(q0, q1, q2),\n",
209247
" cirq.measure(q0, q1, key='a'),\n",
210248
" cirq.measure(q2, key='b'),\n",
211-
" cirq.X(q0).with_classical_controls('b', sympy_cond),\n",
212-
" cirq.CZ(q1, q2).with_classical_controls('b').with_classical_controls(sympy_cond),\n",
249+
" cirq.X(q3).with_classical_controls('b', sympy_cond),\n",
250+
" cirq.X(q4).with_classical_controls('b').with_classical_controls(sympy_cond),\n",
251+
" cirq.measure(q3, key='c'),\n",
252+
" cirq.measure(q4, key='d'),\n",
213253
")\n",
214-
"print(circuit)"
254+
"print(circuit)\n",
255+
"results = cirq.Simulator(seed=1).run(circuit, repetitions=8)\n",
256+
"print(results.data)"
257+
]
258+
},
259+
{
260+
"cell_type": "markdown",
261+
"metadata": {
262+
"id": "7d9e124484f3"
263+
},
264+
"source": [
265+
"First, remember that the value of a measurement key for multiple qubits will be an integer representative of the bit string of those qubits' measurements. You can see this in the data for `a`, the measurement key for both `q0` and `q1`, which has values in the range `[0, 3]`. The sympy condition `Eq(a, 0)` will then only trigger when both of those qubits individually measure `0`. \n",
266+
"\n",
267+
"This means that `X(q3).with_classical_controls('b', sympy_cond)` only triggers when `b`'s qubit `q2` measures `1` and `a = 0` is true (`q0` and `q1` measure `0`). This is consistent with the simulated results, for both `c` (`q3`'s key) and `d` (`q4`'s key).\n",
268+
"\n",
269+
"Finally, the fact that `c` and `d` are always identical serves as a reminder that chaining multiple calls of `with_classical_controls()` together is equivalent to calling it once with multiple arguments."
215270
]
216271
},
217272
{
@@ -222,7 +277,7 @@
222277
"source": [
223278
"## Variable scope\n",
224279
"\n",
225-
"When used with `CircuitOperation`, classically controlled operations will be resolved using local repetition IDs, if any. This is the only way to create a non-global variable scope within a circuit. A simple example of this is shown below, where the controls inside and outside a subcircuit rely on measurements in their respective scopes:"
280+
"When used with `cirq.CircuitOperation`, classically controlled operations will be resolved using local repetition IDs, if any. This is the only way to create a non-global variable scope within a circuit. A simple example of this is shown below, where the controls inside and outside a subcircuit rely on measurements in their respective scopes:"
226281
]
227282
},
228283
{
@@ -233,15 +288,17 @@
233288
},
234289
"outputs": [],
235290
"source": [
236-
"subcircuit = cirq.FrozenCircuit(\n",
237-
" cirq.measure(q0, key='a'), cirq.X(q0).with_classical_controls('a')\n",
238-
")\n",
291+
"q0 = cirq.LineQubit(0)\n",
292+
"subcircuit = cirq.FrozenCircuit(cirq.measure(q0, key='a'), cirq.X(q0).with_classical_controls('a'))\n",
239293
"circuit = cirq.Circuit(\n",
240294
" cirq.measure(q0, key='a'),\n",
241295
" cirq.CircuitOperation(subcircuit, repetitions=2),\n",
242-
" cirq.X(q0).with_classical_controls('a')\n",
296+
" cirq.X(q0).with_classical_controls('a'),\n",
243297
")\n",
244-
"print(circuit)"
298+
"print(\"Original Circuit\")\n",
299+
"print(circuit)\n",
300+
"print(\"Circuit with nested circuit unrolled.\")\n",
301+
"print(cirq.CircuitOperation(cirq.FrozenCircuit(circuit)).mapped_circuit(deep=True))"
245302
]
246303
},
247304
{
@@ -250,6 +307,8 @@
250307
"id": "b0807e8edb7f"
251308
},
252309
"source": [
310+
"The measurement key `a` is present both in the outer circuit and the `FrozenCircuit` nested within it, but these two keys are different due to their different scopes. After unrolling the inner circuit twice, these inner `a`s get prefixed by the repetition number and becomes new, separate measurement keys, `0:a` and `1:a`, that don't interact with each other or the original `a`. \n",
311+
"\n",
253312
"More complex scoping behavior is described in the [classically controlled operation tests](https://github.com/quantumlib/Cirq/blob/master/cirq-core/cirq/ops/classically_controlled_operation_test.py)."
254313
]
255314
},
@@ -261,7 +320,7 @@
261320
"source": [
262321
"## Using with transformers\n",
263322
"\n",
264-
"Cirq [transformers](transformers.ipynb) are aware of classical control and will avoid changes which move a control before its corresponding measurement. Additionally, for some simple cases the [`defer_measurements` transformer](https://github.com/daxfohl/Cirq/blob/e68ff85e9bb0c7373572cdc212c10f226cd40b0f/cirq-core/cirq/transformers/measurement_transformers.py#L58) can convert a classically-controlled circuit into a purely-quantum circuit:"
323+
"Cirq [transformers](transformers.ipynb) are aware of classical control and will avoid changes which move a control before its corresponding measurement. Additionally, for some simple cases the [`defer_measurements` transformer](https://github.com/quantumlib/Cirq/blob/6e0e164e8ac1c2f28a1f3389370fffb50a4d2a4f/cirq-core/cirq/transformers/measurement_transformers.py#L58) can convert a classically-controlled circuit into a purely-quantum circuit:"
265324
]
266325
},
267326
{
@@ -272,10 +331,9 @@
272331
},
273332
"outputs": [],
274333
"source": [
334+
"q0 = cirq.LineQubit(0)\n",
275335
"circuit = cirq.Circuit(\n",
276-
" cirq.measure(q0, key='a'),\n",
277-
" cirq.X(q1).with_classical_controls('a'),\n",
278-
" cirq.measure(q1, key='b'),\n",
336+
" cirq.measure(q0, key='a'), cirq.X(q1).with_classical_controls('a'), cirq.measure(q1, key='b')\n",
279337
")\n",
280338
"deferred = cirq.defer_measurements(circuit)\n",
281339
"print(\"Original circuit:\")\n",
@@ -292,7 +350,7 @@
292350
"source": [
293351
"## Compatibility\n",
294352
"\n",
295-
"The Cirq built-in simulators provide support for classical control, but caution should be exercised when exporting these circuits to other environments. `ClassicallyControlledOperation` is fundamentally different from other operations in that it requires access to the measurement results, and simulators or hardware that does not explicitly support this will not be able to run `ClassicallyControlledOperation`s."
353+
"The Cirq built-in simulators provide support for classical control, but caution should be exercised when exporting these circuits to other environments. `ClassicallyControlledOperation` is fundamentally different from other operations in that it requires access to the measurement results, and simulators or hardware that do not explicitly support this will not be able to run `ClassicallyControlledOperation`s."
296354
]
297355
}
298356
],

0 commit comments

Comments
 (0)