2-qubit GST with a custom 2-qubit gate

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 tutorial, we look at how to construct a 2-qubit gateset with a "non-standard" 2-qubit gate.

The previous tutorial gave an overview of the steps to run GST on a "standard" 2-qubit system. In that case, the gate set, fiducials, germs, etc., are already contained in pyGSTi within a pygsti.construction.stdXXX module. The previous tutorial also showed how to use build_gateset to construct a gate set single-qubit rotations and a CNOT gate. The only difference when working with a "non-standard" gate set is in the creation of the "target gate set" object. Thus, in this tutorial we focus only on creating a custom 2-qubit gate - the rest of the procedure for doing 2-qubit GST is identical to that in the previous tutorial.

In [1]:
import pygsti

Create a gateset with only single-qubit gates

Since the space of single-qubit gates is relatively small, let's assume that the single-qubit gates in our gateset are able to be specified using the build_gateset. Then we can construct a GateSet object containing all but the two-qubit gate(s) using build_gateset just as in other tutorials.

If our 2-qubit gate happened to be one that could be specified using build_gateset then we would just use it to construct the entire GateSet and we would be done. Currently, build_gateset can create any controlled $X$, $Y$, or $Z$ rotation using CX, CY and CZ (for details, see how CX was used to construct a CNOT gate in the previous tutorial).

In [2]:
# Notes on build_gateset arguments:
#   [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"
#   "E1" = effect label; can be anything that begins with "E"
#   "2" = a prep or effect expression indicating a projection/preparation of the 3rd (b/c 0-based) computational basis element
#   'dnup': ('rho0','E2') = designate the SPAM label "dnup" to mean preparation using "rho0" (a prep label) and measuring the outcome "E2" (an effect label)
#   "pp" = create all of these gate & SPAM operators in the Pauli-product basis.
gs_target = pygsti.construction.build_gateset( 
            [4], [('Q0','Q1')],['Gix','Gix','Gxi','Gyi'], 
            [ "X(pi/2,Q1)", "Y(pi/2,Q1)", "X(pi/2,Q0)", "Y(pi/2,Q0)"],
            prepLabels=['rho0'], prepExpressions=["0"],
            effectLabels=['E0','E1','E2'], effectExpressions=["0","1","2"], 
            spamdefs={'upup': ('rho0','E0'), 'updn': ('rho0','E1'),
                           'dnup': ('rho0','E2'), 'dndn': ('rho0','remainder') }, basis="pp")

Create a 2-qubit gate

This is how you create a 2-qubit gate from a given unitary which acts on the state space.

In [3]:
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.std_to_pp(mySuperOp_stdbasis)

#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

That's it!

We're done creating our 2-qubit gateset. Proceed as in the previous tutorial to run a 2-qubit GST analysis.

In [ ]: