Skip to content

Commit

Permalink
Add CircuitOperation blurb (quantumlib#5399)
Browse files Browse the repository at this point in the history
Documents `CircuitOperation` usage on the "Circuits" page.
  • Loading branch information
95-martin-orion authored Jun 2, 2022
1 parent ac5aa50 commit dc19b85
Showing 1 changed file with 189 additions and 20 deletions.
209 changes: 189 additions & 20 deletions docs/circuits.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
},
"outputs": [],
"source": [
"qubits = [cirq.GridQubit(x, y) for x in range(3) for y in range(3)]\n",
"qubits = cirq.GridQubit.square(3)\n",
"\n",
"print(qubits[0])"
]
Expand Down Expand Up @@ -224,10 +224,9 @@
},
"outputs": [],
"source": [
"from cirq.ops import CZ, H\n",
"q0, q1, q2 = [cirq.GridQubit(i, 0) for i in range(3)]\n",
"circuit = cirq.Circuit()\n",
"circuit.append([CZ(q0, q1), H(q2)])\n",
"circuit.append([cirq.CZ(q0, q1), cirq.H(q2)])\n",
"\n",
"print(circuit)"
]
Expand All @@ -249,7 +248,7 @@
},
"outputs": [],
"source": [
"circuit.append([H(q0), CZ(q1, q2)])\n",
"circuit.append([cirq.H(q0), cirq.CZ(q1, q2)])\n",
"\n",
"print(circuit)"
]
Expand All @@ -272,7 +271,7 @@
"outputs": [],
"source": [
"circuit = cirq.Circuit()\n",
"circuit.append([CZ(q0, q1), H(q2), H(q0), CZ(q1, q2)])\n",
"circuit.append([cirq.CZ(q0, q1), cirq.H(q2), cirq.H(q0), cirq.CZ(q1, q2)])\n",
"\n",
"print(circuit)"
]
Expand Down Expand Up @@ -316,8 +315,8 @@
"from cirq.circuits import InsertStrategy\n",
"\n",
"circuit = cirq.Circuit()\n",
"circuit.append([CZ(q0, q1)])\n",
"circuit.append([H(q0), H(q2)], strategy=InsertStrategy.EARLIEST)\n",
"circuit.append([cirq.CZ(q0, q1)])\n",
"circuit.append([cirq.H(q0), cirq.H(q2)], strategy=InsertStrategy.EARLIEST)\n",
"\n",
"print(circuit)"
]
Expand All @@ -344,7 +343,9 @@
"outputs": [],
"source": [
"circuit = cirq.Circuit()\n",
"circuit.append([H(q0), H(q1), H(q2)], strategy=InsertStrategy.NEW)\n",
"circuit.append(\n",
" [cirq.H(q0), cirq.H(q1), cirq.H(q2)], strategy=InsertStrategy.NEW\n",
")\n",
"\n",
"print(circuit)"
]
Expand Down Expand Up @@ -385,9 +386,11 @@
"outputs": [],
"source": [
"circuit = cirq.Circuit()\n",
"circuit.append([CZ(q1, q2)])\n",
"circuit.append([CZ(q1, q2)])\n",
"circuit.append([H(q0), H(q1), H(q2)], strategy=InsertStrategy.INLINE)\n",
"circuit.append([cirq.CZ(q1, q2)])\n",
"circuit.append([cirq.CZ(q1, q2)])\n",
"circuit.append(\n",
" [cirq.H(q0), cirq.H(q1), cirq.H(q2)], strategy=InsertStrategy.INLINE\n",
")\n",
"\n",
"print(circuit)"
]
Expand Down Expand Up @@ -422,8 +425,10 @@
"outputs": [],
"source": [
"circuit = cirq.Circuit()\n",
"circuit.append([H(q0)])\n",
"circuit.append([CZ(q1,q2), H(q0)], strategy=InsertStrategy.NEW_THEN_INLINE)\n",
"circuit.append([cirq.H(q0)])\n",
"circuit.append(\n",
" [cirq.CZ(q1,q2), cirq.H(q0)], strategy=InsertStrategy.NEW_THEN_INLINE\n",
")\n",
"\n",
"print(circuit)"
]
Expand Down Expand Up @@ -468,10 +473,10 @@
"outputs": [],
"source": [
"def my_layer():\n",
" yield CZ(q0, q1)\n",
" yield [H(q) for q in (q0, q1, q2)]\n",
" yield [CZ(q1, q2)]\n",
" yield [H(q0), [CZ(q1, q2)]]\n",
" yield cirq.CZ(q0, q1)\n",
" yield [cirq.H(q) for q in (q0, q1, q2)]\n",
" yield [cirq.CZ(q1, q2)]\n",
" yield [cirq.H(q0), [cirq.CZ(q1, q2)]]\n",
"\n",
"circuit = cirq.Circuit()\n",
"circuit.append(my_layer())\n",
Expand Down Expand Up @@ -521,7 +526,7 @@
},
"outputs": [],
"source": [
"circuit = cirq.Circuit(H(q0), H(q1))\n",
"circuit = cirq.Circuit(cirq.H(q0), cirq.H(q1))\n",
"print(circuit)"
]
},
Expand All @@ -544,7 +549,7 @@
},
"outputs": [],
"source": [
"circuit = cirq.Circuit(H(q0), CZ(q0, q1))\n",
"circuit = cirq.Circuit(cirq.H(q0), cirq.CZ(q0, q1))\n",
"for moment in circuit:\n",
" print(moment)"
]
Expand All @@ -566,7 +571,7 @@
},
"outputs": [],
"source": [
"circuit = cirq.Circuit(H(q0), CZ(q0, q1), H(q1), CZ(q0, q1))\n",
"circuit = cirq.Circuit(cirq.H(q0), cirq.CZ(q0, q1), cirq.H(q1), cirq.CZ(q0, q1))\n",
"print(circuit[1:3])"
]
},
Expand All @@ -579,6 +584,170 @@
"Especially useful is dropping the last moment (which are often just measurements): `circuit[:-1]`, or reversing a circuit: `circuit[::-1]`.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "e31b4ff57134"
},
"source": [
"### Nesting circuits with CircuitOperation\n",
"\n",
"Circuits can be nested inside one another with `cirq.CircuitOperation`. This is useful for concisely defining large, repetitive circuits, as the repeated section can be defined once and then be reused elsewhere. Circuits that need to be serialized especially benefit from this, as loops and functions used in the Python construction of a circuit are otherwise not captured in serialization.\n",
"\n",
"The subcircuit must first be \"frozen\" to indicate that no further changes will be made to it."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "b3772732f57b"
},
"outputs": [],
"source": [
"subcircuit = cirq.Circuit(\n",
" cirq.H(q1), cirq.CZ(q0, q1), cirq.CZ(q2, q1), cirq.H(q1)\n",
")\n",
"subcircuit_op = cirq.CircuitOperation(subcircuit.freeze())\n",
"circuit = cirq.Circuit(cirq.H(q0), cirq.H(q2), subcircuit_op)\n",
"print(circuit)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "3557398496c2"
},
"source": [
"Frozen circuits can also be constructed directly, for convenience."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "3d80776e09fe"
},
"outputs": [],
"source": [
"circuit = cirq.Circuit(\n",
" cirq.CircuitOperation(\n",
" cirq.FrozenCircuit(\n",
" cirq.H(q1), cirq.CZ(q0, q1), cirq.CZ(q2, q1), cirq.H(q1)\n",
" )\n",
" )\n",
")\n",
"print(circuit)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "e2c83225f05a"
},
"source": [
"A `CircuitOperation` is sort of like a function: by default, it will behave like the circuit it contains, but we can also pass arguments to it that alter the qubits it operates on, the number of times it repeats, and other properties. `CircuitOperation`s can also be referenced multiple times within the same \"outer\" circuit for conciseness."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "62d50a44bdb3"
},
"outputs": [],
"source": [
"subcircuit_op = cirq.CircuitOperation(cirq.FrozenCircuit(cirq.CZ(q0, q1)))\n",
"\n",
"# Create a copy of subcircuit_op that repeats twice...\n",
"repeated_subcircuit_op = subcircuit_op.repeat(2)\n",
"\n",
"# ...and another copy that replaces q0 with q2 to perform CZ(q2, q1).\n",
"moved_subcircuit_op = subcircuit_op.with_qubit_mapping({q0: q2})\n",
"circuit = cirq.Circuit(\n",
" repeated_subcircuit_op,\n",
" moved_subcircuit_op,\n",
")\n",
"print(circuit)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4e9d0e01508a"
},
"source": [
"For the most part, a `CircuitOperation` behaves just like a regular `Operation`: its qubits are the qubits of the contained circuit (after applying any provided mapping), and it can be placed inside any `Moment` that doesn't already contain operations on those qubits. This means that `CircuitOperation`s can be used to represent more complex operation timing, such as three operations on one qubit in parallel with two operations on another:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "1b06bfaddfdf"
},
"outputs": [],
"source": [
"subcircuit_op = cirq.CircuitOperation(cirq.FrozenCircuit(cirq.H(q0)))\n",
"circuit = cirq.Circuit(\n",
" subcircuit_op.repeat(3),\n",
" subcircuit_op.repeat(2).with_qubit_mapping({q0: q1})\n",
")\n",
"print(circuit)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "c8a67e3ec86d"
},
"source": [
"`CircuitOperation`s can also be nested within each other to arbitrary depth."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "4acf1a942b75"
},
"outputs": [],
"source": [
"qft_1 = cirq.CircuitOperation(cirq.FrozenCircuit(cirq.H(q0)))\n",
"qft_2 = cirq.CircuitOperation(\n",
" cirq.FrozenCircuit(cirq.H(q1), cirq.CZ(q0, q1) ** 0.5, qft_1)\n",
")\n",
"qft_3 = cirq.CircuitOperation(\n",
" cirq.FrozenCircuit(\n",
" cirq.H(q2), cirq.CZ(q1, q2) ** 0.5, cirq.CZ(q0, q2) ** 0.25, qft_2\n",
" )\n",
")\n",
"# etc."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "98a7293a6e4b"
},
"source": [
"Finally, the `mapped_circuit` method will return the circuit that a `CircuitOperation` represents after all repetitions and remappings have been applied. By default, this only \"unrolls\" a single layer of `CircuitOperation`s; to recursively unroll all layers, we can pass `deep=True` to this method."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "7a0b7fc127f2"
},
"outputs": [],
"source": [
"print('Single layer unroll:')\n",
"print(qft_3.mapped_circuit(deep=False))\n",
"print('Recursive unroll:')\n",
"print(qft_3.mapped_circuit(deep=True))"
]
},
{
"cell_type": "markdown",
"metadata": {
Expand Down

0 comments on commit dc19b85

Please sign in to comment.