diff --git a/docs/tutorials/basics.ipynb b/docs/tutorials/basics.ipynb index 6aec9605d09..98c0a6d59e8 100644 --- a/docs/tutorials/basics.ipynb +++ b/docs/tutorials/basics.ipynb @@ -6,7 +6,7 @@ "id": "7FV_m2u2Hny8" }, "source": [ - "##### Copyright 2020 The Cirq Developers" + "##### Copyright 2022 The Cirq Developers" ] }, { @@ -18,7 +18,7 @@ }, "outputs": [], "source": [ - "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# @title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", @@ -68,11 +68,11 @@ "id": "67gUU740MSXd" }, "source": [ - "This tutorial will teach the basics of how to use Cirq. This tutorial will walk through how to use qubits, gates, and operations to create and simulate your first quantum circuit using Cirq. It will briefly introduce devices, unitary matrices, decompositions, and transformers.\n", + "This tutorial will teach the basics of how to use Cirq. It will walk through how to use qubits, gates, and operations to create and simulate your first quantum circuit using Cirq. It will briefly introduce devices, unitary matrices, decompositions, and transformers as well.\n", "\n", - "This tutorial isn’t a quantum computing 101 tutorial, we assume familiarity of quantum computing at about the level of the textbook “Quantum Computation and Quantum Information” by Nielsen and Chuang.\n", + "This tutorial isn’t a quantum computing 101 tutorial: it assumes familiarity of quantum computing at about the level of the textbook “Quantum Computation and Quantum Information” by Nielsen and Chuang.\n", "\n", - "For more in-depth examples closer to those found in current work, check out our tutorials page." + "For more in-depth examples of quantum algorithms and experiments, see [Experiments](/cirq/experiments)." ] }, { @@ -81,9 +81,7 @@ "id": "1dOjJlgrNUuz" }, "source": [ - "To begin, please follow the instructions for [installing Cirq](../install.md).\n", - "\n", - "Note: this notebook relies on unreleased Cirq features. If you want to try these features, make sure you install cirq via `pip install cirq --pre`." + "To begin, please follow the instructions for [installing Cirq](/cirq/install.md)." ] }, { @@ -99,8 +97,10 @@ "except ImportError:\n", " print(\"installing cirq...\")\n", " !pip install --quiet cirq\n", + " print(\"installed cirq.\")\n", " import cirq\n", - " print(\"installed cirq.\")" + "\n", + "import cirq_google" ] }, { @@ -130,8 +130,6 @@ }, "outputs": [], "source": [ - "import cirq_google\n", - "\n", "# Using named qubits can be useful for abstract algorithms\n", "# as well as algorithms not yet mapped onto hardware.\n", "q0 = cirq.NamedQubit('source')\n", @@ -145,7 +143,7 @@ "q0, q1, q2 = cirq.LineQubit.range(3)\n", "\n", "# Grid Qubits can also be referenced individually\n", - "q4_5 = cirq.GridQubit(4,5)\n", + "q4_5 = cirq.GridQubit(4, 5)\n", "\n", "# Or created in bulk in a square\n", "# This will create 16 qubits from (0,0) to (3,3)\n", @@ -158,7 +156,7 @@ "id": "4zE6AutyQhQ6" }, "source": [ - "There are also pre-packaged sets of qubits called [Devices](../devices.md). These are qubits along with a set of rules of how they can be used. A `cirq.Device` can be used to apply adjacency rules and other hardware constraints to a quantum circuit. For our example, we will use the `cirq_google.Sycamore` device that comes with cirq. It is a 2x11 grid that mimics early hardware released by Google." + "There are also pre-packaged sets of qubits called [Devices](/cirq/devices). These are qubits along with a set of rules for how they can be used. A `cirq.Device` can be used to ensure that two-qubit gates are only applied to qubits that are adjacent in the hardware, and other constraints. The following example will use the `cirq_google.Sycamore` device that comes with cirq. It is a diamond-shaped grid with 54 qubits that mimics early hardware released by Google." ] }, { @@ -180,16 +178,16 @@ "source": [ "## Gates and operations\n", "\n", - "The next step is to use the qubits to create operations that can be used in our circuit. Cirq has two concepts that are important to understand here:\n", + "The next step is to use the qubits to create operations that can be used in the circuit. Cirq has two concepts that are important to understand here:\n", "\n", "* A `Gate` is an effect that can be applied to a set of qubits. \n", "* An `Operation` is a gate applied to a set of qubits.\n", "\n", "For instance, `cirq.H` is the quantum [Hadamard](https://en.wikipedia.org/wiki/Quantum_logic_gate#Hadamard_(H)_gate) and is a `Gate` object. `cirq.H(cirq.LineQubit(1))` is an `Operation` object and is the Hadamard gate applied to a specific qubit (line qubit number 1).\n", "\n", - "Many textbook gates are included within cirq. `cirq.X`, `cirq.Y`, and `cirq.Z` refer to the single-qubit Pauli gates. `cirq.CZ`, `cirq.CNOT`, `cirq.SWAP` are a few of the common two-qubit gates. `cirq.measure` is a macro to apply a `MeasurementGate` to a set of qubits. You can find more, as well as instructions on how to creats your own custom gates, on the [Gates documentation](../gates.ipynb) page.\n", + "Many textbook gates are included within cirq. `cirq.X`, `cirq.Y`, and `cirq.Z` refer to the single-qubit Pauli gates. `cirq.CZ`, `cirq.CNOT`, `cirq.SWAP` are a few of the common two-qubit gates. `cirq.measure` is a macro to apply a `MeasurementGate` to a set of qubits. You can find more, as well as instructions on how to create your own custom gates, on the [Gates documentation](/cirq/gates) page.\n", "\n", - "Many arithmetic operations can also be applied to gates. Here are some examples:" + "Here are some examples of operations that can be performed on gates and operations:" ] }, { @@ -201,20 +199,28 @@ "outputs": [], "source": [ "# Example gates\n", - "not_gate = cirq.CNOT\n", + "cnot_gate = cirq.CNOT\n", "pauli_z = cirq.Z\n", "\n", - "# Using exponentiation to get square root gates\n", + "# Use exponentiation to get square root gates.\n", "sqrt_x_gate = cirq.X**0.5\n", "\n", "# Some gates can also take parameters\n", "sqrt_sqrt_y = cirq.YPowGate(exponent=0.25)\n", "\n", - "# Example operations\n", + "# Create two qubits at once, in a line.\n", "q0, q1 = cirq.LineQubit.range(2)\n", + "\n", + "# Example operations\n", "z_op = cirq.Z(q0)\n", "not_op = cirq.CNOT(q0, q1)\n", - "sqrt_iswap_op = cirq.SQRT_ISWAP(q0, q1)" + "sqrt_iswap_op = cirq.SQRT_ISWAP(q0, q1)\n", + "\n", + "# You can also use the gates you specified earlier.\n", + "cnot_op = cnot_gate(q0, q1)\n", + "pauli_z_op = pauli_z(q0)\n", + "sqrt_x_op = sqrt_x_gate(q0)\n", + "sqrt_sqrt_y_op = sqrt_sqrt_y(q0)" ] }, { @@ -225,9 +231,11 @@ "source": [ "## Circuits and moments\n", "\n", - "We are now ready to construct a quantum circuit. A `Circuit` is a collection of `Moment`s. A `Moment` is a collection of `Operation`s that all act during the same abstract time slice. Each `Operation` must have a disjoint set of qubits from the other `Operation`s in the `Moment`. A `Moment` can be thought of as a vertical slice of a quantum circuit diagram.\n", + "You are now ready to construct a quantum circuit. A `Circuit` is a collection of `Moment`s. A `Moment` is a collection of `Operation`s that all act during the same abstract time slice. Each `Operation` must be applied to a disjoint set of qubits compared to each of the other `Operation`s in the `Moment`. A `Moment` can be thought of as a vertical slice of a quantum circuit diagram.\n", "\n", - "Circuits can be constructed in several different ways. By default, cirq will attempt to slide your operation into the earliest possible `Moment` when you insert it.\n" + "Circuits can be constructed in several different ways. By default, cirq will attempt to slide your operation into the earliest possible `Moment` when you insert it. You can use the append function in two ways: \n", + "\n", + "By appending each operation one-by-one: " ] }, { @@ -239,12 +247,72 @@ "outputs": [], "source": [ "circuit = cirq.Circuit()\n", - "# You can create a circuit by appending to it\n", - "circuit.append(cirq.H(q) for q in cirq.LineQubit.range(3))\n", - "# All of the gates are put into the same Moment since none overlap\n", + "qiubits = cirq.LineQubit.range(3)\n", + "circuit.append(cirq.H(qubits[0]))\n", + "circuit.append(cirq.H(qubits[1]))\n", + "circuit.append(cirq.H(qubits[2]))\n", "print(circuit)" ] }, + { + "cell_type": "markdown", + "metadata": { + "id": "ccd445492b19" + }, + "source": [ + "Or by appending some iterable of operations. A preconstructed list works:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HEuqEZcXkz3Q" + }, + "outputs": [], + "source": [ + "circuit = cirq.Circuit()\n", + "ops = [cirq.H(q) for q in cirq.LineQubit.range(3)]\n", + "circuit.append(ops)\n", + "print(circuit)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a0a5de4d8f01" + }, + "source": [ + "A generator that yields operations also works. This syntax will be used often in documentation, and works both with the `cirq.Circuit()` initializer and the `cirq.Circuit.append()` function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HEuqEZcXkz3Q" + }, + "outputs": [], + "source": [ + "# Append with generator\n", + "circuit = cirq.Circuit()\n", + "circuit.append(cirq.H(q) for q in cirq.LineQubit.range(3))\n", + "print(circuit)\n", + "# Initializer with generator\n", + "print(cirq.Circuit(cirq.H(q) for q in cirq.LineQubit.range(3)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a6bd4d3152d5" + }, + "source": [ + "Note that all of the Hadamard gates are pushed as far left as possible, and put into the same Moment since none overlap.\n", + "\n", + "If your operations are applied to the same qubits, they will be put in sequential, insertion-ordered moments. In the following example, the two-qubit gates overlap, and are placed in consecutive moments." + ] + }, { "cell_type": "code", "execution_count": null, @@ -253,8 +321,7 @@ }, "outputs": [], "source": [ - "# We can also create a circuit directly as well:\n", - "print(cirq.Circuit(cirq.SWAP(q, q+1) for q in cirq.LineQubit.range(3)))" + "print(cirq.Circuit(cirq.SWAP(q, q + 1) for q in cirq.LineQubit.range(3)))" ] }, { @@ -274,7 +341,7 @@ }, "outputs": [], "source": [ - "# Creates each gate in a separate moment.\n", + "# Creates each gate in a separate moment by passing an iterable of Moments instead of Operations.\n", "print(cirq.Circuit(cirq.Moment([cirq.H(q)]) for q in cirq.LineQubit.range(3)))" ] }, @@ -286,9 +353,9 @@ "source": [ "### Circuits and devices\n", "\n", - "One important consideration when using real quantum devices is that there are often hardware constraints on the circuit. Creating a circuit with a `Device` will allow you to capture some of these requirements. `Device`s can be used to validate your circuit to make sure that it contains no illegal operations.\n", + "One important consideration when using real quantum devices is that there are often constraints on circuits that are able to be run on the hardware. `Device` objects specify these constraints and can be used to validate your circuit to make sure that it contains no illegal operations. For more information on what constraints `Device` objects can specify and how to use them, see the [Devices](/cirq/devices) page.\n", "\n", - "Let's look at an example using the Sycamore Device:" + "The following example demonstrates this with the Sycamore Device:" ] }, { @@ -299,9 +366,13 @@ }, "outputs": [], "source": [ + "# Create some qubits.\n", "q0 = cirq.GridQubit(5, 6)\n", "q1 = cirq.GridQubit(5, 5)\n", "q2 = cirq.GridQubit(4, 5)\n", + "\n", + "# Create operations using the Sycamore gate, which is supported by the Sycamore device.\n", + "# However, create operations for both adjacent and non-adjacent qubit pairs.\n", "adjacent_op = cirq_google.SYC(q0, q1)\n", "nonadjacent_op = cirq_google.SYC(q0, q2)\n", "\n", @@ -314,9 +385,9 @@ "bad_circuit = cirq.Circuit()\n", "bad_circuit.append(nonadjacent_op)\n", "try:\n", - " cirq_google.Sycamore.validate_circuit(bad_circuit)\n", + " cirq_google.Sycamore.validate_circuit(bad_circuit)\n", "except ValueError as e:\n", - " print(e)" + " print(e)" ] }, { @@ -331,10 +402,10 @@ "\n", "There are two different approaches to using a simulator:\n", "\n", - "* `simulate()`: Since we are classically simulating a circuit, a simulator can directly access and view the resulting wave function. This is useful for debugging, learning, and understanding how circuits will function. \n", + "* `simulate()`: When classically simulating a circuit, a simulator can directly access and view the resulting wave function. This is useful for debugging, learning, and understanding how circuits will function. \n", "* `run()`: When using actual quantum devices, we can only access the end result of a computation and must sample the results to get a distribution of results. Running the simulator as a sampler mimics this behavior and only returns bit strings as output.\n", "\n", - "Let's try to simulate a 2-qubit \"Bell State\":" + "Next simulate a 2-qubit \"Bell State\":" ] }, { @@ -350,23 +421,80 @@ "bell_circuit = cirq.Circuit()\n", "q0, q1 = cirq.LineQubit.range(2)\n", "bell_circuit.append(cirq.H(q0))\n", - "bell_circuit.append(cirq.CNOT(q0,q1))\n", + "bell_circuit.append(cirq.CNOT(q0, q1))\n", "\n", "# Initialize Simulator\n", - "s=cirq.Simulator()\n", + "s = cirq.Simulator()\n", "\n", "print('Simulate the circuit:')\n", - "results=s.simulate(bell_circuit)\n", + "results = s.simulate(bell_circuit)\n", "print(results)\n", - "print()\n", "\n", "# For sampling, we need to add a measurement at the end\n", "bell_circuit.append(cirq.measure(q0, q1, key='result'))\n", "\n", - "print('Sample the circuit:')\n", - "samples=s.run(bell_circuit, repetitions=1000)\n", - "# Print a histogram of results\n", - "print(samples.histogram(key='result'))" + "# Sample the circuit\n", + "samples = s.run(bell_circuit, repetitions=1000)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "be79fa541b8a" + }, + "source": [ + "## Visualizing Results\n", + "\n", + "When you use `run()` to get a sample distribution of measurements, you can directly graph the simulated samples as a histogram with `cirq.plot_state_histogram`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "69dca44091f4" + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "cirq.plot_state_histogram(samples, plt.subplot())\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b4f20cffe4ae" + }, + "source": [ + "However, this histogram has some empty qubit states, which may become problematic if you work with more qubits. To graph sparse sampled data, first get the `Counts` from your results with its `histogram()` function, and pass that to `cirq.plot_state_histogram`. By collecting the results into counts, all the qubit states that were never seen are ignored." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "07ec16b02594" + }, + "outputs": [], + "source": [ + "# Pull of histogram counts from the result data structure\n", + "counts = samples.histogram(key='result')\n", + "print(counts)\n", + "\n", + "# Graph the histogram counts instead of the results\n", + "cirq.plot_state_histogram(counts, plt.subplot())\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ef90dabdd52e" + }, + "source": [ + "A histogram over the states that were actually observed can often be more useful when analyzing results. To learn more about the available options for creating result histograms, see the [State Histograms](/cirq/tutorials/state_histograms) page. " ] }, { @@ -401,13 +529,11 @@ }, "outputs": [], "source": [ - "import matplotlib.pyplot as plt\n", "import sympy\n", "\n", "# Perform an X gate with variable exponent\n", - "q = cirq.GridQubit(1,1)\n", - "circuit = cirq.Circuit(cirq.X(q) ** sympy.Symbol('t'),\n", - " cirq.measure(q, key='m'))\n", + "q = cirq.GridQubit(1, 1)\n", + "circuit = cirq.Circuit(cirq.X(q) ** sympy.Symbol('t'), cirq.measure(q, key='m'))\n", "\n", "# Sweep exponent from zero (off) to one (on) and back to two (off)\n", "param_sweep = cirq.Linspace('t', start=0, stop=2, length=200)\n", @@ -419,7 +545,10 @@ "# Plot all the results\n", "x_data = [trial.params['t'] for trial in trials]\n", "y_data = [trial.histogram(key='m')[1] / 1000.0 for trial in trials]\n", - "plt.scatter('t','p', data={'t': x_data, 'p': y_data})\n" + "plt.scatter('t', 'p', data={'t': x_data, 'p': y_data})\n", + "plt.xlabel(\"trials\")\n", + "plt.ylabel(\"frequency of qubit measured to be one\")\n", + "plt.show()" ] }, { @@ -430,7 +559,7 @@ "source": [ "## Unitary matrices and decompositions\n", "\n", - "Most quantum operations have a unitary matrix representation. This matrix can be accessed by applying `cirq.unitary()`. This can be applied to gates, operations, and circuits that support this protocol and will return the unitary matrix that represents the object." + "Many quantum operations have unitary matrix representations. This matrix can be accessed by applying `cirq.unitary(operation)` to that `operation`. This can be applied to gates, operations, and circuits that support this protocol and will return the unitary matrix that represents the object. See [Protocols](/cirq/protocols) for more about this and other protocols." ] }, { @@ -503,7 +632,7 @@ "id": "VWcik4ZwggXj" }, "source": [ - "The above decomposes the Toffoli into a simpler set of one-qubit gates and CZ gates at the cost of lengthening the circuit considerably." + "The above decomposes the Toffoli into a simpler set of one-qubit gates and two-qubit CZ gates at the cost of lengthening the circuit considerably." ] }, { @@ -527,8 +656,8 @@ }, "outputs": [], "source": [ - "q=cirq.GridQubit(1, 1)\n", - "c=cirq.Circuit(cirq.X(q) ** 0.25, cirq.Y(q) ** 0.25, cirq.Z(q) ** 0.25)\n", + "q = cirq.GridQubit(1, 1)\n", + "c = cirq.Circuit(cirq.X(q) ** 0.25, cirq.Y(q) ** 0.25, cirq.Z(q) ** 0.25)\n", "print(c)\n", "c = cirq.merge_single_qubit_gates_to_phxz(c)\n", "print(c)" @@ -540,7 +669,7 @@ "id": "xRfQqzdx7lUI" }, "source": [ - "Other transformers can assist in transforming a circuit into operations that are native operations on specific hardware devices. You can find more about transformers and how to create your own elsewhere in the documentation." + "Other transformers can assist in transforming a circuit into operations that are native operations on specific hardware devices. You can find more about transformers and how to create your own in [Transformers](/cirq/transformers)." ] }, { @@ -558,7 +687,7 @@ "* Learn about the variety of [Gates](../gates.ipynb) available in cirq and more about the different ways to construct [Circuits](../circuits.ipynb)\n", "* Learn more about [Simulations](../simulate.ipynb) and how it works.\n", "* Learn about [Noise](../noise.ipynb) and how to utilize multi-level systems using [Qudits](../qudits.ipynb)\n", - "* Dive into some [Experiments](experiments) and some in-depth tutorials of how to use cirq.\n", + "* Dive into some [Experiments](/cirq/experiments) and some in-depth tutorials of how to use cirq.\n", "\n", "Also, join our [cirq-announce mailing list](https://groups.google.com/forum/#!forum/cirq-announce) to hear about changes and releases or go to the [cirq github](https://github.com/quantumlib/Cirq/) to file issues." ]