From 725a552479055ca4a31989ee3c37d156481f4738 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Wed, 25 May 2022 09:28:01 -0700 Subject: [PATCH 1/2] Add CircuitOperation blurb --- docs/circuits.ipynb | 131 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/docs/circuits.ipynb b/docs/circuits.ipynb index 4a7552d3d47..a5a44e36ac8 100644 --- a/docs/circuits.ipynb +++ b/docs/circuits.ipynb @@ -579,6 +579,137 @@ "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 `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(H(q1), CZ(q0, q1), CZ(q2, q1), H(q1))\n", + "subcircuit_op = cirq.CircuitOperation(subcircuit.freeze())\n", + "circuit = cirq.Circuit(H(q0), 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(H(q1), CZ(q0, q1), CZ(q2, q1), H(q1))\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(CZ(q0, q1)))\n", + "repeated_subcircuit_op = subcircuit_op.repeat(2)\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(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(H(q0)))\n", + "qft_2 = cirq.CircuitOperation(\n", + " cirq.FrozenCircuit(H(q1), CZ(q0, q1) ** 0.5, qft_1)\n", + ")\n", + "qft_3 = cirq.CircuitOperation(\n", + " cirq.FrozenCircuit(H(q2), CZ(q1, q2) ** 0.5, CZ(q0, q2) ** 0.25, qft_2)\n", + ")\n", + "# etc." + ] + }, { "cell_type": "markdown", "metadata": { From 9d5c879e98f8e09ce52c5be851447902aad9e5d3 Mon Sep 17 00:00:00 2001 From: Orion Martin <40585662+95-martin-orion@users.noreply.github.com> Date: Thu, 26 May 2022 09:29:09 -0700 Subject: [PATCH 2/2] Review comments --- docs/circuits.ipynb | 96 +++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/docs/circuits.ipynb b/docs/circuits.ipynb index a5a44e36ac8..7991035b470 100644 --- a/docs/circuits.ipynb +++ b/docs/circuits.ipynb @@ -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])" ] @@ -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)" ] @@ -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)" ] @@ -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)" ] @@ -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)" ] @@ -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)" ] @@ -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)" ] @@ -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)" ] @@ -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", @@ -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)" ] }, @@ -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)" ] @@ -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])" ] }, @@ -587,7 +592,7 @@ "source": [ "### Nesting circuits with CircuitOperation\n", "\n", - "Circuits can be nested inside one another with `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", + "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." ] @@ -600,9 +605,11 @@ }, "outputs": [], "source": [ - "subcircuit = cirq.Circuit(H(q1), CZ(q0, q1), CZ(q2, q1), H(q1))\n", + "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(H(q0), H(q2), subcircuit_op)\n", + "circuit = cirq.Circuit(cirq.H(q0), cirq.H(q2), subcircuit_op)\n", "print(circuit)" ] }, @@ -625,7 +632,9 @@ "source": [ "circuit = cirq.Circuit(\n", " cirq.CircuitOperation(\n", - " cirq.FrozenCircuit(H(q1), CZ(q0, q1), CZ(q2, q1), H(q1))\n", + " cirq.FrozenCircuit(\n", + " cirq.H(q1), cirq.CZ(q0, q1), cirq.CZ(q2, q1), cirq.H(q1)\n", + " )\n", " )\n", ")\n", "print(circuit)" @@ -648,8 +657,12 @@ }, "outputs": [], "source": [ - "subcircuit_op = cirq.CircuitOperation(cirq.FrozenCircuit(CZ(q0, q1)))\n", + "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", @@ -675,7 +688,7 @@ }, "outputs": [], "source": [ - "subcircuit_op = cirq.CircuitOperation(cirq.FrozenCircuit(H(q0)))\n", + "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", @@ -700,16 +713,41 @@ }, "outputs": [], "source": [ - "qft_1 = cirq.CircuitOperation(cirq.FrozenCircuit(H(q0)))\n", + "qft_1 = cirq.CircuitOperation(cirq.FrozenCircuit(cirq.H(q0)))\n", "qft_2 = cirq.CircuitOperation(\n", - " cirq.FrozenCircuit(H(q1), CZ(q0, q1) ** 0.5, qft_1)\n", + " cirq.FrozenCircuit(cirq.H(q1), cirq.CZ(q0, q1) ** 0.5, qft_1)\n", ")\n", "qft_3 = cirq.CircuitOperation(\n", - " cirq.FrozenCircuit(H(q2), CZ(q1, q2) ** 0.5, CZ(q0, q2) ** 0.25, qft_2)\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": {