Circuit analysis

Download this notebook - circuit_analysis_example.ipynb

This notebook will introduce the basic methods of analysis and visualization of circuits available in pytket.

It makes use of the modules pytket_qiskit and pytket_cirq for visualization; these need to be installed (with pip) in addition to pytket.

We’ll start by generating a small circuit to use as an example, and give it a name.

from pytket.circuit import Circuit, OpType
c = Circuit(4, name="example")
c.add_gate(OpType.CU1, 0.5, [0, 1])
c.H(0).X(1).Y(2).Z(3)
c.X(0).CX(1, 2).Y(1).Z(2).H(3)
c.Y(0).Z(1)
c.add_gate(OpType.CU1, 0.5, [2, 3])
c.H(2).X(3)
c.Z(0).H(1).X(2).Y(3).CX(3, 0)

Basic statistics

From the circuit we can easily read off the number of qubits …

c.n_qubits

… the name …

c.name

… the overall depth of the circuit …

c.depth()

… and the depth by type of gate:

from pytket.circuit import OpType
c.depth_by_type(OpType.CU1), c.depth_by_type(OpType.H)

This last method counts the number of instances of the specified gate type that cannot be parallelized. Notice that although there are 4 H gates in the circuit, the H-depth is 2 because pairs of them can be parallelized (as will be clear from the visualizations below).

Visualization

There are several ways to produce useful visualizations of circuits from pytket: we can use the methods in the pytket.circuit.display class; we can use the built-in Graph class to visualize the circuit as a directed acyclic graph (DAG); we can convert the circuit to either Qiskit or Cirq and use the tools provided by those modules; or we can export to Latex.

Via the render_circuit_jupyter method

from pytket.circuit.display import render_circuit_jupyter
render_circuit_jupyter(c)

Notice that although the render_circuit_jupyter method is the recommended way to render a circuit as jupyter cell output, one of the other methods should be used when working with scripts or python shells.

Via the Graph class

from pytket.utils import Graph
G = Graph(c)
G.get_DAG()

The small numbers (0 and 1) shown at the entry to and exit from the two-qubit gates represent “port numbers” on the gates; these allow us to track individual qubits, which may be placed in a different order on entry and exit in order to simplify the layout.

The Graph class also has methods to save this image to a file and to open it in a PDF viewer.

We can also view the qubit connectivity graph of a circuit:

G.get_qubit_graph()

Via Qiskit

from pytket.extensions.qiskit import tk_to_qiskit
print(tk_to_qiskit(c))

Via Cirq

from pytket.extensions.cirq import tk_to_cirq
print(tk_to_cirq(c))

(Note that Cirq cannot represent all gates diagrammatically.)

Via Latex

We can create a Latex document containing a diagram of the circuit using the to_latex_file() method. This uses the quantikz library. The document can be viewed on its own or the Latex can easily be copied and pasted into a larger document.

c.to_latex_file("c.tex")

Commands

We can retrieve a list of operations comprising a circuit, each represented as a Command object:

cmds = c.get_commands()
print(cmds)

Each Command is defined by an operation and the qubits it operates on (as well as the classical bits and conditions, if any). For example:

cmd0 = cmds[0]
op0 = cmd0.op
print(op0)
qubits0 = cmd0.args
print(qubits0)

From the Op we can read off the string representation (in normal or Latex form), the parameters and the type:

op0.get_name()  # normal form
op0.get_name(latex=True)  # Latex form
op0.type, op0.params

Note that some compilation passes introduce implicit wire swaps at the end of the circuit, which are not represented in the command list. (The internal representation of the circuit as a directed acyclic graph reduces explicit permutations of qubits to implicit features of the graph.)