This tutorial outlines the mathematical framework needed to do computations with qudits and outlines how users can use this framework within True-Q™.

## The Weyl-Heisenberg Group

The Weyl-Heisenberg group is a generalization of the Pauli group to $$d$$-dimensional systems and has operators analogous to the Pauli $$X$$ and $$Z$$ operators, which are referred to as “shift” ($$X$$) and “clock” ($$Z$$) operators. In cases where the dimension is unclear, we will denote the $$d$$-dimensional shift (clock) matrix as $$X_d$$ ($$Z_d$$).

These operators are defined to be,

$\begin{split}X &= \sum_{a\in\mathbb{Z}_d}\ket{a \oplus 1}\bra{a}\\ Z &= \sum_{a\in\mathbb{Z}_d}\textrm{exp}(2\pi i a/d)\ket{a}\bra{a},\end{split}$

where $$\oplus$$ denotes addition modulo $$d$$. When $$d=2$$, these operators are the familiar Pauli $$X$$ and $$Z$$ operators. Their actions,

$\begin{split}X\ket{a} &= \ket{a+1}\\ Z\ket{a} &= \textrm{exp}(2\pi i a/d)\ket{a},\end{split}$

where $$a\in\mathbb{Z}_d$$ and operations act according to modular arithmetic, for example for 3-dimensional qudits (aka qutrits), $$X\ket{2}=\ket{0}$$.

Often, we specify elements of the Weyl-Heisenberg group (Weyl operators) by the powers of the clock and shift operators acting on a qudit along with an integer $$k$$ which specifies the phase. For a single-qudit system, a Weyl operator can be written as $$\omega(k)X^xZ^z$$, where

$\begin{split}\omega(k) =\begin{cases} \textrm{exp}(\pi i(2k+x\cdot z\%2)/2) & d=2 \\ \textrm{exp}(2\pi ik/d) & d>2 \end{cases}\end{split}$

Then, the powers and phase which specify that Weyl operator are $$(x, z)\in \mathbb{Z}_d^2$$ and $$k$$ respectively.

## Defining Weyl Operators in True-Q™

A $$n$$-qudit Weyl operator can be expressed as an element $$w\in\mathbb{Z}_d^{2n}$$ and an integer specifying the phase, where the first (last) $$n$$ entries of $$w$$ are the powers of the shift (clock) operators acting on each of the first (last) $$n$$ qudits. For example, the 3-qudit Weyl operator $$X\otimes X^2\otimes Z$$ can be specified by $$1,2,0,0,0,1$$ with $$k=0$$. To initialize a Weyl operator from a string in True-Q™, we specify the action of the operator on each individual qudit sequentially, separated by a W. For example, the operator $$X\otimes X^2\otimes Z$$ given above would be specified by W10W20W01 as

[2]:

import numpy as np
import trueq.math as tqm

# create a three-qutrit Weyls object which stores the example given above
weyl = tqm.Weyls("W10W20W01", 3)

# we can access the powers as follows:
print(weyl.powers)

[[1 2 0 0 0 1]]


The second argument in the constructor specifies the dimension of the qudits. If the global dimension has been specified by tq.settings.set_dim(3), these methods will use the specified dimension automatically. To instantiate a Weyls object with multiple Weyl operators, we separate the operators by a _ character. In this notation, all operators must act on the same number of qudits. For example, for the pair $$\{Z\otimes XZ^2, X^2\otimes XZ\}$$, we can run

[3]:

tqm.Weyls("W01W12_W20W11", 3)

[3]:

Weyls('W01W12_W20W11', dim=3)


Note

In the example above, we stored Weyl operators as Weyls objects. True-Q™ has several classes for storing Weyl operators which are used in different contexts. The Weyls class does not store phases, and is used, for example, to specify targeted errors for error diagnostic protocols such as Cycle Benchmarking (CB). Some of True-Q™'s classes for Weyl operators account for phases and some do not. If you are unsure which is relevant for your use-case, consult the API references.

## The Clifford Group on Qudits

The $$n$$-qudit Clifford group is defined to be the group of unitary operators that map each $$n$$-qudit Weyl operator to a phase multiple of an $$n$$-qudit Weyl operator under conjugation. This coincides with the definition of the $$n$$-qubit Clifford group with respect to the Pauli group. We can specify elements of the $$d$$-dimensional Clifford group by their unique action on a basis of the Weyl-Heisenberg group. True-Q™ follows the convention of specifying the mapping of the Weyl basis described by the rows of an identity matrix, that is the set $$\{X_1, X_2,...,X_n, Z_1,Z_2,...,Z_n\}$$.

A Clifford object then stores the outcome of applying the corresponding Clifford operator to each of those basis elements. For example, a generalized Hadamard (or Fourier) gate on 2 qudits maps the basis elements $$XI\rightarrow ZI$$, $$IX\rightarrow IZ$$, $$ZI\rightarrow X^{d-1}I$$, and $$IZ\rightarrow IX^{d-1}$$, and would therefore be stored as the ordered list $$\{ZI,\: IZ,\: X^{d-1}I,\: IX^{d-1}\}$$. We can instantiate a Clifford object which stores a Hadamard as

instantiate a 2-qudit Hadamard gate with dimension 3

[4]:

hadamard = tqm.Clifford("W01W00_W00W01_W20W00_W00W20", [0, 0, 0, 0], 3)


The second argument provides the phases on the basis elements after the Clifford is applied. True-Q™ has Clifford constructors for generalized versions of the Hadamard (fourier()), controlled-X (cx()) and controlled-Z (cz()) gates.

[5]:

built_in_h = tqm.Clifford.fourier(3)


We can retrieve the unitary matrix representation of a Clifford by calling mat(). Let’s use the matrices to check if these constructions are equivalent:

[6]:

np.isclose(hadamard.mat, np.kron(built_in_h.mat, built_in_h.mat))

[6]:

array([[ True,  True,  True,  True,  True,  True,  True,  True,  True],
[ True,  True,  True,  True,  True,  True,  True,  True,  True],
[ True,  True,  True,  True,  True,  True,  True,  True,  True],
[ True,  True,  True,  True,  True,  True,  True,  True,  True],
[ True,  True,  True,  True,  True,  True,  True,  True,  True],
[ True,  True,  True,  True,  True,  True,  True,  True,  True],
[ True,  True,  True,  True,  True,  True,  True,  True,  True],
[ True,  True,  True,  True,  True,  True,  True,  True,  True],
[ True,  True,  True,  True,  True,  True,  True,  True,  True]])


Some users may desire to incorporate random Cliffords in their circuits. For convenience, we provide the random() function that can construct a random Clifford as follows:

[7]:

# construct a random Clifford on 2 qutrits
tqm.Clifford.random(2, 3)

[7]:

Clifford('W01W20_W20W01_W00W02_W22W01', [2, 2, 2, 2], dim=3)