#
 Practical Quantum Computing with Cirq

 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 featured 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.

#
I. Introduction

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:

Schematic diagram showing quantum computing companies and their position in the quantum software stack. "Full-stack providers" focus on all areas of the stack whereas "target providers" focus on particular areas. Because companies that do algorithms/applications frequently do software as well, we have combined these two categories on the right. (Similarly for hardware & compilation and control.)

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:

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

##
 A. Format of the Article

This article was written as a Jupyter Notebook and exported as an HTML file. The HTML file is hosted on the Quantum Computing Report website and the Jupyter notebook is hosted on GitHub. In the HTML version, all code and all outputs are visible in the article, but the code is not able to run. To interactively run the code while reading through the article, see the Jupyter Notebook version. In order to run the code, a working installation of Cirq is required (see 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.

#
 II. The Basics of Cirq

    Institution

    Version

    GitHub
    Documentation
    OS
    Requirements
    Classical Language
    Quantum Language
    Quantum Hardware
    Simulator

    Google Quantum AI
    v0.4.0
    Git
    Docs
      Mac, Windows, Linux
      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 components of Cirq. When installed onto a computer, Cirq provides a library for working with quantum circuits and a high-performance local quantum circuit simulator. As of December 2018, connection to hardware devices or the "Quantum Engine"/"Quantum Cloud Services" is unavailable to general users, but this is expected to change in the future.

##
 A. Installation

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.

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

##
 B. Documentation and Tutorials

The documentation for Cirq contains instructions on installation for all three major operating systems, an in-depth tutorial for the variational quantum eigensolver, and details on three major components of the Cirq library: circuits, gates, and simulation. In addition, the section on 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.

##
 C. Language Syntax

As in our previous coverage of quantum software platforms, we include example programs to demonstrate the language syntax. Below, we implement the "quantum random bit generator" algorithm, which produces either zero or one output that is random by the laws of quantum mechanics. To use the functionality of Cirq, we first import the library (and additional libraries we'll use throughout the article).

```python
"""Library imports for the article."""
import cirq

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
```

In what follows we create a circuit and instantiate it with the operations for the algorithm (Hadamard and measure).

```python
"""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:

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

    0: ───H───M('z')───

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.

##
 D. Quantum Computers

Cirq does not currently provide cloud access to Google's quantum computers for general users. Indeed, as per the documentation in Cirq's engine class:

"In order to run on[e] must have access to the Quantum Engine API. Access to this
API is (as of June 22, 2018) restricted to invitation only."

Nonetheless, it is known that Google has quantum computers that have been stated to be made available over the cloud in the near future [Ho2018], using Cirq as an interface. Indeed, Cirq already provides details on these devices. For instance, the architecture of the 22-qubit FoxTail computer can be printed out in Cirq by doing:

```python
"""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)
```

    FoxTail has 22 qubits arranged as follows:

    (0, 0)───(0, 1)───(0, 2)───(0, 3)───(0, 4)───(0, 5)───(0, 6)───(0, 7)───(0, 8)───(0, 9)───(0, 10)
    │        │        │        │        │        │        │        │        │        │        │
    │        │        │        │        │        │        │        │        │        │        │
    (1, 0)───(1, 1)───(1, 2)───(1, 3)───(1, 4)───(1, 5)───(1, 6)───(1, 7)───(1, 8)───(1, 9)───(1, 10)

The 72-qubit Bristlecone computer, with which quantum supremacy may be demonstrated on, can be displayed similarly:

```python
"""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)
```

    Bristlecone has 72 qubits arranged as follows:

                                                 (0, 5)────(0, 6)
                                                 │         │
                                                 │         │
                                        (1, 4)───(1, 5)────(1, 6)────(1, 7)
                                        │        │         │         │
                                        │        │         │         │
                               (2, 3)───(2, 4)───(2, 5)────(2, 6)────(2, 7)───(2, 8)
                               │        │        │         │         │        │
                               │        │        │         │         │        │
                      (3, 2)───(3, 3)───(3, 4)───(3, 5)────(3, 6)────(3, 7)───(3, 8)───(3, 9)
                      │        │        │        │         │         │        │        │
                      │        │        │        │         │         │        │        │
             (4, 1)───(4, 2)───(4, 3)───(4, 4)───(4, 5)────(4, 6)────(4, 7)───(4, 8)───(4, 9)───(4, 10)
             │        │        │        │        │         │         │        │        │        │
             │        │        │        │        │         │         │        │        │        │
    (5, 0)───(5, 1)───(5, 2)───(5, 3)───(5, 4)───(5, 5)────(5, 6)────(5, 7)───(5, 8)───(5, 9)───(5, 10)───(5, 11)
             │        │        │        │        │         │         │        │        │        │
             │        │        │        │        │         │         │        │        │        │
             (6, 1)───(6, 2)───(6, 3)───(6, 4)───(6, 5)────(6, 6)────(6, 7)───(6, 8)───(6, 9)───(6, 10)
                      │        │        │        │         │         │        │        │
                      │        │        │        │         │         │        │        │
                      (7, 2)───(7, 3)───(7, 4)───(7, 5)────(7, 6)────(7, 7)───(7, 8)───(7, 9)
                               │        │        │         │         │        │
                               │        │        │         │         │        │
                               (8, 3)───(8, 4)───(8, 5)────(8, 6)────(8, 7)───(8, 8)
                                        │        │         │         │
                                        │        │         │         │
                                        (9, 4)───(9, 5)────(9, 6)────(9, 7)
                                                 │         │
                                                 │         │
                                                 (10, 5)───(10, 6)

The Foxtail and Bristlecone processors implement a Controlled-$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:
\begin{equation}
C(Z) = \left[ \begin{matrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & -1 \\
\end{matrix} \right] .
\end{equation}

It should be noted that Cirq provides built-in functionality to convert its circuits to OpenQASM code:

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

    // Generated from Cirq v0.4.0

    OPENQASM 2.0;
    include "qelib1.inc";

    // Qubits: [0]
    qreg q[1];
    creg m_z[1];

    h q[0];
    measure q[0] -> m_z[0];

This functionality makes it very simple to run algorithms generated in Cirq on IBM's quantum computers. (For instance, by navigating to the IBM Q Experience website and using the online QASM editor. See the Figure below.)

Screenshot from the IBM Q Experience online QASM Editor using Cirq to generate QASM code. The code input into the editor is the same code obtained above by executing `circ.to_qasm()`. By selecting "run" or "simulate" on the IBM Q Experience website, one can execute the quantum algorithm on a real or simulated quantum computer, respectively.

##
 E. Quantum Simulators

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:

```python
"""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)
```

    Counter({1: 31, 0: 19})

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:

```python
"""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.

###
 1. Performance of the Simulators

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.

```python
"""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)
```

    Circuit structure shown below:
    0: ───Rx(1.924π)───Ry(0.123π)───Rz(0.784π)───X───────M───────
                                                 │
    1: ───Rx(0.338π)───Ry(1.188π)───Rz(1.708π)───┼───X───M───────
                                                 │   │
    2: ───Rx(0.673π)───Ry(0.136π)───Rz(0.596π)───@───@───@───M───
                                                         │
    3: ───Rx(1.692π)───Ry(0.949π)───Rz(1.988π)───────────X───M───

    It took 0.03 seconds to run the circuit above.

For such a small circuit, the runtime is almost instantaneous. As we scale to larger and larger circuits, however, the complexity of simulating the circuit increases. Below we up the scale to 20 qubits with a depth of 10.

```python
"""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)
```

    It took 29.53 seconds to run the circuit.

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.

Plots showing the performance of the `Simulator` (top) and `XmonSimulator` (bottom). The horizontal axis shows the number of qubits and the vertical axis shows the depth as defined above. Dark green represents faster simulation time and bright yellow represents longer simulation time. Values in each square show total overall runtime in seconds, averaged over many runs. Color scales are different for each plot.

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.

```python
"""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))
```

                  ┌──┐
    (0, 5): ───H───T────@───────Y^0.5───T────────────────────H───
                        │
    (0, 6): ───H───T────@───────@───────Y^0.5────────T───────H───
                                │
    (1, 5): ───H───T────────────┼───────@────────────@───────H───
                                │       │            │
    (1, 6): ───H───@────Y^0.5───@───────┼────X^0.5───@───────H───
                   │                    │
    (2, 5): ───H───┼T───@───────X^0.5───@────────────Y^0.5───H───
                   │    │
    (2, 6): ───H───@────@───────X^0.5───T────────────────────H───
                  └──┘

(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.

###
 2. Features of the Simulators

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.

```python
"""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.

```python
"""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)
```

    0: ───H───@───
              │
    1: ───────X───

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.

```python
"""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]))
```

    The wavefunction of the final state is:
     [-3.0908623e-08+0.7071068j  2.5716280e-16+0.j
     -3.0908616e-08+0.j         6.1817239e-08+0.7071068j]

    The Dirac notation for the final state is:
     (0.71j)|00⟩ + (0.71j)|11⟩

    The density matrix of the final state is:
     [[ 5.0000006e-01+0.0000000e+00j -7.9485483e-24+1.8184158e-16j
       9.5534273e-16-2.1855694e-08j  5.0000006e-01+6.5567093e-08j]
     [-7.9485483e-24-1.8184158e-16j  6.6132707e-32+0.0000000e+00j
      -7.9485467e-24-0.0000000e+00j  1.5897095e-23-1.8184158e-16j]
     [ 9.5534273e-16+2.1855694e-08j -7.9485467e-24+0.0000000e+00j
       9.5534252e-16+0.0000000e+00j -1.9106853e-15+2.1855694e-08j]
     [ 5.0000006e-01-6.5567093e-08j  1.5897095e-23+1.8184158e-16j
      -1.9106853e-15-2.1855694e-08j  5.0000006e-01+0.0000000e+00j]]

    The reduced density matrix of the first qubit is:
     [[5.0000006e-01+0.0000000e+00j 9.5534273e-16-2.1855694e-08j]
     [9.5534273e-16+2.1855694e-08j 5.0000006e-01+0.0000000e+00j]]

#
 III. Features of Cirq

In the previous section, we looked at features of the quantum computer simulators of Cirq. In this section we discuss notable features of the entire Cirq platform, including methods for manipulating, parameterizing, optimizing, and compiling circuits. Since each "full-stack" software platform has the same goal (programming a quantum computer), the features of each are what set them apart. Our coverage is not comprehensive (see the documentation for all features) but consists of features we find particularly notable or useful.

##
 A. Manipulating Circuits

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.

```python
"""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)
```

    0: ───H───────Z───Z───────H───Z───────

    1: ───X^0.5───T───X───────Y───T───────

    2: ───H───────T───X^0.5───Z───X^0.5───

    3: ───X^0.5───H───H───────X───H───────

    4: ───H───────X───X^0.5───X───Z───────

Circuits are stored as iterables of _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:

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

    H(0) and X**0.5(1) and H(2) and X**0.5(3) and H(4)
    Z(0) and T(1) and T(2) and H(3) and X(4)
    Z(0) and X(1) and X**0.5(2) and H(3) and X**0.5(4)
    H(0) and Y(1) and Z(2) and X(3) and X(4)
    Z(0) and T(1) and X**0.5(2) and H(3) and Z(4)

This output tells us exactly what we see in the circuit diagram above as we move from left to right in the circuit.

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.

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

    0: ───H───────Z───Z───────H───Z───────

    1: ───X^0.5───────────────────T───────

    2: ───H───────────────────────X^0.5───

    3: ───X^0.5───────────────────H───────

    4: ───H───────X───X^0.5───X───Z───────

In the same manner, Cirq contains methods for inserting operations into a circuit, such as `batch_insert`, which inserts a sequence of (location, operation) pairs into the circuit, as demonstrated below.

```python
"""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)
```

    0: ───H───────Z───Z───────H───Z───────

    1: ───X^0.5───────@───────────T───────
                      │
    2: ───H───────@───┼───────────X^0.5───
                  │   │
    3: ───X^0.5───@───X───────────H───────

    4: ───H───────X───X^0.5───X───Z───────

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`.

##
 B. Parameterizing Circuits

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.

```python
"""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)
```

    0: ───X^(-3/7)───X^t───M('z')───

When we print out the circuit diagram, we see that we have one gate with a definite parameter (the first gate) and another with a symbol $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.

```python
"""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)
```

With this method, we only have to change the value of the `ParamResolver` to change the circuit. By looping over many values, we can find the minimum of some cost function defined for a variational algorithm. A slightly simplified way to do this in Cirq is by using the "sweep" methods, e.g. `run_sweep` or `simulate_sweep`, to loop over these values automatically. A simple example of doing so using the same circuit above is as follows.

```python
"""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");
```

##
 C. Optimizing Circuits

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.

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

print(circ)
```

    0: ───H───X───Y───────H───X───

    1: ───H───X───Z───────Z───X───

    2: ───Z───T───T───────T───Z───

    3: ───Z───Y───X^0.5───T───Z───

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`.

The first optimization pass we will try is `EjectZ`, which rewrites the circuit by attempting to push all $Z$ gates to the end of the circuit.

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

    0: ───H────────X───Y────────H───X───

    1: ───H────────X────────────────X───

    2: ───Z^0.75────────────────────────

    3: ────────────Y───X^-0.5───T───────

As we can see, all $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.

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

print(circ)
```

    0: ───H────────Z───────H─────────────────X───

    1: ───H──────────────────────────────────────

    2: ───Z^0.75─────────────────────────────────

    3: ────────────X^0.5───PhasedX(-0.375)───────

Another circuit optimization provided by Cirq is merging all single qubit gates into `PhasedXPow` and `PhasedZPow` gates, which we demonstrate below.

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

print(circ)
```

    1: ───Y^-0.5───Z──────────────────────

    2: ───Z^0.75──────────────────────────

    3: ────────────────X^-0.5───Z^-0.75───

We see that the entire circuit is now expressed in terms of these gates. As a last touch, we can attempt to drop any gates that have a "negligible" (i.e., very small) effect on the overall circuit and drop empty `moment`s in the circuit.

```python
"""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)
```

    1: ───Y^-0.5───Z──────────────────────

    2: ───Z^0.75──────────────────────────

    3: ────────────────X^-0.5───Z^-0.75───

Cirq also allows single qubit gates to be merged into arbitrary two by two unitary transformations. These may not in general have a specified name like $H$, $T$, etc., so the quantum circuit drawer simply shows matrix elements:

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

print(circ)
```

    1: ───┌                   ┐───────────────────────────────────────────
          │ 0.5-0.5j  0.5-0.5j│
          │ 0.5-0.5j -0.5+0.5j│
          └                   ┘

    2: ───┌                           ┐───────────────────────────────────
          │ 1.   +0.j     0.   +0.j   │
          │ 0.   +0.j    -0.707+0.707j│
          └                           ┘

    3: ───────────────────────────────────┌                           ┐───
                                          │ 0.5  -0.5j    0.5  +0.5j  │
                                          │ 0.   -0.707j -0.707-0.j   │
                                          └                           ┘

We note that optimized circuits can be run/simulated with either the `Simulator` or `XmonSimulator` and that optimizing circuits before simulating them may result in improved performance. Below we simulate the above optimized circuit and display its wavefunction in Dirac notation.

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

    (0.5j)|000⟩ + (0.35+0.35j)|001⟩ + (0.5j)|100⟩ + (0.35+0.35j)|101⟩

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.

##
 D. Devices and Schedules

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:

```python
"""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)
```

Once we grab the `Foxtail` device, we can get its qubits and then create a circuit. We can use the method `decompose_operation` of a `Device` to compile/decompose a gate operation. Below we show this for the example of the Hadamard gate.

```python
"""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)
```

    H((0, 0)) = Y**-0.5((0, 0)) Z**-1.0((0, 0))

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.

```python
"""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)
```

    (0, 0): ───Y^-0.5───Z───────────────────@──────────────────────
                                            │
    (0, 1): ────────────PhasedX(-0.5)^0.5───@───PhasedX(0.5)^0.5───

```python
"""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)
```

    (0, 0): ───Y^-0.5───────────────────────@───Z──────────────────
                                            │
    (0, 1): ────────────PhasedX(-0.5)^0.5───@───PhasedX(0.5)^0.5───

Here we can see the native operations of Foxtail (which are the same for Bristlecone). When running the circuit, these are the gates the computer actually implements. Another useful feature of `Device`s is that they contain timing information for each of the native gates. (When programming a user-defined `Device`, these values can be specified to match an arbitrary computer.) To see this information, we can use the `duration_of` method to see how long each native gate takes to implement.

```python
"""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 time = 20.0 nanoseconds.
    Z time = 0.0 nanoseconds.
    CZ time = 50.0 nanoseconds.

Here we see that the single qubit gate $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.

##
 E. Other Features

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.

#
 IV. Example Algorithms

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

##
 A. Quantum Teleportation Algorithm

For consistency with our comparison of pyQuil, QISKit, ProjectQ, and the Quantum Developer Kit [LaR2018], we include a complete program for the quantum teleportation algorithm below. This algorithm teleports an arbitrary quantum state $|\psi\rangle$ (which we take to be $|1\rangle = X |0\rangle$ 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.

```python
"""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)
```

    0: ───X───────────H───@───M───
                          │
    1: ───────H───@───────X───M───
                  │
    2: ───────────X───────────────

To the author's knowledge, there does not exist an easy way to perform classical conditional operations, such as those required by the quantum teleportation algorithm, in Cirq. However, we can use the "simulate" methods of Cirq to get the state of the third qubit, as shown below. Note that both measurement outcomes are deterministically zero in this situation, so no conditional operations are required.

```python
"""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]))
```

    [[ 9.9999988e-01+0.0000000e+00j -3.1794184e-23+3.6368302e-16j]
     [-3.1794184e-23-3.6368302e-16j  1.3226535e-31+0.0000000e+00j]]

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

##
 B. Variational Algorithm

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 $\rho$. For our simple illustrative example, we will use a one qubit state $\rho = |+\rangle \langle +|$ which can be prepared by performing a Hadmard gate on each qubit at the start of the algorithm, $|+\rangle := H |0\rangle$. (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(\pi / 2) X^t R_z(\pi / 2)$ where $t$ is the only parameter. Finally, a measure of "diagonality" of the state after the ansatz circuit has been applied is computed. For our case, this amounts to a single CNOT gate and a measurement in the computational basis.

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 $\rho' := U \rho U^\dagger$. The complete program is shown below.

```python
"""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)
```

    0: ───H───Rz(0.5π)───X^t───Rz(0.5π)───X───M('z')───
                                          │
    1: ───H───Rz(0.5π)───X^t───Rz(0.5π)───@────────────

The complete circuit for the VQSD algorithm is shown above. (Note that this can be easily simplified to contain only one qubit, but here we stick to the general formulation of the algorithm.) We now plot the "degree of diagonality" vs. the parameter $t$, which ranges from zero to one, in the plot below.

```python
# 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");
```

Here we can see that the state $\rho' := U(t) \rho U(t)^\dagger$ is the most diagonal at the value of $t = 1/2$. A simple classical calculation using matrix arithmetic shows that $\rho'$ is indeed diagonal in the computational basis when $t = 1/2$.

#
 V. Comparisons and Conclusions

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 fantastic 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.

#
 Acknowledgements

The author thanks Doug Finke from Quantum Computing Report for many useful comments. We both thank Alan Ho from Google for reviewing the article and providing feedback.

#
 References

[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.


# Design
###### Created by [Roberto Mora](https://github.com/romogo17)

I will be structuring the `pgcv` datatypes and functions in different PostgreSQL schemas. This schemas will group the operations logically. The structure is somewhat base on OpenCV.

The schemas will be:
1. `pgcv_core`
1. `pgcv_io`
1. `pgcv_filter`
1. `pgcv_histogram`
1. `pgcv_measure`
1. `pgcv_bundle`
1. `pgcv_draw` _(not yet implemented)_

## `pgcv_core`

This schema contains the datatypes used in the `pgcv` extension. These datatypes are basically a representation of a N-dimension array in PostgreSQL. The idea is to map the `numpy.ndarray` to theese structures.

The `regionprops` data type is used to to store information about the properties of the segmented labeled image. This allows the user to query specific features of the object found in the image.

```SQL
CREATE TYPE pgcv_core.ndarray_int4 AS (
  shape   int[],
  data    int[]
);

CREATE TYPE pgcv_core.regionprops AS (
  label         int,
  area          int,
  perimeter     float,
  centroid      float[2],
  solidity      float,
  eccentricity  float,
  convex_area   int,
  circularity   float,
  orientation   float,
  bbox          int[4]
);
```

On the other hand, there are some functions that were incorporated into the core of the extension. For instance `hash_avg` calculates the average hash of an image

```SQL
CREATE OR REPLACE FUNCTION pgcv_core.hash_avg(image pgcv_core.ndarray_int4, size int DEFAULT 8)
  RETURNS varchar
```

---
These functions can be called with the following code

```SQL
-- having an image in the database (size defaults to 8)
SELECT pgcv_core.hash_avg(<image>, [<size>]);

-- having an image in the database,
-- and the size of the thumbnail (size defaulta to 200x200)
SELECT pgcv_core.thumbnail(<image>, [<width>, [<height>]])

-- having an image in the database,
-- and the size of the thumbnail (size defaulta to 200x200)
SELECT pgcv_core.thumbnail_uri_base64(<image>, [<width>, [<height>]])

-- having an image in the database
SELECT pgcv_core.uri_base64(<image>)
```

```python
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

filename = 'data/mdb147lx'

'''
With the extension
'''
# img = np.array(image["data"]).reshape(image["shape"]).astype('uint8')
# img = Image.fromarray(img)

'''
With a file
'''
size = 200, 200
img = Image.open(filename + '.png')

'''
Create a thumbnail for the image
'''
img.thumbnail(size)
img.save(filename + ".thumbnail.jpg", "JPEG")
plt.imshow(img, cmap = plt.get_cmap('gray'))
print("Thumbnail of size 200x200")

# '''
# Convert the image to base64 data URI
# '''
# import base64
# from io import BytesIO

# buffered = BytesIO()
# img.save(buffered, format="PNG")
# img_str = base64.b64encode(buffered.getvalue())

# print('data:image/png;base64,' + img_str.decode("utf-8"))

```

## `pgcv_io`

This schema contains the image input and output functions to the filesystem. Meaning that this functions read and write images into files.

```SQL
CREATE OR REPLACE FUNCTION pgcv_io.image_read(filename varchar)
  RETURNS pgcv_core.ndarray_int4

CREATE OR REPLACE FUNCTION pgcv_io.image_write(image pgcv_core.ndarray_int4, filename varchar)
  RETURNS boolean
```
---
These functions can be tested with the following code

```SQL
-- having a filename that represents an image
SELECT shape FROM pgcv_io.image_read('<filename>');

-- having an image in the database and the output filename
SELECT pgcv_io.image_write(<image>, '<filename>');
```

## `pgcv_filtering`

This schema contains the image filtering functions. One example of this functions is the `median_blur` which replaces each pixel by the median of a local window array given by a kernel size.

```SQL
CREATE OR REPLACE FUNCTION pgcv_filter.blur_median(image pgcv_core.ndarray_int4, kernel int DEFAULT 3)
  RETURNS pgcv_core.ndarray_int4

CREATE OR REPLACE FUNCTION pgcv_filter.threshold_otsu(image pgcv_core.ndarray_int4)
  RETURNS float
```

The next function uses a method designed by **Johnny Villalobos** that has proven to be quite effective for mammogram segmentation. It is described follows:

Let $t$ be the _threshold_ of an image calculated through the Otsu's method and $f$ the enhancement factor so that

$$f = \frac{t}{255 - t}$$

the value of each enhanced pixel $p'$ corresponds to

\begin{equation}
\begin{split}
p'  &= (255 - p (1 + f)) - f(255 - p (1 + f)) \\
    &= (1 - f) (255 - p (1 + f))
\end{split}
\end{equation}

```SQL
CREATE OR REPLACE FUNCTION pgcv_filter.enhancement_otsu(image pgcv_core.ndarray_int4)
  RETURNS pgcv_core.ndarray_int4

CREATE OR REPLACE FUNCTION pgcv_filter.binarize(image pgcv_core.ndarray_int4, thresh float)
  RETURNS pgcv_core.ndarray_int4
```
---
These functions can be tested with the following code

```SQL
-- having an image in the database and odd kernel size (kernel size defaults to 5 if not specified)
SELECT pgcv_filter.median_blur(<image>, [<kernel size>]);

-- having an image in the database
SELECT pgcv_filter.threshold_otsu(<image>);

-- having an image in the database
SELECT pgcv_filter.enhancement_otsu(<image>);

-- having an image in the database and a threshold value
SELECT pgcv_filter.binarize(<image>, <threshold>);
```

## `pgcv_histogram`

This schema contains the histogram computing functions. There are two main kinds of histograms in `pgcv`, both return an histogram and a set of bin features (either the center of the bins or the edges)

```SQL
CREATE OR REPLACE FUNCTION pgcv_histogram.hist_bin_edges(image pgcv_core.ndarray_int4, bins int DEFAULT 10, as_float boolean DEFAULT TRUE,
  OUT hist numeric[], OUT bin_edges numeric[])

CREATE OR REPLACE FUNCTION pgcv_histogram.hist_bin_centers(image pgcv_core.ndarray_int4, bins int DEFAULT 10,
  OUT hist numeric[], OUT bin_centers numeric[])
```

---
These functions can be tested with the following code

```SQL
-- having an image in the database, the number of bins (bins defaults to 10 if not specified) and whether the histogram has to be normalized or not
SELECT * FROM pgcv_histogram.hist_bin_edges(<image>, [<bins>, [<as_float>]]);

-- having an image in the database and the number of bins (bins defaults to 10 if not specified)
SELECT * FROM pgcv_histogram.hist_bin_centers(<image>, [<bins>]);
```

## `pgcv_measure`

This schema contains the functions that perform measure computations on the image. In particular, `pgcv_measure` includes de region properties functions, which find objects on a binarize image

```SQL
CREATE OR REPLACE FUNCTION pgcv_measure.region_props_json(image pgcv_core.ndarray_int4)
  RETURNS jsonb

CREATE OR REPLACE FUNCTION pgcv_measure.region_props(image pgcv_core.ndarray_int4)
  RETURNS SETOF pgcv_core.regionprops
```

---
These functions can be tested with the following code

```SQL
-- having a binarized image in the database
SELECT pgcv_measure.region_props_json(<image>);

-- having a binarized image in the database
-- this allows for the inclusion of WHERE conditions
-- for filter the properties
SELECT * FROM pgcv_measure.region_props(<image>);
```

## `pgcv_bundle`

The bundle schema provides access to common successive operations performed to an image. The purpose of this schema is to reduce the overhead produced by the comunication from the PostgreSQL server and Python.

This functions operate on unaltered images, meaning, images in the PostgreSQL server but not yet processed.

```SQL
CREATE OR REPLACE FUNCTION pgcv_bundle.mam_region_props(image pgcv_core.ndarray_int4, kernel int DEFAULT 5)
  RETURNS SETOF pgcv_core.regionprops
```

---

These functions can be tested with the following code

```SQL
-- having an image in the database and odd kernel size (kernel size defaults to 5 if not specified)
SELECT * FROM pgcv_bundle.mam_region_props(<image>, [<kernel size>]);
```

```python
from PIL import Image
import matplotlib.pyplot as plt

import numpy as np
import pandas as pd
from scipy import signal
from skimage import measure, filters
import json

img = Image.open('./data/mdb155.pgm')
#img = Image.open('/Users/ro/Desktop/prueba.png')
img = np.array(img).astype('uint8')

"""
1. Apply the mean filter
"""
img = signal.medfilt(img, 5).astype('uint8')

"""
2. Enhance the image through the otsu enhancement
"""
thresh = filters.threshold_otsu(img)
f = thresh / (255 - thresh)
img = ((1 - f) * (255 - img * (1 + f))).astype('uint8')

"""
3. Binarize the image
"""
img = np.where(img < thresh, 0, 255)

"""
4. Calculate the regionprops
"""
# Get the regionprops
lbl = measure.label(img)
regions = measure.regionprops(lbl, coordinates='rc')

# Initialize the DataFrame with the desired columns
columns = [('label', int),
           ('area', int),
           ('perimeter', float),
           ('centroid', object),
           ('solidity', float),
           ('eccentricity', float),
           ('convex_area', int),
           ('circularity', float),
           ('orientation', float),
           ('bbox', object)]
df = pd.DataFrame({k: pd.Series(dtype=t) for k, t in columns})

# Fill the DataFrame
for i, reg in enumerate(regions):
    df.loc[i] = [
        reg.label,
        reg.area,
        reg.perimeter,
        reg.centroid,
        reg.solidity,
        reg.eccentricity,
        reg.convex_area,
        4 * np.pi * reg.area / reg.perimeter ** 2 if reg.perimeter != 0 else np.inf,  # circularity
        reg.orientation,
        reg.bbox
    ]

# result = df.to_json(orient='records', double_precision=4)
# print(result)

display(df)

# plt.figure(figsize=(11, 11))
# plt.imshow(img, cmap = plt.get_cmap('gray'))

```

      label
      area
      perimeter
      centroid
      solidity
      eccentricity
      convex_area
      circularity
      orientation
      bbox

      0
      1
      593891
      6379.937300
      (444.15259197394806, 454.06933932320914)
      0.566379
      0.519865
      1048576
      0.183351
      -1.535771
      (0, 0, 1024, 1024)

      1
      2
      16264
      709.646753
      (112.46593703885883, 793.8274717166748)
      0.922937
      0.913972
      17622
      0.405838
      -0.294397
      (28, 710, 280, 837)

      2
      3
      49
      31.727922
      (286.9387755102041, 833.1020408163265)
      0.720588
      0.773579
      68
      0.611678
      -0.320758
      (283, 829, 295, 837)

      3
      4
      12
      10.035534
      (299.25, 833.4166666666666)
      0.923077
      0.855502
      13
      1.497305
      0.685730
      (297, 832, 302, 836)

      4
      5
      10
      8.828427
      (420.2, 427.5)
      1.000000
      0.683130
      10
      1.612290
      -1.570796
      (419, 426, 422, 430)

      5
      6
      229
      94.911688
      (427.4017467248908, 446.3930131004367)
      0.620596
      0.796357
      369
      0.319452
      1.566909
      (421, 431, 440, 462)

      6
      7
      29
      17.071068
      (473.6551724137931, 481.2758620689655)
      0.966667
      0.516245
      30
      1.250508
      -0.618715
      (471, 479, 477, 485)

      7
      8
      101
      50.870058
      (478.5742574257426, 504.05940594059405)
      0.801587
      0.961610
      126
      0.490464
      -1.322596
      (475, 494, 484, 516)

      8
      9
      8
      8.414214
      (490.5, 461.125)
      0.888889
      0.937309
      9
      1.419949
      -1.456334
      (490, 459, 492, 464)

      9
      10
      3853
      396.818326
      (543.431871269141, 440.0153127433169)
      0.724520
      0.716600
      5318
      0.307486
      1.427281
      (503, 385, 583, 489)

      10
      11
      10
      8.828427
      (544.0, 580.8)
      1.000000
      0.612372
      10
      1.612290
      -1.570796
      (543, 579, 546, 583)

      11
      12
      78
      35.970563
      (570.3846153846154, 385.7564102564103)
      0.876404
      0.888431
      89
      0.757548
      -0.519845
      (564, 382, 577, 392)

      12
      13
      1
      0.000000
      (564.0, 392.0)
      1.000000
      0.000000
      1
      inf
      0.785398
      (564, 392, 565, 393)

      13
      14
      42
      23.071068
      (577.1666666666666, 400.6666666666667)
      0.954545
      0.867421
      44
      0.991571
      1.520056
      (575, 396, 580, 406)

      14
      15
      19
      13.656854
      (578.6842105263158, 379.57894736842104)
      1.000000
      0.776662
      19
      1.280154
      1.276256
      (577, 377, 581, 383)

      15
      16
      22
      16.692388
      (637.8181818181819, 391.1363636363636)
      0.956522
      0.890439
      23
      0.992192
      0.883639
      (635, 388, 641, 395)

      16
      17
      74
      35.213203
      (657.6351351351351, 449.31081081081084)
      0.860465
      0.842383
      86
      0.749947
      -1.059964
      (653, 443, 664, 455)

      17
      18
      24
      17.071068
      (661.7916666666666, 466.875)
      0.960000
      0.865032
      25
      1.034903
      1.017494
      (660, 464, 665, 471)

      18
      19
      11
      8.621320
      (667.5454545454545, 452.0)
      0.846154
      0.800815
      13
      1.859752
      -0.000000
      (665, 451, 670, 454)

      19
      20
      118
      65.455844
      (669.8389830508474, 508.46610169491527)
      0.715152
      0.983523
      165
      0.346095
      1.327167
      (666, 495, 677, 523)

      20
      21
      4
      3.207107
      (674.25, 450.75)
      0.800000
      0.912871
      5
      4.887008
      0.321751
      (673, 450, 676, 452)

      21
      22
      17
      13.414214
      (676.6470588235294, 463.94117647058823)
      1.000000
      0.875727
      17
      1.187212
      -0.075186
      (674, 463, 680, 466)

      22
      23
      8
      7.414214
      (687.25, 449.375)
      0.800000
      0.921119
      10
      1.828815
      0.557941
      (686, 448, 690, 452)

      23
      24
      7
      6.207107
      (687.1428571428571, 493.0)
      0.875000
      0.801784
      8
      2.283124
      -1.570796
      (686, 492, 689, 496)

      24
      25
      9
      8.000000
      (722.0, 590.0)
      1.000000
      0.000000
      9
      1.767146
      0.785398
      (721, 589, 724, 592)

      25
      26
      47
      25.313708
      (725.6595744680851, 301.8723404255319)
      0.903846
      0.731657
      52
      0.921714
      0.379708
      (722, 299, 731, 306)

      26
      27
      1397
      313.255880
      (770.0178954903364, 322.1660701503221)
      0.526772
      0.926261
      2652
      0.178899
      -0.125265
      (724, 303, 803, 345)

      27
      28
      346
      91.254834
      (740.693641618497, 495.2369942196532)
      0.814118
      0.914069
      425
      0.522125
      0.436476
      (725, 483, 755, 506)

      28
      29
      20
      14.242641
      (734.45, 462.75)
      0.952381
      0.714569
      21
      1.238964
      1.356578
      (733, 460, 737, 466)

      29
      30
      91
      36.142136
      (746.021978021978, 461.9120879120879)
      0.947917
      0.731242
      96
      0.875434
      1.062351
      (742, 456, 752, 469)

      30
      31
      4
      4.621320
      (743.25, 445.0)
      1.000000
      0.790569
      4
      2.353627
      -1.570796
      (743, 444, 745, 447)

      31
      32
      16
      12.242641
      (750.1875, 426.8125)
      1.000000
      0.664230
      16
      1.341466
      0.301144
      (748, 425, 753, 429)

      32
      33
      233
      71.012193
      (760.0085836909872, 519.8068669527897)
      0.811847
      0.500166
      287
      0.580631
      0.329675
      (751, 511, 773, 532)

      33
      34
      27
      17.071068
      (758.3333333333334, 461.6666666666667)
      0.964286
      0.735647
      28
      1.164266
      0.785398
      (756, 459, 762, 465)

      34
      35
      32
      20.485281
      (768.0625, 507.78125)
      0.888889
      0.870765
      36
      0.958244
      0.457543
      (764, 505, 773, 511)

      35
      36
      19
      15.035534
      (768.3157894736842, 536.5263157894736)
      0.950000
      0.851822
      20
      1.056150
      1.469859
      (767, 533, 771, 540)

      36
      37
      11
      9.656854
      (773.8181818181819, 366.8181818181818)
      1.000000
      0.269680
      11
      1.482283
      0.785398
      (772, 365, 776, 369)

      37
      38
      46
      22.727922
      (785.5217391304348, 284.5869565217391)
      0.958333
      0.640396
      48
      1.119047
      0.884309
      (782, 281, 790, 289)

      38
      39
      71
      34.727922
      (797.0704225352113, 387.8732394366197)
      0.898734
      0.897346
      79
      0.739794
      1.514134
      (794, 382, 801, 396)

      39
      40
      7
      7.207107
      (804.7142857142857, 309.57142857142856)
      1.000000
      0.888666
      7
      1.693503
      0.145728
      (803, 309, 807, 311)

      40
      41
      74
      44.349242
      (810.9864864864865, 461.0945945945946)
      0.755102
      0.946313
      98
      0.472791
      1.064276
      (805, 453, 816, 470)

      41
      42
      3
      1.000000
      (816.0, 344.0)
      1.000000
      1.000000
      3
      37.699112
      -0.000000
      (815, 344, 818, 345)

      42
      43
      271
      91.976659
      (838.6752767527676, 441.6088560885609)
      0.687817
      0.798153
      394
      0.402554
      -0.462306
      (828, 427, 855, 452)

      43
      44
      113
      52.526912
      (839.7787610619469, 481.01769911504425)
      0.824818
      0.934978
      137
      0.514665
      1.123224
      (833, 473, 847, 493)

      44
      45
      58
      31.556349
      (839.2758620689655, 357.8965517241379)
      0.892308
      0.776567
      65
      0.731921
      -1.333403
      (836, 353, 844, 364)

      45
      46
      88
      35.556349
      (845.8977272727273, 342.3181818181818)
      0.946237
      0.775866
      93
      0.874698
      0.084746
      (840, 339, 853, 348)

      46
      47
      68
      32.520815
      (853.9117647058823, 379.75)
      0.944444
      0.844541
      72
      0.807971
      0.265653
      (848, 377, 861, 384)

      47
      48
      2
      0.000000
      (850.5, 375.0)
      1.000000
      1.000000
      2
      inf
      -0.000000
      (850, 375, 852, 376)

      48
      49
      57
      32.384776
      (859.8070175438596, 401.17543859649123)
      0.791667
      0.877876
      72
      0.682972
      0.318750
      (854, 397, 866, 405)

      49
      50
      70
      30.970563
      (867.7571428571429, 436.14285714285717)
      0.945946
      0.802344
      74
      0.917085
      1.092008
      (863, 431, 873, 442)

      50
      51
      19
      13.899495
      (868.7368421052631, 457.6842105263158)
      1.000000
      0.660753
      19
      1.235849
      1.204857
      (867, 455, 872, 461)

      51
      52
      14
      11.656854
      (872.0714285714286, 422.2142857142857)
      0.933333
      0.668793
      15
      1.294718
      0.536914
      (870, 421, 875, 425)

      52
      53
      170
      60.183766
      (899.3705882352941, 433.61176470588236)
      0.801887
      0.758786
      212
      0.589794
      0.721005
      (891, 427, 910, 445)

      53
      54
      14
      10.828427
      (940.5, 429.5)
      1.000000
      0.670820
      14
      1.500400
      0.785398
      (939, 428, 943, 432)

      54
      55
      103
      55.455844
      (954.4563106796116, 411.22330097087377)
      0.668831
      0.956596
      154
      0.420874
      -1.412918
      (950, 401, 960, 422)

      55
      56
      30
      19.313708
      (974.9666666666667, 635.4)
      0.967742
      0.866951
      31
      1.010648
      0.767837
      (972, 632, 979, 639)

      56
      57
      96
      38.420310
      (987.84375, 411.5208333333333)
      0.888889
      0.743860
      108
      0.817258
      -1.011551
      (983, 404, 995, 418)

```python

```