Quantum Computing Report

Where Qubits Entangle with Commerce

Ryan LaRose

Department of Computational Mathematics, Science, and Engineering, Michigan State University

Department of Physics and Astronomy, Michigan State University

Abstract

In July 2018, the Quantum Computing Report published an in-depth analysis of four major gate-model quantum software platforms: Forest by Rigetti, QISKit by IBM, ProjectQ by ETH Zurich, and the Quantum Developer Kit by Microsoft. Shortly after, Google announced the release of their own quantum software platform: Cirq. In this paper, we provide an overview and comparative analysis of this newly-released platform with respect to the previously-reviewed quantum software platforms. Our analysis proceeds similarly by covering requirements and installation, language syntax through example programs, quantum simulators, and quantum computer capabilities. We additionally cover more advanced features of the platform including quantum circuit manipulation/optimization, device-oriented circuits and quantum compiling, and strong support for variational quantum algorithms and near-term quantum computing.

Quantum computing is transitioning from a theoretical to practical phase. Historically, researchers have asked questions about the possibilities of speedups through black-box access to abstract, idealized quantum computers. Recently, small, imperfect quantum computers have been fabricated and made available over the cloud. A significant body of literature is emerging as researchers use these devices to solve problems in nuclear physics [Du2018], quantum chemistry [Pe2014], condensed matter [La2018], optimization [Fa2014], number theory [An2018], graph theory [Wa2018], and even quantum computing itself [Kh2018]. While these problems are small and easily handled by conventional computers, the prospect of large-scale quantum computers could quickly change this. Even on current quantum computers, certain contrived problems may soon demonstrate “quantum supremacy” [Ne2017, Ma2018], an exciting landmark in the history of computation.

This theoretical-to-practical progression of the field necessitates access to quantum computers that is much different from black box access on pen and paper. Many institutions in industry and academia have recognized this and began building tools for this purpose, and a slew of startup companies has emerged to fill the gaps in the transition to practical quantum computing. The following diagram shows a partial snapshot of this rapidly evolving landscape:

For most “full-stack” quantum computing companies, access to quantum computers is granted over the cloud, and the interaction between research scientists and quantum computers is mediated by a software platform with API access. To an outside observer, this interaction does not warrant much thought: it just needs to give users the ability to implement quantum gates on qubits. However, many practical considerations emerge when using a software interface to communicate with a real or simulated quantum computer. Among these are the following:

- What gate operations are natively built-in?
- Can a gate that’s not native be implemented into a quantum algorithm? How difficult is it to do this?
- How many quantum computers does the platform give access to?
- Is compilation handled automatically by the software? And to what degree of optimality?
- How do job requests to quantum computers get handled? In a queue, dedicated time?
- What quantum computer simulators can be used to test algorithms? How many qubits can be simulated? Is the simulator noisy or noiseless?
- Does every quantum algorithm have to be programmed manually? Or are some common subroutines built-in?
- What
*classical*programming language is the software written in? - How easy is it to create, work with, and manipulate quantum circuits?
- How easy is it to parametrize algorithms for near-term quantum computing?

Each of these considerations is important from a practical perspective. For example, an algorithm on many qubits may be better implemented on a platform with a higher-performance simulator; an algorithm with many gates may be better for a platform with higher fidelity qubits; an algorithm with many non-standard gates may be better for a platform with an optimal compiler. Other considerations such as examples, tutorials, and documentation are equally important as they help bring in new users and answer questions of experienced users.

For these reasons, it is both valid and important to evaluate quantum software platforms as more than the simple interface they may appear to be. To this end, the Quantum Computing Report published an article [LaR2018] comparing Forest by Rigetti, QISKit by IBM, ProjectQ by ETH Zurich, and the Quantum Developer Kit by Microsoft. (Subsequently, other articles on quantum software have also been written, e.g. [Fi2018].) Each platform was found to have different strengths and different emphases that determined the set of problems best-suited for the environment. The purpose of this article is to introduce and analyze Cirq in a similar fashion.

To this end, the rest of the article is organized as follows. After briefly commenting on the format of this article, we cover installation, documentation, language syntax, and quantum computer/simulator support in Cirq. We then transition into more advanced features like circuit manipulation and optimization and circuits for a partcular quantum computer. Lastly, we conclude with example programs including a variational quantum algorithm to demonstrate the near-term capabilities of Cirq. Throughout, we refer back to previously covered software platforms to maintain the comparative analysis in our previous installation.

*Installation* below). The Cirq code in the notebook version will be kept up-to-date with future versions/releases of Cirq. The article assumes basic familiarity with quantum computing, for which many good resources now exist.

Institution |
Version |
GitHub |
Documentation |
OS |

Google Quantum AI | v0.4.0 | Git | Docs | Mac, Windows, Linux |

Requirements |
Classical Language |
Quantum Language |
Quantum Hardware |
Simulator |

Python 3.5 or greater (else Python 2.7) | Python | —- | Foxtail (22 qubits), Bristlecone (72 qubits) | ~20-30 qubits |

Cirq is an open-source Python framework for “creating, editing, and invoking Noisy Intermediate-Scale Quantum (NISQ) circuits” [Ho2018]. The first version of the software was publicly announced in July 2018; the code is still in alpha testing and under development. As such, some features or code included in this article may need modifications for future versions of Cirq. This article should be considered a review for the version of Cirq at the time of writing, which is listed in the table above. (As mentioned above, the Jupyter Notebook version of this article, hosted online at [GitHub](https://github.com/rmlarose), will be kept up to date as future versions of Cirq are released.)

The easiest way to install `Cirq`

is by using pip via

`pip install cirq`

at a command line. Without leaving the notebook, executing the cell below will try to install Cirq v0.4.0 on the user’s computer. Alternatively, the source code for Cirq can be obtained from https://github.com/quantumlib/Cirq. For complete installation instructions on multiple platforms, see the documentation at https://cirq.readthedocs.io/en/latest/install.html. Readers who simply wish to read the article without using Cirq can ignore this step.

In [ ]:

```
"""Attempts to pip install Cirq 0.4.0."""
#!pip install --upgrade pip
#!pip install cirq==0.4.0
```

*Schedules and Devices* details how Cirq can be used with specific quantum hardware and reflects the emphasis on near-term quantum computing. The documentation also contains a detailed API reference for the entire library and development guidelines for those who may want to contribute to the source code.

In [1]:

```
"""Library imports for the article."""
import cirq
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
```

In [2]:

```
"""Create a random number generator circuit using Cirq."""
# get a qubit register
qbits = [cirq.LineQubit(0)]
# get a quantum circuit
circ = cirq.Circuit()
# add the instructions to the circuit
circ.append([cirq.H(qbits[0]),
cirq.measure(qbits[0], key="z")])
```

Note that Cirq defines qubits to be `LineQubit`s or `GridQubit`s, since these are common constructions in NISQ computers. The former is indexed by one integer, as we have done in Line 4 above, and the latter by two (x, y coordinates). Qubits are commonly defined in lists (or generally iterables) for easy indexing in algorithms. In line 6 above we instantiate a circuit, and in lines 9-10 we append the instructions for the algorithm. (There are multiple ways to add instructions to an algorithm in Cirq. For most of this article, we’ll stick to the above method for simplicity. See Section III.A for alternatives.)

The Cirq library provides text diagram representation of quantum circuits, which can be visualized by printing out the circuit:

In [3]:

```
"""Print out the random number generator circuit."""
print(circ)
```

While these diagrams are not of publication quality like those that can be made in ProjectQ or even QISKit, they are a useful tool for verifying correctness of quantum circuits and debugging. Note that measurements are made with a `key` to easily access the results of running the circuit, as we will see below.

API is (as of June 22, 2018) restricted to invitation only.”

In [4]:

```
"""Print out the architecture of the FoxTail quantum computer."""
s = "FoxTail has {} qubits arranged as follows:\n"
print(s.format(len(cirq.google.Foxtail.qubits)))
print(cirq.google.Foxtail)
```

In [5]:

```
"""Print out the architecture of the Bristlecone quantum computer."""
s = "Bristlecone has {} qubits arranged as follows:\n"
print(s.format(len(cirq.google.Bristlecone.qubits)))
print(cirq.google.Bristlecone)
```

*Z* gate as their two-qubit unitary (as opposed to a CNOT gate as is common with other architectures, e.g. IBM), but it is unclear to the author which particular single qubit gates are in the native gate set. (This statement is made based off of experiments compiling arbitrary gates into Foxtail/Bristlecone. See Section III.D below.) For reference, the Controlled-*Z* gate has the following matrix representation: It should be noted that Cirq provides built-in functionality to convert its circuits to OpenQASM code:

In [6]:

```
"""Generate OpenQASM code for the random number generator circuit."""
print(circ.to_qasm())
```

Although access to Google’s quantum computers is currently restricted, Cirq provides two quantum computer simulators, the `Simulator` and the `XmonSimulator`, to locally execute quantum algorithms. The `Simulator` works for generic gates that implement their unitary matrix. The `XmonSimulator` is specialized for the native gate set of Google’s quantum computers and can utilize multi-threading to improve performance in certain cases.

To run the random bit generator circuit above with the `Simulator`, we can do the following:

In [7]:

```
"""Run the random number generator on the XmonSimulator."""
# get the simulator
simulator = cirq.Simulator()
# run the circuit
out = simulator.run(circ, repetitions=50)
# get the results and display them
results = out.histogram(key="z")
print(results)
```

The output is returned as a `Counter` object (Python built-in class in the `collections` library) that displays key-value pairs corresponding to the output and number of times that output was recorded. Cirq also provides the function `plot_state_histogram` to visualize the output distribution:

In [8]:

```
"""Display the output distribution of a cirq.TrialResult."""
counts = cirq.plot_state_histogram(out)
print("counts =", counts)
```

For such a simple quantum algorithm, any quantum computer simulator is essentially equivalent. However, as algorithms scale to larger numbers of qubits and larger numbers of gates, runtime of the simulator can become important. The best current methods for classically simulating quantum circuits peak somewhere around 50 qubits due to memory limitations. (Note that this is the memory limitation of the world’s best supercomputers, personal computers are typically much lower around 30 qubits.) For a fixed number of qubits, better simulators can simulate circuits with lower overall runtime and potentially even lower memory usage, which are desirable features for many applications.

Below we test the performance of Cirq’s simulators along these lines. After, we discuss other important capabilities of quantum computer simulators such as noise modeling.

Here we test the performance of Cirq’s simulators using random quantum circuits with different numbers of qubits and total depth. The particular form of the circuit we will consider consists of random single qubit rotations on all qubits, then a layer of entangling CNOT gates with one qubit randomly selected as the control. We define the layer of single qubit rotations plus the layer of entangling CNOTs to have a depth of one. (A circuit diagram is shown below in the article.)

The code for testing the simulator is contained in a separate Python file called `cirq_code.py`. Within this file is a function called `sim_test` which inputs the number of qubits, depth of the circuit, number of times to run the circuit (also called *shots* or *repetitions*), and which simulator in Cirq to use. This function creates a random circuit of the form described above, runs it for the desired number of times, then returns the wall clock time for how long it took. An example of using this function is shown in the code cell below.

In [9]:

```
"""Simulator performance test for a small circuit.
Note that the circuit structure will be random.
"""
# import the simulator test function
from cirq_code import sim_test
# inputs to sim_test
nqubits = 4 # number of qubits
depth = 1 # depth of circuit
nreps = 1 # number of repetitions
verbose = True # verbose output (prints circuit)
sim_type = 0 # 0 <==> XmonSimulator, 1 <==> Simulator
# do the timing test
time = sim_test(nqubits, depth, nreps,
verbose=verbose, sim_type=sim_type)
# display the output
print("\nIt took %0.2f seconds to run the circuit above." % time)
```

In [10]:

```
"""Simulator performance test for a larger circuit."""
# inputs to sim_test
nqubits = 20 # number of qubits
depth = 10 # depth of circuit
nreps = 1 # number of repetitions
verbose=False # verbose output (prints circuit)
# do the timing test
time = sim_test(nqubits, depth, nreps, verbose=verbose)
# display the output
print("\nIt took %0.2f seconds to run the circuit." % time)
```

Here the code takes noticeably longer to run. For many applications, simulating large circuits quickly is an important task. We use the `sim_test` function above to test the performance of both the `Simulator` and `XmonSimulator` by sweeping over values of qubits and depth. In particular, we sweep over the qubit numbers *n = 10, 12, …, 24* and depths *d = 20, 40, …, 100* and time the simulator performance at each. The results of this study are displayed in the figure below.

As can be seen, the performance of the `XmonSimulator` and `Simulator` are similar in terms of overall runtime. Depending on the computer architecture, increasing the number of threads or shards could increase the performance of the `XmonSimulator`. (This is discussed below in Section 2.) The maximum number of qubits that can be simulated depends on memory of the user’s computer (more RAM implies larger circuits). The largest circuit the author attempted to run with the `Simulator` contained 26 qubits and a depth of 10 as defined above. This circuit finished running in just over one hour. Larger circuit instances throw `MemoryError`s on the author’s computer. (For reference, on a similar circuit with a depth of 20, the state vector simulator in QISKit is able to simulate 25 qubits in 160 seconds, and the C++ simulator in ProjectQ is able to simulate 27 qubits in 504 seconds [LaR2018].)

The resource requirements (memory and runtime) for simulating larger circuits quickly reaches the limitations of current supercomputers. Researchers at Google are very interested in using a quantum computer to simulate some circuit that a classical computer cannot feasibly do, a feat called *quantum supremacy*. Cirq contains built-in functions for generating so-called “supremacy circuits,” as can be seen below.

In [11]:

```
"""Display a quantum supremacy circuit on the
first three rows of Bristlecone with a CZ depth of 5.
"""
print(cirq.generate_supremacy_circuit_google_v2_bristlecone(3, 5, 0))
```

(In the circuit diagram, brackets at the top/bottom are placed when operations are in the same moment but it is impossible to draw them all in the same column. In the second moment, all *T* gates and the Controlled-*Z* gates, which are all encompassed by the top/bottom brackets, happen simultaneously.)

As we’ve seen above, this small circuit using only three rows (six qubits) of Bristlecone could be easily simulated by a classical computer. However, extending the circuit to all qubits on Bristlecone generates a circuit that is extremely difficult to classically simulate. Quantum supremacy is expected to be announced within the next few years.

While raw performance is an important aspect of simulators, other features such as noise capabilities and special-purpose simulators (e.g., Clifford circuit simulators) are equally if not more important. In particular, noise capabilities allow users to mimic the evolution on real quantum hardware and get better estimates of the performance of their algorithms. The software packages pyQuil and QISKit contain these features.

Currently, Cirq does not contain any obvious support for noisy circuit simulation for either the `Simulator` or `XmonSimulator`. As Cirq is still in alpha testing, however, this feature is likely to be implemented in future releases and is a current work in progress.

For now, notable features of the simulators include multi-threading for the `XmonSimulator` and access to the wave function for both simulators. To get a simulator that uses multiple threads, we can pass an `XmonOptions` object specified with the number of threads into the `XmonSimulator`, as shown below.

In [12]:

```
"""Get an XmonSimulator with multiple threads (shards)."""
options = cirq.google.sim.XmonOptions(num_shards=2)
simulator = cirq.google.XmonSimulator(options)
```

This simulator can then be used to execute circuits in the same way as above.

Additionally, there are two types of methods that simulators support, the “run methods” and the “simulate methods.” The “run methods” (`run` and `run_sweep`) emulate quantum computer hardware and only return measurement results. (The `run` method is what we used above to test the simulator performance.) For full access to the wavefunction and for debugging purposes, the “simulate methods” (`simulate`, `simulate_sweep`, and `simulate_moment_steps`) can be used. An example of using a simulate method is shown below.

In [13]:

```
"""Use the "simulate methods" to get full access to the wavefunction."""
# make a bell state preparation circuit
circ = cirq.Circuit()
qbits = [cirq.LineQubit(x) for x in range(2)]
circ.append([cirq.H(qbits[0]), cirq.CNOT(qbits[0], qbits[1])])
# print out the circuit
print(circ)
# simulate the circuit
res = simulator.simulate(circ)
```

Once the circuit has been simulated, the result

`res`

is stored as a `SimulationTrialResult`

. This output supports several useful features including generating the wavefunction, (reduced) density matrix, and even Dirac notation of the state. A few of these features are demonstrated below.In [14]:

```
"""Use the output of a simulation to generate
the wavefunction, density matrix, and Dirac notation.
"""
# show the wavefunction
print("The wavefunction of the final state is:\n",
res.final_state, end="\n")
# show the Dirac notation for the state
print("\nThe Dirac notation for the final state is:\n",
res.dirac_notation())
# show the density matrix of the total state
print("\nThe density matrix of the final state is:\n",
res.density_matrix())
# show the reduced density matrix of the first qubit
print("\nThe reduced density matrix of the first qubit is:\n",
res.density_matrix([0]))
```

As suggested by the name, the main utility of Cirq is working with and manipulating quantum circuits. By “manipulating quantum circuits,” we mean operations of the following form:

- Creating quantum circuits.
- Performing arithmetic with circuits.
- Inserting instructions in a circuit.
- Removing instructions from a circuit.
- Gaining information about a circuit.

In order to demonstrate these operations, we’ll first import a function for creating quantum circuits with random single qubit gates.

In [15]:

```
"""Get a function for creating circuits with random one-qubit gates."""
from cirq_code import random_circuit
# create a random circuit with 5 qubits and 5 moments
circ, qbits = random_circuit(5, 5)
print(circ)
```

*moments*, where a moment is a set of operations that happens at the same time. To see each moment comprising a circuit, we can do the following:

In [16]:

```
"""Print out each moment in the circuit."""
for moment in circ:
print(moment)
```

Arithmetic can be performed on circuits in a natural way. For example, the sum of two circuits is a circuit consisting of all moments from the first circuit then all moments from the second circuit. Circuit multiplication is repeated addition of the same circuit. While simple, these operations are very useful when designing circuits, especially variational circuits. (Note that pyQuil and QISKit also have circuit arithmetic.) In this case, one circuit can be dedicated to, say, state preparation, while the other contains the variational ansatz. The circuit to be executed then consists of the sum of the two.

In many other cases with variational algorithms, it is desirable to remove specific gates from a circuit. Cirq provides a simple built-in method to perform this task, demonstrated below.

In [17]:

```
"""Clear operations from a circuit."""
circ.clear_operations_touching(qbits[1:4], range(1, 4))
print(circ)
```

In [18]:

```
"""Insert operations into a circuit at specific locations."""
circ.batch_insert([(1, cirq.CZ(qbits[2], qbits[3])),
(2, cirq.CNOT(qbits[1], qbits[3]))])
print(circ)
```

Here we have inserted two-qubit gates (for clarity) into the region in which we previously removed gates. Although these abilities may appear to be contrived, operations of this form are particularly useful for programming variational quantum algorithms.

In addition to manipulating quantum circuits, there are many methods built in to circuits that return useful information. Among these are `all_operations`, `all_qubits`, and `next_moment_operating_on` (also `prev_moment_operating_on`). A full set of circuit methods returning information about the circuit or modifying the circuit in place can be found in the documentation. Lastly, we remark that a circuit can be turned into a unitary matrix by using the method `to_unitary_matrix`.

Because of Cirq’s strong support for variational quantum algorithms (in which parameters/angles of an algorithm are iteratively changed to minimize an energy or cost function), the platform provides useful features for working with these types of circuits. In particular, two components are particularly notable for this task: `Symbol`s and simulating/running “sweeps” of circuits, which we now elaborate on.

Rather than having to create a new quantum circuit for every new set of variables, Cirq allows gates with parameters to have `Symbol`s, which can be resolved by a `ParamResolver` with a given set of angles. In addition to cleaning up code, this decreases overall runtime of programs by avoiding the task of creating a new quantum circuit for every given set of angles. An example of creating a circuit with `Symbol`s is shown below.

In [19]:

```
"""Create a circuit that contains a symbol."""
# get a circuit and a qubit
circ = cirq.Circuit()
qbit = cirq.LineQubit(0)
# add a gate with a definite angle
circ.append([cirq.XPowGate(exponent=np.pi / 2)(qbit)])
# add a gate with a symbol which can take any value
sym = cirq.Symbol("t")
gate = cirq.XPowGate(exponent=sym)
circ.append([gate(qbit)])
# add a measurement
circ.append(cirq.measure(qbit, key="z"))
# show the circuit diagram
print(circ)
```

*t*, which we must instantiate with a value before trying to run a circuit. One way to do this is to explicitly use a `ParamResolver` to give *t* a value, as follows.

In [20]:

```
"""Run a parameterized circuit by resolving
the circuit with a ParamResolver.
"""
# get a param resolver
param_resolver = cirq.ParamResolver({sym.name: np.pi / 4})
# run the resolved circuit using the param resolver
res = simulator.run(circ, param_resolver, repetitions=500)
# plot the output distribution
vals = cirq.plot_state_histogram(res)
```

In [21]:

```
"""Sweep over a set of values to run a parameterized circuit at."""
# get a "sweep" of values
sweep = cirq.Linspace(key=sym.name, start=0, stop=np.pi, length=100)
# run the circuit at all values in the sweep
res = simulator.run_sweep(circ, sweep, repetitions=1000)
# plot the frequency of zero outputs for all values in the sweep
tvals = [x[0][1] for x in sweep.param_tuples()]
cvals = [res[i].histogram(key="z")[0]/1000 for i in range(len(res))]
plt.plot(tvals, cvals, "-o", linewidth=2)
# plot style
plt.grid(); plt.xlabel("t");
plt.ylabel("Frequency of 0 Measurement");
plt.title("Sweeping over parameters in a circuit");
```

*Quantum compiling* consists of rewriting a given algorithm in terms of gates a quantum computer can actually implement (a *native gate set* or simply *gate set*). Cirq has functionality to create a circuit for a particular `Device` (see *Devices and Schedules* below) and automatically compile gates into the native gate set. A similar problem is that of *quantum circuit optimization*, by which we mean rewriting a quantum circuit to contain as few gates as possible. For example, we would never implement two Pauli operations (*X*, *Y*, *Z*) in sequence because they square to the identity. Similar ideas apply to other gates. This task is critical for NISQ hardware as errors accumulate through gate application and decoherence throughout the algorithm.

Cirq provides several utilities for quantum circuit optimization, which we demonstrate below without specification to a native gate set. (The exact same functionality works for circuits tied to a `Device`.) First, we obtain a random circuit consisting of only single qubit gates for simplicity.

In [22]:

```
"""Get a random circuit."""
circ, qbits = random_circuit(4, 5)
print(circ)
```

The random circuit we obtained is shown above. Note that this function utilizes an `InsertStrategy` in Cirq, which “indicates preferences on how to add multiple operations to a circuit.” In the above example, we used `InsertStrategy.EARLIEST`, which tells the circuit to push gates being inserted as far to the left as possible. Other strategies include `INLINE` and `NEW_THEN_INLINE`.

*Z* gates to the end of the circuit.

In [23]:

```
"""Push Z gates toward the end of the circuit."""
ejectZ = cirq.optimizers.EjectZ()
ejectZ.optimize_circuit(circ)
print(circ)
```

*Z* operations now appear at the right-most portion of the circuit. Next we will perform a similar optimization pass, this time attempting to push *X*, *Y*, and `PhasedXPowGate`s to the right.

In [24]:

```
"""Push X, Y, and PhasedXPow gates toward the end of the circuit."""
ejectPaulis = cirq.optimizers.EjectPhasedPaulis()
ejectPaulis.optimize_circuit(circ)
print(circ)
```

In [25]:

```
"""Merge single qubit gates into PhasedX and PhasedZ gates."""
cirq.merge_single_qubit_gates_into_phased_x_z(circ)
print(circ)
```

In [26]:

```
"""Removes operations with a tiny effect and
drop empty moments in the circuit.
"""
# drop negligible gates
drop_neg = cirq.optimizers.DropNegligible()
drop_neg.optimize_circuit(circ)
# drop empty moments
drop_empty = cirq.optimizers.DropEmptyMoments()
drop_empty.optimize_circuit(circ)
print(circ)
```

*H*, *T*, etc., so the quantum circuit drawer simply shows matrix elements:

In [27]:

```
"""Merge single qubit gates."""
merge = cirq.optimizers.MergeSingleQubitGates()
merge.optimize_circuit(circ)
print(circ)
```

In [28]:

```
"""Run the optimized circuit and display its wavefunction in Dirac notation."""
res = simulator.simulate(circ)
print(res.dirac_notation())
```

The Dirac notation of the wavefunction above corresponds to the same wavefunction we would have obtained by simulating the original circuit. Here, we achieved the same effect with much fewer gates.

Of course, a given quantum computer cannot implement arbitrary gates like the simulator has done above. As mentioned, these gates need to be compiled into operations the computer can implement. Cirq provides this functionality with a `Device`, as we elaborate on below.

The existence of `Device`s and `Schedule`s again reflects Cirq’s emphasis on near-term quantum computing. Briefly, a `Device` corresponds to a quantum computer with given constraints such as qubit connectivity and native gate sets. A `Schedule` corresponds to the real-time implementation of operations on a `Device`. Users have full control to specify any quantum computer architecture using a `Device` and can program a circuit for any given quantum computer. Because the documentation contains detailed information on user-defined devices and schedules, we omit this discussion here. Instead, we provide information on `Device`s by analyzing one that is already defined in Cirq. Namely, the Foxtail quantum computer.

In the following code, we create a quantum circuit with its device set as Foxtail:

In [29]:

```
"""Make a quantum circuit whose device is the
22 qubit Foxtail quantum computer.
"""
# grab the device
foxtail = cirq.google.Foxtail
# get the qubits of foxtail
qbits = sorted(list(foxtail.qubits))
# make a circuit and set foxtail to be its device
circ = cirq.Circuit(device=foxtail)
```

In [30]:

```
"""Compile/decompose operations into the native gate set of Foxtail."""
# get the hadamard gate
hgate = cirq.H(qbits[0])
# compile/decompose the gate into native operations
compiled_hgate = foxtail.decompose_operation(hgate)
# print out the gate and its compilation
print(hgate, "=", *compiled_hgate)
```

Once we have compiled gates, we can append them into the circuit. Alternatively, if we add any gate operation into a circuit, they will get compiled automatically. Below we show this for the Bell state preparation circuit consisting of a Hadamard and CNOT gate.

In [31]:

```
"""Adding operations to a circuit with a device compiles them automatically."""
# cnot gate
cnot = cirq.CNOT(qbits[0], qbits[1])
# append the operations
circ.append([hgate, cnot])
# print the circuit
print(circ)
```

In [32]:

```
"""Attempt to optimize the circuit. Only commutes
Z across CZ for a single Bell state preparation circuit,
simplifies two sequential Bell state preparation circuits.
(Run the cell above twice to see this.)
"""
ejectZ.optimize_circuit(circ)
ejectPaulis.optimize_circuit(circ)
drop_neg.optimize_circuit(circ)
cirq.merge_single_qubit_gates_into_phased_x_z(circ)
print(circ)
```

In [33]:

```
"""See how long each native operation takes to implement."""
# grab the times of some native gates
ytime = foxtail.duration_of(cirq.Y(qbits[0]))
ztime = foxtail.duration_of(cirq.Z(qbits[0]))
cztime = foxtail.duration_of(cirq.CZ(qbits[0], qbits[1]))
# print them out
print("Y time =", ytime.total_nanos(), "nanoseconds.")
print("Z time =", ztime.total_nanos(), "nanoseconds.")
print("CZ time =", cztime.total_nanos(), "nanoseconds.")
```

*Y* takes 20 nanoseconds to implement (on any qubit), and the two-qubit gate Controlled-*Z* takes over twice as long to implement at 50 nanoseconds. The *Z* gate takes no time to implement because it is a “virtual” gate. That is, it is implemented by shifting the clock of the software keeping track of the qubit and not by any physical operation acting on the qubit like the *Y* or Controlled-*Z* gate.

To conclude this section, we briefly mention other notable features of Cirq.

In terms of library support, which we define to mean any built-in algorithms or examples demonstrating how to implement algorithms, several companies have used Cirq in collaboration with Google for various projects. For example, Zapata Computing used Cirq to implement a quantum autoencoder, QC Ware used Cirq for implementing QAOA, and Heisenberg Quantum Simulations used Cirq to simulate the Anderson model. The documentation of Cirq contains an in-depth tutorial of a variational quantum algorithm, and the examples folder on Cirq’s GitHub contains detailed Python scripts on Grover’s algorithm, the quantum Fourier Transform, the phase estimation algorithm, the Bernstein-Vazirani algorithm, and an example preparing BCS ground states for superconductors/superfluids.

Cirq also contains integration with OpenFermion, a hardware-agnostic library for simulating fermionic systems on quantum computers, through OpenFermion-Cirq. This library, also in alpha release, is focused on extending the functionality of OpenFermion “by providing routines and tools for using Cirq to compile and compose circuits for quantum simulation algorithms” (from the README).

Other notable features of the Cirq library are quantum channels and stabilizers. These features are still labeled “work in progress” in the documentation, so for fairness. we do not delve into these topics but rather note them as interesting and useful features once they are completed.

In this final section, we look at example algorithms programmed in Cirq.

*|Ψ>* (which we take to be *|1> = X|0>* for example below) from one person, Alice, to another, Bob, without either one knowing what the actual state is. More complete descriptions of this protocol can be found in any standard source on quantum computing, for example the ones listed here.

In [34]:

```
"""Quantum teleportation algorithm written in Cirq."""
# get qubits and a circuit
qbits = [cirq.LineQubit(x) for x in range(3)]
circ = cirq.Circuit()
# Alice wants to teleport |1> to Bob
circ.append(cirq.ops.X(qbits[0]))
# Bell state preparation on Bob's qubits
circ.append([cirq.ops.H(qbits[1]),
cirq.ops.CNOT(qbits[1], qbits[2])])
# Bell state measurement on Alice's qubits
circ.append([cirq.ops.H(qbits[0]),
cirq.ops.CNOT(qbits[0], qbits[1]),
cirq.measure(qbits[0]),
cirq.measure(qbits[1])])
# print out the circuit
print(circ)
```

In [35]:

```
"""Verify the state of the third qubit is |1> using the simulate methods."""
# simulate the circuit with access to the wavefunction
res = simulator.simulate(circ)
# print out the density matrix, which should be
# [[0, 0],
# [0, 1]]
print(res.density_matrix([2]))
```

To within floating point precision, this is exactly the density matrix we expect.

We now show an example of a variational quantum-classical algorithm using Cirq. The algorithm shown, called Variational Quantum State Diagonalization (VQSD), is used to diagonalize a quantum state (more generally, a positive semidefinite matrix) and was introduced in [La2018]. Without going into all details in the paper, the algorithm works by taking as input two copies of a quantum state *ρ*. For our simple illustrative example, we will use a one qubit state *ρ = |+><+|* which can be prepared by performing a Hadamard gate on each qubit at the start of the algorithm, *|+> := H|0>*. (Generally, VQSD assumes the state is prepared from some efficient process, a notable example being quantum simulation, for which VQSD can be used for entanglement spectroscopy and other condensed matter applications) Next, the algorithm implements a variational ansatz (or structure of the circuit), which we take to be *U(t) = R _{z}(π/2) X^{t} R_{z}(π/2)* where

In general for variational algorithms, a (classical) optimization algorithm can be used to find the best set of parameters for the ansatz. Here, we simply sweep over the parameter *t* in our ansatz and plot the results to find the parameter that maximizes the measure of “diagonality” of *ρ’ := UρU†*. The complete program is shown below.

In [36]:

```
"""Implementation of the VQSD algorithm [La2018]
on a simple one qubit state.
"""
# ====================
# get number of qubits
# ====================
n = 2
qbits = [cirq.LineQubit(x) for x in range(n)]
# =========================
# state preparation circuit
# =========================
prep = cirq.Circuit()
prep.append(cirq.H(qbits[x]) for x in range(n))
# ==============
# ansatz circuit
# ==============
ansatz = cirq.Circuit()
gate0 = cirq.Rz(rads=np.pi / 2)
gate1 = cirq.XPowGate(exponent=cirq.Symbol("t"))
ansatz.append([[gate0(qbits[x]),
gate1(qbits[x]),
gate0(qbits[x])] for x in range(2)],
strategy=cirq.InsertStrategy.EARLIEST)
# ==========================
# "diagonality" test circuit
# ==========================
diag = cirq.Circuit()
diag.append([cirq.CNOT(qbits[1], qbits[0]),
cirq.measure(qbits[0], key="z")])
# ================
# complete circuit
# ================
circ = prep + ansatz + diag
# =========================
# sweep over the parameters
# =========================
reps = 10000
simulator = cirq.google.XmonSimulator()
sweep = (cirq.Linspace(key="t", start=0, stop=1, length=100))
vals = simulator.run_sweep(circ, sweep, repetitions=reps)
# print out the circuit
print(circ)
```

*t*, which ranges from zero to one, in the plot below.

In [37]:

```
# plot the degree of diagonality vs. the parameter t
tvals = [x[0][1] for x in sweep.param_tuples()]
cvals = [vals[i].histogram(key="z")[0]/reps for i in range(len(vals))]
plt.plot(tvals, cvals, "-o", linewidth=2)
# plot style
plt.grid(); plt.xlabel("t");
plt.ylabel("Degree of Diagonality");
plt.title("Diagonalizing a one-qubit state with VQSD");
```

*ρ’ := U(t) ρ U(t)†* is the most diagonal at the value of *t = 1/2*. A simple classical calculation using matrix arithmetic shows that *ρ’* is indeed diagonal in the computational basis when *t = 1/2*.

In this final section we offer concluding remarks about Cirq. The purpose of this article was to introduce and analyze Cirq in a similar fashion to the paper [LaR2018] comparing Forest (pyQuil), QISKit, ProjectQ, and the Quantum Developer Kit (Q#). We hope at this point the reader will be able to come to his/her own conclusions about Cirq’s place in the quantum software community. (Recalling again that Cirq is still alpha software.)

In general, we find that Cirq is already a good tool for writing, manipulating, optimizing, compiling, and simulating quantum circuits. For beginners, the learning curve for Cirq is small as the language is written in Python and the syntax/organization of the code is very clean. (We do note that Cirq is written with function annotations in Python which in several cases can be rather verbose.) For experienced users, Cirq exposes details of the hardware to the programmer to maximize effective utilization of near-term processors.

We expect the platform to keep improving as quantum hardware and other simulators are made available. We find the tools for working with variational quantum algorithms, including local simulators as opposed to cloud-based simulators, to be one of the best features of Cirq. (It should be noted that Forest has emphasis on this feature as well, but currently the functionality is smoother with Cirq in the author’s opinion.) The integration with OpenFermion will likely lead to one of the best tools for quantum simulation algorithms, competing with IBM’s QISKit Aqua library. The simulators of Cirq are both good utilities (though not as high-performance as other simulators such as in QISKit Aer or ProjectQ) and will get better with the ongoing work on noise modeling. Additionally, Finally, we expect the quantum channel and stabilizer code features of Cirq to be interesting and useful once they are released.

As mentioned, probably the biggest missing feature of Cirq is the ability to simulate noisy quantum circuits, which Forest and QISKit do very well. (We expect this feature to be released in future versions.) As was seen in the quantum teleportation algorithm, the ability to implement classical operations in circuits, a feature emphasized by the Forest platform, is entirely missing in Cirq. Additionally, the documentation, tutorials, and library support of Cirq are much more sparse than Forest, QISKit, the QDK, and even ProjectQ. (Again, the alpha disclaimer is important here.) Currently, users of Cirq will not be able to connect to real quantum computers as they would with Forest, QISKit, or ProjectQ. Once Foxtail and/or Bristlecone are made available over the cloud, however, this will of course change.

The addition of Cirq to the quantum software community provides excellent tools for working with circuits for near-term quantum computers, and will soon offer connectivity to some of the world’s most advanced quantum computers. We hope that this article provides a fair introduction and overview of the platform. For readers who wish to explore Cirq further, we recommend the API Reference of the documentation.

[An2018] E. R. Anschuetz, J. P. Olson, A. Aspuru-Guzik, and Y. Cao, “Variational Quantum Factoring,” arXiv:1808.08927 [quant-ph], Aug. 2018.

[Du2018] Dumitrescu, E. F. et al. Cloud Quantum Computing of an Atomic Nucleus. Physical Review Letters 120, (2018).

[Fa2014] E. Farhi, J. Goldstone, and S. Gutmann, “A Quantum Approximate Optimization Algorithm,” arXiv:1411.4028 [quant-ph], Nov. 2014.

[Fi2018] M. Fingerhuth, T. Babej, and P. Wittek, “Open source software in quantum computing,” PLOS ONE, vol. 13, no. 12, p. e0208561, Dec. 2018.

[Ho2018] A. Ho and D. Bacon, “Announcing Cirq: An Open Source Framework for NISQ Algorithms,” https://ai.googleblog.com/2018/07/announcing-cirq-open-source-framework.html, Jul. 2018.

[Kh2018] S. Khatri, R. LaRose, A. Poremba, L. Cincio, A. T. Sornborger, and P. J. Coles, “Quantum-assisted quantum compiling,” arXiv:1807.00800 [quant-ph], Jul. 2018.

[La2018] R. LaRose, A. Tikku, É. O’Neel-Judy, L. Cincio, and P. J. Coles, “Variational Quantum State Diagonalization,” arXiv:1810.10506 [quant-ph], Oct. 2018.

[LaR2018] R. LaRose, “Overview and Comparison of Gate Level Quantum Software Platforms,” arXiv:1807.02500 [quant-ph], Jul. 2018.

[Ma2018] I. L. Markov, A. Fatima, S. V. Isakov, and S. Boixo, “Quantum Supremacy Is Both Closer and Farther than It Appears,” arXiv:1807.10749 [quant-ph], Jul. 2018.

[Ne2017] C. Neill et al., “A blueprint for demonstrating quantum supremacy with superconducting qubits,” Sep. 2017.

[Pe2014] A. Peruzzo et al., “A variational eigenvalue solver on a quantum processor,” Nature Communications, vol. 5, no. 1, Dec. 2014.

[Wa2018] Z. Wang, S. Hadfield, Z. Jiang, and E. G. Rieffel, “The Quantum Approximation Optimization Algorithm for MaxCut: A Fermionic View,” Physical Review A, vol. 97, no. 2, Feb. 2018.