Gates, Cycles and Circuits

True-Qᵀᴹ uses the following hierarchy to express actions to be performed on quantum registers, ordered starting with the most primitive:

Gates

The trueq.Gate object has certain common gates aliased for convenience. For example, we can create a CNOT gate as follows:

import trueq as tq

gate = tq.Gate.cnot

Below are some of the included gate names.

Description

Alias

Hadamard

h

Pauli gates and identity

x, y, z, id

Controlled Paulis

cx, cy, cz

S and T gates

s, t

SWAP and iSWAP

swap, iswap

Note

To avoid global phase ambiguity when comparing trueq.Gate objects, their global phase is modified on instantiation with the convention that its linear expansion in terms of the Pauli matrices has a positive coefficient on the first nonzero term (in lexographical order). For this reason you may find that predefined gates, listed above, contain a global phase relative to their standard textbook representation.

An example showing some of the common functions for initializing and outputting trueq.Gate objects follows:

import trueq as tq

# Custom gates can be defined using a matrix representation, or using an expansion in
# terms of Pauli matrices. The following code gives an example of each:
gate1 = tq.Gate([[1, 1], [-1, 0]])
gate2 = tq.Gate.from_generators("XX", 90, "YZ", 180)

# To generate a visualization of a Gate, run visualization.plot_mat()
tq.visualization.plot_mat(gate1.mat)
tq.visualization.plot_mat(gate2.mat)

# Custom gates are not checked automatically to ensure that they are unitary.
# To check whether a gate is unitary, run gates.is_unitary():
print(gate1.is_unitary)
print(gate2.is_unitary)

# Gates can be printed in either their matrix form or as an expansion of Pauli matrices
# as follows:
print(gate1.generators)
print(gate2.mat)
../_images/gates_cycles_circuits_00.svg
../_images/gates_cycles_circuits_01.svg
False
True
{'Y': -138.56406460551017, 'Z': 0.0}
[[ 0.00000000e+00+0.00000000e+00j -7.07106781e-01+4.32978028e-17j
  -4.32978028e-17-7.07106781e-01j  0.00000000e+00+0.00000000e+00j]
 [ 7.07106781e-01-4.32978028e-17j  0.00000000e+00+0.00000000e+00j
   0.00000000e+00+0.00000000e+00j  4.32978028e-17+7.07106781e-01j]
 [ 4.32978028e-17+7.07106781e-01j  0.00000000e+00+0.00000000e+00j
   0.00000000e+00+0.00000000e+00j  7.07106781e-01-4.32978028e-17j]
 [ 0.00000000e+00+0.00000000e+00j -4.32978028e-17-7.07106781e-01j
  -7.07106781e-01+4.32978028e-17j  0.00000000e+00+0.00000000e+00j]]

Cycles

A cycle refers to one round of gates on a quantum device. Here are some examples:

cycle1 = tq.Cycle({(0, ): tq.Gate.x, (1, ): tq.Gate.y})
cycle2 = tq.Cycle({(0, 2): tq.Gate.cz, (1, ): tq.Gate.x})
cycle3 = tq.Cycle({(3, ): tq.Gate.h, (5, ): tq.Gate.s})

Note

Multi-qubit gate objects have an implicit qubit order in the qubit labels. For example, the controlled-X gate implicitly controls on the first qubit, so that tq.Cycle({(0, 1): tq.Gate.cx}) controls on qubit 0 whereas tq.Cycle({(1, 0): tq.Gate.cx}) controls on qubit 1.

Note

Any qubit that does not have a specified gate action is implicitly acted upon by the identity gate.

Circuits

Circuit objects belong to classes that all inherit from the base class trueq.Circuit, exposing the following common attributes:

  1. trueq.Circuit.labels: Which registers of a quantum device this circuit (ideally) acts on.

  2. trueq.Circuit.cycles: a list of trueq.Cycles, thereby defining a quantum circuit.

  3. trueq.Circuit.results: a dictionary of results, describing what was measured when the circuit was implemented (possibly many times). This attribute is initially the empty dictionary {}.

The following example shows how to construct a circuit from cycles.

import trueq as tq

circuit = tq.Circuit([
    tq.Cycle({(0, ): tq.Gate.x, (1, ): tq.Gate.y}),
    tq.Cycle({(0, 2): tq.Gate.cz, (1, ): tq.Gate.x}),
    tq.Cycle({(3, ): tq.Gate.h, (5, ): tq.Gate.s})
])

Circuit Collections

Collections of circuits are stored in instances of trueq.CircuitCollection, and this is the type that will be returned by the functions that create circuits for diagnostic tools. Multiple protocols can (and indeed usually should) put their circuits into the same collection, so that their results can be analyzed together if applicable. Different types of circuit are differentiated within the collection by their trueq.Circuit.key attribute, and collections of circuits are designed so that accessing subsets of circuits by properties of these keys is convenient.

For example, any circuits created with trueq.make_srb() get a key property protocol=SRB and likewise any circuits created with trueq.make_xrb() get a key property protocol=XRB. We may access only those circuits where protocol=SRB as follows:

import trueq as tq

circuits = tq.make_srb([0], [4, 100])
circuits.append(tq.make_xrb([0], [4, 100]))
for circuit in circuits.subset(protocol="SRB"):
    print(circuit.key.protocol)

See the API documentation for trueq.CircuitCollection for other key-related convenience functions.

Perhaps most importantly, circuit collections implement the method trueq.CircuitCollection.fit(), which takes all of the available data in the collection and analyzes it, returning a trueq.parameters.ParameterCollection . Similarly, trueq.CircuitCollection.plot() contains methods to visualize the results of any analysis.

Circuit collections are iterable, yielding each circuit in turn. The order of yielding is not guaranteed to be consistent—the idea is that a quantum device will iterate through the circuits, performing each and entering the results (see step 2 from Streamlined Randomized Benchmarking, for example).