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.

In [1]:

```
#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.)

In [2]:

```
#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 achieve

`slackFrac`

: The relative score a fiducial set is allowed to achieve

Note: **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 size

`scoreFunc`

: 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 space

Here 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.

In [3]:

```
#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)
```

`optimize_integer_fiducials_slack`

function below throws a divide by zero warning;
this just means one of the tested cases was *really* bad.

In [4]:

```
#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
```

In [5]:

```
# 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
```

In [6]:

```
print "We have selected our preparation fiducials to be:"
for fid in prepFidList1:
print '\t',fid
```

In [7]:

```
print "We have selected our measurement fiducials to be:"
for fid in measFidList1:
print '\t',fid
```

*informational completeness*. Below, we test whether our selected preparation and measurement fiducuials are in fact so.

In [8]:

```
FS.test_fiducial_list(gs_target,prepFidList1,'prep',returnAll=True)
```

Out[8]:

In [9]:

```
FS.test_fiducial_list(gs_target,measFidList1,'meas',returnAll=True)
```

Out[9]:

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`

.)In [10]:

```
#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)
```

In [11]:

```
#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
```

In [12]:

```
#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!
```

In [13]:

```
#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
```

In [14]:

```
#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
```

In [15]:

```
print "We have selected our size-6 preparation fiducials to be:"
for fid in prepFidList1_force6:
print '\t',fid
```

In [16]:

```
print "We have selected our size-6 measurement fiducials to be:"
for fid in measFidList1_force6:
print '\t',fid
```

In [17]:

```
FS.test_fiducial_list(gs_target,prepFidList1_force6,'prep',returnAll=True)
```

Out[17]:

In [18]:

```
FS.test_fiducial_list(gs_target,measFidList1_force6,'meas',returnAll=True)
```

Out[18]:

In [19]:

```
#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)
```