<jupyter_start><jupyter_text>Copyright 2022 The Cirq Developers<jupyter_code># @title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.<jupyter_output><empty_output><jupyter_text>Cirq basics       View on QuantumAI        Run in Google Colab        View source on GitHub        Download notebook   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.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.For more in-depth examples of quantum algorithms and experiments, see [Experiments](/cirq/experiments). To begin, please follow the instructions for [installing Cirq](/cirq/install.md).<jupyter_code>try:
    import cirq
except ImportError:
    print("installing cirq...")
    !pip install --quiet cirq
    print("installed cirq.")
    import cirq

import cirq_google<jupyter_output><empty_output><jupyter_text>QubitsThe first part of creating a quantum circuit is to define a set of qubits (also known as a quantum registers) to act on.Cirq has three main ways of defining qubits:*   `cirq.NamedQubit`: used to label qubits by an abstract name*   `cirq.LineQubit`: qubits labelled by number in a linear array *   `cirq.GridQubit`: qubits labelled by two numbers in a rectangular lattice.Here are some examples of defining each type of qubit.<jupyter_code># Using named qubits can be useful for abstract algorithms
# as well as algorithms not yet mapped onto hardware.
q0 = cirq.NamedQubit('source')
q1 = cirq.NamedQubit('target')

# Line qubits can be created individually
q3 = cirq.LineQubit(3)

# Or created in a range
# This will create LineQubit(0), LineQubit(1), LineQubit(2)
q0, q1, q2 = cirq.LineQubit.range(3)

# Grid Qubits can also be referenced individually
q4_5 = cirq.GridQubit(4, 5)

# Or created in bulk in a square
# This will create 16 qubits from (0,0) to (3,3)
qubits = cirq.GridQubit.square(4)<jupyter_output><empty_output><jupyter_text>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.<jupyter_code>print(cirq_google.Sycamore)<jupyter_output><empty_output><jupyter_text>Gates and operationsThe 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:*   A `Gate` is an effect that can be applied to a set of qubits.  *   An `Operation` is a gate applied to a set of qubits.For instance, `cirq.H` is the quantum [Hadamard](https://en.wikipedia.org/wiki/Quantum_logic_gateHadamard_(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).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.Here are some examples of operations that can be performed on gates and operations:<jupyter_code># Example gates
cnot_gate = cirq.CNOT
pauli_z = cirq.Z

# Use exponentiation to get square root gates.
sqrt_x_gate = cirq.X**0.5

# Some gates can also take parameters
sqrt_sqrt_y = cirq.YPowGate(exponent=0.25)

# Create two qubits at once, in a line.
q0, q1 = cirq.LineQubit.range(2)

# Example operations
z_op = cirq.Z(q0)
not_op = cirq.CNOT(q0, q1)
sqrt_iswap_op = cirq.SQRT_ISWAP(q0, q1)

# You can also use the gates you specified earlier.
cnot_op = cnot_gate(q0, q1)
pauli_z_op = pauli_z(q0)
sqrt_x_op = sqrt_x_gate(q0)
sqrt_sqrt_y_op = sqrt_sqrt_y(q0)<jupyter_output><empty_output><jupyter_text>Circuits and momentsYou 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.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: By appending each operation one-by-one:<jupyter_code>circuit = cirq.Circuit()
qiubits = cirq.LineQubit.range(3)
circuit.append(cirq.H(qubits[0]))
circuit.append(cirq.H(qubits[1]))
circuit.append(cirq.H(qubits[2]))
print(circuit)<jupyter_output><empty_output><jupyter_text>Or by appending some iterable of operations. A preconstructed list works:<jupyter_code>circuit = cirq.Circuit()
ops = [cirq.H(q) for q in cirq.LineQubit.range(3)]
circuit.append(ops)
print(circuit)<jupyter_output><empty_output><jupyter_text>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.<jupyter_code># Append with generator
circuit = cirq.Circuit()
circuit.append(cirq.H(q) for q in cirq.LineQubit.range(3))
print(circuit)
# Initializer with generator
print(cirq.Circuit(cirq.H(q) for q in cirq.LineQubit.range(3)))<jupyter_output><empty_output><jupyter_text>Note that all of the Hadamard gates are pushed as far left as possible, and put into the same Moment since none overlap.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.<jupyter_code>print(cirq.Circuit(cirq.SWAP(q, q + 1) for q in cirq.LineQubit.range(3)))<jupyter_output><empty_output><jupyter_text>Sometimes, you may not want cirq to automatically shift operations all the way to the left.  To construct a circuit without doing this, you can create the circuit moment-by-moment or use a different `InsertStrategy`, explained more in the [Circuit documentation](../circuits.ipynb).<jupyter_code># Creates each gate in a separate moment by passing an iterable of Moments instead of Operations.
print(cirq.Circuit(cirq.Moment([cirq.H(q)]) for q in cirq.LineQubit.range(3)))<jupyter_output><empty_output><jupyter_text>Circuits and devicesOne 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.The following example demonstrates this with the Sycamore Device:<jupyter_code># Create some qubits.
q0 = cirq.GridQubit(5, 6)
q1 = cirq.GridQubit(5, 5)
q2 = cirq.GridQubit(4, 5)

# Create operations using the Sycamore gate, which is supported by the Sycamore device.
# However, create operations for both adjacent and non-adjacent qubit pairs.
adjacent_op = cirq_google.SYC(q0, q1)
nonadjacent_op = cirq_google.SYC(q0, q2)

# A working circuit for the Sycamore device raises no issues.
working_circuit = cirq.Circuit()
working_circuit.append(adjacent_op)
valid = cirq_google.Sycamore.validate_circuit(working_circuit)

# A circuit using invalid operations.
bad_circuit = cirq.Circuit()
bad_circuit.append(nonadjacent_op)
try:
    cirq_google.Sycamore.validate_circuit(bad_circuit)
except ValueError as e:
    print(e)<jupyter_output><empty_output><jupyter_text>SimulationThe results of the application of a quantum circuit can be calculated by a `Simulator`.  Cirq comes bundled with a simulator that can calculate the results of circuits up to about a limit of 20 qubits.  It can be initialized with `cirq.Simulator()`.There are two different approaches to using a simulator:*   `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.  *   `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.Next simulate a 2-qubit "Bell State":<jupyter_code># Create a circuit to generate a Bell State:
# 1/sqrt(2) * ( |00⟩ + |11⟩ )
bell_circuit = cirq.Circuit()
q0, q1 = cirq.LineQubit.range(2)
bell_circuit.append(cirq.H(q0))
bell_circuit.append(cirq.CNOT(q0, q1))

# Initialize Simulator
s = cirq.Simulator()

print('Simulate the circuit:')
results = s.simulate(bell_circuit)
print(results)

# For sampling, we need to add a measurement at the end
bell_circuit.append(cirq.measure(q0, q1, key='result'))

# Sample the circuit
samples = s.run(bell_circuit, repetitions=1000)<jupyter_output><empty_output><jupyter_text>Visualizing ResultsWhen 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`.<jupyter_code>import matplotlib.pyplot as plt

cirq.plot_state_histogram(samples, plt.subplot())
plt.show()<jupyter_output><empty_output><jupyter_text>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.<jupyter_code># Pull of histogram counts from the result data structure
counts = samples.histogram(key='result')
print(counts)

# Graph the histogram counts instead of the results
cirq.plot_state_histogram(counts, plt.subplot())
plt.show()<jupyter_output><empty_output><jupyter_text>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.   Using parameter sweepsCirq circuits allow for gates to have symbols as free parameters within the circuit.  This is especially useful for variational algorithms, which vary parameters within the circuit in order to optimize a cost function, but it can be useful in a variety of circumstances.For parameters, cirq uses the library `sympy` to add `sympy.Symbol` as parameters to gates and operations.  Once the circuit is complete, you can fill in the possible values of each of these parameters with a `Sweep`.  There are several possibilities that can be used as a sweep:*   `cirq.Points`: A list of manually specified values for one specific symbol as a sequence of floats*   `cirq.Linspace`: A linear sweep from a starting value to an ending value.*   `cirq.ListSweep`: A list of manually specified values for several different symbols, specified as a list of dictionaries.*   `cirq.Zip` and `cirq.Product`: Sweeps can be combined list-wise by zipping them together or through their Cartesian product.A parameterized circuit and sweep together can be run using the simulator or other sampler by changing `run()` to `run_sweep()` and adding the sweep as a parameter.Here is an example of sweeping an exponent of a X gate:<jupyter_code>import sympy

# Perform an X gate with variable exponent
q = cirq.GridQubit(1, 1)
circuit = cirq.Circuit(cirq.X(q) ** sympy.Symbol('t'), cirq.measure(q, key='m'))

# Sweep exponent from zero (off) to one (on) and back to two (off)
param_sweep = cirq.Linspace('t', start=0, stop=2, length=200)

# Simulate the sweep
s = cirq.Simulator()
trials = s.run_sweep(circuit, param_sweep, repetitions=1000)

# Plot all the results
x_data = [trial.params['t'] for trial in trials]
y_data = [trial.histogram(key='m')[1] / 1000.0 for trial in trials]
plt.scatter('t', 'p', data={'t': x_data, 'p': y_data})
plt.xlabel("trials")
plt.ylabel("frequency of qubit measured to be one")
plt.show()<jupyter_output><empty_output><jupyter_text>Unitary matrices and decompositionsMany 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.<jupyter_code>print('Unitary of the X gate')
print(cirq.unitary(cirq.X))

print('Unitary of SWAP operator on two qubits.')
q0, q1 = cirq.LineQubit.range(2)
print(cirq.unitary(cirq.SWAP(q0, q1)))

print('Unitary of a sample circuit')
print(cirq.unitary(cirq.Circuit(cirq.X(q0), cirq.SWAP(q0, q1))))<jupyter_output><empty_output><jupyter_text>Decompositions Many gates can be decomposed into an equivalent circuit with simpler operations and gates.  This is called decomposition and can be accomplished with the `cirq.decompose` protocol.  For instance, a Hadamard H gate can be decomposed into X and Y gates:<jupyter_code>print(cirq.decompose(cirq.H(cirq.LineQubit(0))))<jupyter_output><empty_output><jupyter_text>Another example is the 3-qubit Toffoli gate, which is equivalent to a controlled-controlled-X gate.  Many devices do not support a three qubit gate, so it is important<jupyter_code>q0, q1, q2 = cirq.LineQubit.range(3)
print(cirq.Circuit(cirq.decompose(cirq.TOFFOLI(q0, q1, q2))))<jupyter_output><empty_output><jupyter_text>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.  TransformersThe last concept in this tutorial is the transformer.  An transformer can take a circuit and modify it.  Usually, this will entail combining or modifying operations to make it more efficient and shorter, though an transformer can, in theory, do any sort of circuit manipulation.For example, the `cirq.merge_single_qubit_gates_to_phxz` transformer will take consecutive single-qubit operations and merge them into a single `PhasedXZ` operation.<jupyter_code>q = cirq.GridQubit(1, 1)
c = cirq.Circuit(cirq.X(q) ** 0.25, cirq.Y(q) ** 0.25, cirq.Z(q) ** 0.25)
print(c)
c = cirq.merge_single_qubit_gates_to_phxz(c)
print(c)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08091153501472283, 'fraction_numerical': 0.009217769811803866, 'mean_word_length': 5.355980471928397, 'pattern_counts': {'":': 1, '<': 77, '<?xml version=': 0, '>': 77, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '19', 'dir': 'jupyter-structured-clean-dedup', 'id': '323446', 'license': 'Apache-2.0', 'n_tokens_mistral': 4911, 'n_tokens_neox': 4660, 'n_words': 1908, 'path': 'docs/tutorials/basics.ipynb', 'repo_name': 'Nexuscompute/Cirq', 'size': '15622'}