# 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:

`trueq.Gate`

: ideal quantum gates

`trueq.Cycle`

: clock cycles of a quantum circuit

`trueq.Circuit`

: list of cycles and other metadata

`trueq.CircuitCollection`

: organized collection of circuits

## 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)
```

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

`trueq.Circuit.labels`

: Which registers of a quantum device this circuit (ideally) acts on.

`trueq.Circuit.cycles`

: a list of`trueq.Cycle`

s, thereby defining a quantum circuit.

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