diff --git a/docs/circuits.ipynb b/docs/circuits.ipynb index 18bcbd33c1e..61c9522651b 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])" ] }, @@ -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": {