In this notebook, we'll demonstrate how to select preparation and measurement fiducials for the standard single-qubit $X, Y, I$ (or $X, Y$) gate set. The results are very straightforward to generalize for a non $X, Y, I$ single-qubit gateset or a multiqubit gate set.
Fiducial selection is not quite as much of a "dark art" as germ selection is, but there are nonetheless equally valid choices one can make in terms of inputs to the fiducial selection function. At present, we demonstrate only a subset of the functionality, but we endeavour to explain the remaining functionality as well.
#Import relevant modules.
import pygsti
import numpy as _np
from pygsti.algorithms import fiducialselection as FS
from pygsti.construction import std1Q_XYI
from pygsti.construction import std1Q_XY
import matplotlib.pyplot as plt
%matplotlib inline
import time
Different target gatesets require different fiducials. Thus, we must first define the target gateset.
It's worth noting that unlike with germ selection, we will use perfect gates. (In fact, imperfect gates for fiducial selection can introduce errors that we don't want.)
#Define the target gate set we will select fiducials for.
#Here, it is the standard X pi/2, Y pi/2, I gate set.
gs_target = std1Q_XYI.gs_target
Let's now try to actually pick out a fiducial set. The fiducial selection output will strongly depend on several different inputs to the function optimize_integer_fiducials_slack
. These inputs include:
prepOrMeas
: Whether we are attempting to compute preparation fiducials or measurement fiducials.fiducialList
: The list of candidate germs from which the fiducials set will be chosen.initialWeights
: The initial subset of candidate fiducials which the optimizer tests. The default here is None
, meaning that all candidate fiducials are included in the first fiducial set test.fixedSlack
: The absolute score a fiducial set is allowed to achieveslackFrac
: The relative score a fiducial set is allowed to achieveNote: Only one of fixedSlack
or slackFrac
may be set. Typically we will use fixedSlack
, and we find that fixedSlack
~1 is sufficient. These arguments determine the relaxation scheme used to reduce the fiducial list size.
forceEmpty
: Whether or not the fiducial set must contain the empty string.fixedNum
: Whether or not we are forcing the fiducial set to be a fixed sizescoreFunc
: Whether the objective function only tries to minimize how insensitive we are for our most insensitive direction in Hilbert-Schmidt space, or if it tries to make us as sensitive as possible to all directions in Hilbert-Schmidt spaceHere we demonstrate particular choices for the above inputs. By parameter counting, one can see that these particular instances yield "optimal" results (as there are four elements of a state or measurement effect, and we choose four fiducials).
However, we make no claims of optimality for these choices in general (particularly when 2 or more qubits are considered).
End users are encouraged to experiment themselves with these inputs. They are also welcome to contact the pygsti
development team at pygsti@sandia.gov.
#Let's try to pick out a fiducial set.
#First, we generate a candidate set which we'll attempt to prune.
#Here, we're looking at all gate string sequences of maximum length 2.
max_length = 2
gates = ['Gx','Gy']#We omit any identity operations here, as we don't want them in our fiducials.
#Important for the minlength arg to equal 0, so we include the empty string.
testFidList = pygsti.construction.list_all_gatestrings(gates,0,max_length)
Don't worry if the optimize_integer_fiducials_slack
function below throws a divide by zero warning;
this just means one of the tested cases was really bad.
#Compute the preparation fiducials
start = time.time()
prepFidList1 = FS.optimize_integer_fiducials_slack(gs_target,testFidList,prepOrMeas='prep',\
initialWeights=None,slackFrac=1)
end = time.time()
print
print "Fiducial selection completed in {0} seconds.".format(round(end-start, 7))
print prepFidList1
# Compute the measurement fiducials
start = time.time()
measFidList1 = FS.optimize_integer_fiducials_slack(gs_target,testFidList,prepOrMeas='meas',\
initialWeights=None,slackFrac=1)
end = time.time()
print
print "Fiducial selection completed in {0} seconds.".format(round(end-start, 7))
print measFidList1
print "We have selected our preparation fiducials to be:"
for fid in prepFidList1:
print '\t',fid
print "We have selected our measurement fiducials to be:"
for fid in measFidList1:
print '\t',fid
The key property we wish for our fiducials to have is informational completeness. Below, we test whether our selected preparation and measurement fiducuials are in fact so.
FS.test_fiducial_list(gs_target,prepFidList1,'prep',returnAll=True)
FS.test_fiducial_list(gs_target,measFidList1,'meas',returnAll=True)
Note that in practice, we use a set of six fiducials both for prep and measure (which can be created using std1Q_XYI.fiducials
).
This is for greater numerical stability; for single-qubit GST, we recommend 6 preparation and 6 measurement fiducials, as the added cost is not too great, and this provides prep and measure fiducials corresponding to all 6 antipodal points on the Bloch sphere, providing a nice degree of symmetry.
However, for multiqubit GST, experimental resource constraints become more relevant, and we recommend simply directly using the outputs of the fiducial selection code.
That said, one can, for one or more quibits, force optimize_integer_fiducials_slack to return a fiducial set of
fixed size. Instead of running an integer program over fiducial sets of different sizes, we can instead score
all fiducial sets of a fixed size (that are subsets of the input set) and select the best one.
This can become expensive quickly, but it is very feasible for at least single-qubit gate sets, as exhibited below.
To turn this functionality on, set the fixedNum keyword argument to be equal to the fiducial set size you want.
Also, a warning: If there does not exist an informationally complete fiducial set of the desired size, you may still receive an output from optimize_integer_fiducials_slack
, so it is important to check the score of the fiducial set (either via the returnAll
keyword arg, or the function test_fiducial_list
.)
#Again, we'll try to pick out a fiducial set, but now we will insist that the set be of size 6.
#We'll look at all gate string sequences of maximum length 3.
max_length = 3
gates = ['Gx','Gy']
testFidList_force6 = pygsti.construction.list_all_gatestrings(gates,0,max_length)
#Let's again forceEmpty to be True, and see what we get for preparation fiducials.
start = time.time()
prepFidList1_force6 = FS.optimize_integer_fiducials_slack(gs_target,testFidList_force6,prepOrMeas='prep',\
initialWeights=None,slackFrac=1,fixedNum=6,\
forceEmpty=True)
end = time.time()
print
print "Fiducial selection completed in {0} seconds.".format(round(end-start, 7))
print prepFidList1_force6
#Let's set forceEmpty = False
start = time.time()
prepFidList1_force6 = FS.optimize_integer_fiducials_slack(gs_target,testFidList_force6,prepOrMeas='prep',\
initialWeights=None,slackFrac=1,fixedNum=6,\
forceEmpty=False)
end = time.time()
print
print "Fiducial selection completed in {0} seconds.".format(round(end-start, 7))
print prepFidList1_force6
#Conveniently, we get the same results as above!
#Now let's make a measurement fiducial set with forceEmpty = True
start = time.time()
measFidList1_force6 = FS.optimize_integer_fiducials_slack(gs_target,testFidList_force6,prepOrMeas='meas',\
initialWeights=None,slackFrac=1,fixedNum=6,\
forceEmpty=True)
end = time.time()
print
print "Fiducial selection completed in {0} seconds.".format(round(end-start, 7))
print measFidList1_force6
#Now let's make a measurement fiducial set with forceEmpty = False
start = time.time()
measFidList1_force6 = FS.optimize_integer_fiducials_slack(gs_target,testFidList_force6,prepOrMeas='meas',\
initialWeights=None,slackFrac=1,fixedNum=6,\
forceEmpty=False)
end = time.time()
print
print "Fiducial selection completed in {0} seconds.".format(round(end-start, 7))
print measFidList1_force6
print "We have selected our size-6 preparation fiducials to be:"
for fid in prepFidList1_force6:
print '\t',fid
print "We have selected our size-6 measurement fiducials to be:"
for fid in measFidList1_force6:
print '\t',fid
FS.test_fiducial_list(gs_target,prepFidList1_force6,'prep',returnAll=True)
FS.test_fiducial_list(gs_target,measFidList1_force6,'meas',returnAll=True)
#Lastly, let's compare to the "standard" 6-fiducial set we use as our default:
print "std1Q_XYI.fiducials =", std1Q_XYI.fiducials
print FS.test_fiducial_list(gs_target,std1Q_XYI.fiducials,'prep',returnAll=True)
print FS.test_fiducial_list(gs_target,std1Q_XYI.fiducials,'meas',returnAll=True)
The "standard" fiducials are very similar to the automatically selected ones, and score the same! The notable difference is that prep and measurement fiducials are different when automatically selected; our default sets are the same for both prep and measure. This is because each "standard" fiducial is symmetric; the automated fiducials reverse gate order between preparation and measure.