While pyGSTi is able to support several common types of 2-qubit gates, the space of all possible 2-qubit gates is so large that some users will need to construct their own particular 2-qubit gate "from scratch". In this example, we look at how to construct a 2-qubit gateset manually. We'll use pygsti.construction.build_gateset
to construct the single-qubit gates and we'll create a "non-standard" 2-qubit gate by specifying the unitary operation it performs as a $4\times 4$ matrix.
To perform GST on such a gate set, one may need to compute new sets of fiducials and/or germ sequences. Once these are obtained, 2-qubit GST can be run just as in the example which uses one of pyGSTi's built-in 2Q gate sets.
import pygsti
Since the space of single-qubit gates is relatively small, we'll assume that the single-qubit gates in our gateset are able to be specified using pygsti.construction.build_gateset
. So we'll start by construct a GateSet
object containing all but the two-qubit gate(s) using build_gateset
.
gs_target = pygsti.construction.build_gateset(
[4], [('Q0','Q1')],['Gii','Gix','Giy','Gxi','Gyi'],
[ "I(Q0):I(Q1)", "X(pi/2,Q1)", "Y(pi/2,Q1)", "X(pi/2,Q0)", "Y(pi/2,Q0)" ],
effectLabels=['00','01','10','11'], effectExpressions=["0","1","2","3"])
There are lots of arguments to this function, so let's review what they mean:
[4]
= a 4-dimensional Hilbert (state) space[('Q0','Q1')]
= interpret this 4-d space as that of two qubits 'Q0', and 'Q1' (note these labels must begin with 'Q'!)"Gix"
= gate label; can be anything that begins with 'G' and is followed by lowercase letters"X(pi/2,Q1)"
= pi/2 single-qubit x-rotation gate on the qubit labeled Q1"rho0"
= prep label; can be anything that begins with "rho"'10'
= designates a POVM effect label whose corresponding vector is given by the effectExpressions
argument."2"
= a prep or effect expression indicating a projection/preparation of the 3rd (b/c 0-based) computational basis elementYou can also explicity add identity operations, e.g. "I(Q0)"
, to the rotation gates to get the same gate set (see gs_targetB
below), and this same syntax can be used for non-entangling 2-qubit gates, e.g. "X(pi/2,Q0):X(pi/2,Q1)"
.
gs_targetB = pygsti.construction.build_gateset(
[4], [('Q0','Q1')],['Gii','Gix','Giy','Gxi','Gyi','Gcnot'],
[ "I(Q0):I(Q1)", "I(Q0):X(pi/2,Q1)", "I(Q0):Y(pi/2,Q1)", "X(pi/2,Q0):I(Q1)", "Y(pi/2,Q0):I(Q1)", "CNOT(Q0,Q1)" ],
effectLabels=['00','01','10','11'], effectExpressions=["0","1","2","3"])
assert(abs(gs_target.frobeniusdist(gs_targetB)) < 1e-6)
If our 2-qubit gate happens to be one that can be specified using build_gateset
then we can just use it to construct the entire GateSet
and be done. Currently, build_gateset
can create any controlled $X$, $Y$, or $Z$ rotation using CX
, CY
and CZ
, as well as the standard CNOT
and CPHASE
gates. Below we demonstrate creation with the CNOT gate. The resulting GateSet
is then identical to pygsti.construction.std2Q_XYCNOT.gs_target
.
gs_withCNOT = pygsti.construction.build_gateset(
[4], [('Q0','Q1')],['Gii','Gix','Giy','Gxi','Gyi','Gcnot'],
[ "I(Q0):I(Q1)", "I(Q0):X(pi/2,Q1)", "I(Q0):Y(pi/2,Q1)", "X(pi/2,Q0):I(Q1)", "Y(pi/2,Q0):I(Q1)", "CNOT(Q0,Q1)" ],
effectLabels=['00','01','10','11'], effectExpressions=["0","1","2","3"])
#Note this is the same gateset as one of pyGSTi's standard gate sets:
from pygsti.construction import std2Q_XYICNOT
assert(abs(gs_withCNOT.frobeniusdist(std2Q_XYICNOT.gs_target)) < 1e-6)
Thus, since our gs_target
just contains the 1-qubit gates of std2Q_XYCNOT.gs_target
, we could also create has all the 1-qubit gates are the same a third way to obtain gs_target
is to just remove Gcnot
from the standard gate set:
gs_targetC = std2Q_XYICNOT.gs_target.copy()
del gs_targetC.gates['Gcnot']
assert(abs(gs_target.frobeniusdist(gs_targetC)) < 1e-6)
We're assuming that build_gateset
can't make the 2-qubit gate we want, so we'll need to create our own. Below we demonstrate how to create a 2-qubit gate from a given unitary which acts on the 2-qubit, 4-dimensional, state space.
import numpy as np
#Unitary in acting on the state-space { |A>, |B>, |C>, |D> } == { |00>, |01>, |10>, |11> }.
# This unitary rotates the second qubit by pi/2 in either the (+) or (-) direction based on
# the state of the first qubit.
myUnitary = 1./np.sqrt(2) * np.array([[1,-1j,0,0],
[-1j,1,0,0],
[0,0,1,1j],
[0,0,1j,1]])
#Convert this unitary into a "superoperator", which acts on the
# space of vectorized density matrices instead of just the state space.
# These superoperators are what GST calls "gates".
mySuperOp_stdbasis = pygsti.unitary_to_process_mx(myUnitary)
#After the call to unitary_to_process_mx, the superoperator is a complex matrix
# in the "standard" or "matrix unit" basis given by { |A><A|, |A><B|, etc }.
# For use in GST, we want to work with a *real* matrix in either the
# Gell-Mann or Pauli-product basis. Here we choose the Pauli-product basis,
# which is typically more intuitive when working with 2 qubits.
mySuperOp_ppbasis = pygsti.change_basis(mySuperOp_stdbasis, "std", "pp")
#The resulting superoperator in the Pauli-product basis is exactly
# what goes into the GateSet object, which can be set using
# dictionary syntax. The line below names our two-qubit gate 'Gtq'
gs_target['Gtq'] = mySuperOp_ppbasis
We're done creating our 2-qubit gateset, gs_target
, printed below. To run 2-qubit GST with this gate set we ideally would generate fiducials and germs specifically for it. Actually, since the 1-qubit gates are the same as other standard 2Q gate sets, and the fiducial seqeuences for these standard sets only contain 1-qubit gates, we can just use the fiducial sets from a standard set (e.g. std2Q_XYCNOT
). The germs should be computed, though typically you can get away with just using the germ set of a standard gate set and replacing it's 2-qubit gate with the custom one - so it can be worth checking whether such a set is complete before running a full germ-selection procedure.
print(gs_target)
rho0 = FullyParameterizedSPAMVec with dimension 16 0.50 0 0 0.50 0 0 0 0 0 0 0 0 0.50 0 0 0.50 Mdefault = UnconstrainedPOVM with effect vectors: 00: FullyParameterizedSPAMVec with dimension 16 0.50 0 0 0.50 0 0 0 0 0 0 0 0 0.50 0 0 0.50 01: FullyParameterizedSPAMVec with dimension 16 0.50 0 0-0.50 0 0 0 0 0 0 0 0 0.50 0 0-0.50 10: FullyParameterizedSPAMVec with dimension 16 0.50 0 0 0.50 0 0 0 0 0 0 0 0-0.50 0 0-0.50 11: FullyParameterizedSPAMVec with dimension 16 0.50 0 0-0.50 0 0 0 0 0 0 0 0-0.50 0 0 0.50 Gii = FullyParameterizedGate with shape (16, 16) 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 Gix = FullyParameterizedGate with shape (16, 16) 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 Giy = FullyParameterizedGate with shape (16, 16) 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 Gxi = FullyParameterizedGate with shape (16, 16) 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 Gyi = FullyParameterizedGate with shape (16, 16) 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 Gtq = FullyParameterizedGate with shape (16, 16) 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0-1.00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.00 0 0 0 0 0 0 0 0 0 0 0 0 0