Consider the space of density matrices corresponding to a Hilbert space $\mathcal{H}$ of dimension $d$. The basis used for this Hilbert-Schmidt space, $B(\mathcal{H})$, can be any set of $d\times d$ matrices which span the density matrix space. pyGSTi supports arbitrary bases by deriving from the pygsti.tools.Basis
class, and constains built-in support for the following basis sets:
"std"
to appropriate function arguments."pp"
."gm"
."qt"
.Various functions and objects within pyGSTi require knowledge of what Hilbert-Schmidt basis is being used. The pygsti.objects.Basis
object encapsulates a basis, and is the most flexible way of specifying a basis in pyGSTi. Alternatively, many functions also accept the short strings "std"
, "gm"
, "pp"
, and "qt"
to select one of the standard bases. In this tutorial, we'll demonstrate how to create a Basis
object and use it and related functions to obtain and change the basis of the operation matrices and SPAM vectors stored in a Model
.
The most straightforward way to create a Basis
object is to provide its short name and dimension to the Basis.cast
function, which "casts" various things as a basis object. PyGSTi contains built-in support for bases consisting of the tensor product of Pauli matrices (or just the Pauli matrices in the case of 1 qubit), named "pp"
, as well as the Gell-Mann matrices, named "gm"
. It also contains a special "qutrit" basis, named "qt"
, for the case of 3-level quantum systems. In cases when there are an integral number of qubits, and the dimension equals $4^N$, the "pp"
basis is usually preferred since it is more intuitive. In other cases, where the Hilbert space includes non-qubit (e.g. environmental) degrees of freedom, the Gell-Mann basis may be useful since it can be used in any dimension. Note that both the Gell-Mann and Pauli-Product bases reduce to the usual Pauli matrices plus identity in when the dimension equals 4 (1 qubit).
Here are some examples:
import pygsti
from pygsti.baseobjs import Basis
pp = Basis.cast('pp', 4) # Pauli-product (in this dim=4 case, just the Paulis)
std = Basis.cast('std', 4) # "standard" basis of matrix units
gm = Basis.cast('gm', 4) # Gell-Mann
qt = Basis.cast('qt', 9) # qutrit - must be dim 9
bases = [pp, std, gm, qt]
Each of the pp
, std
, and gm
bases created will have $4$ $2x2$ matrices each. The qt
basis has $9$ $3x3$ matrices instead:
for basis in bases:
print('\n{} basis (dim {}):'.format(basis.name, basis.dim))
print('{} elements:'.format(len(basis)))
for element in basis:
pygsti.tools.print_mx(element)
However, custom "explicit" bases, which expicitly hold a set of basis-element matrices, can be easily created by supplying a list of the elements:
import numpy as np
from pygsti.baseobjs import ExplicitBasis
std2x2Matrices = [
np.array([[1, 0],
[0, 0]]),
np.array([[0, 1],
[0, 0]]),
np.array([[0, 0],
[1, 0]]),
np.array([[0, 0],
[0, 1]])]
alt_standard = ExplicitBasis(std2x2Matrices, ["myElement%d" % i for i in range(4)],
name='std', longname='Standard')
print(alt_standard)
More complex bases can be created by chaining the elements of other bases together along the diagonal. This yields a basis for the direct-sum of the spaces spanned by the original basis. For example, a composition of the $2x2$ std
basis with the $1x1$ std
basis leads to a basis with state vectors of length $5$, or $5x5$ matrices:
from pygsti.baseobjs import DirectSumBasis
comp = Basis.cast('std', [4, 1])
comp = Basis.cast([('std', 4), ('std', 1)])
comp = DirectSumBasis([ Basis.cast('std', 4), Basis.cast('std', 1)])
#All three comps above give the same final basis
print(comp)
for element in comp.elements:
print(element)
Once created, bases are used to manipulate matrices and vectors within pygsti:
from pygsti.tools import change_basis, flexible_change_basis
mx = np.array([[1, 0, 0, 1],
[0, 1, 2, 0],
[0, 2, 1, 0],
[1, 0, 0, 1]])
change_basis(mx, 'std', 'gm') # shortname lookup
change_basis(mx, std, gm) # object only
change_basis(mx, std, 'gm') # combination
Composite bases can be converted between expanded and contracted forms:
mxInStdBasis = np.array([[1,0,0,2],
[0,0,0,0],
[0,0,0,0],
[3,0,0,4]],'d')
begin = Basis.cast('std', 4)
end = Basis.cast('std', [1,1])
mxInReducedBasis = flexible_change_basis(mxInStdBasis, begin, end)
print(mxInReducedBasis)
original = flexible_change_basis(mxInReducedBasis, end, begin)