In [1]:
from __future__ import print_function

In [2]:
#Import relevant modules.

import pygsti
import numpy as _np

from pygsti.algorithms import fiducialselection as FS

import matplotlib.pyplot as plt
%matplotlib inline

import time


In this notebook, we'll demonstrate how to select preparation and measurement fiducials for a standard two-qubit gate set. By "standard", we mean that a) measurements are made in the computational (Z) basis (and state prep is |00>), and b) gate set consists of independent X pi/2 and Y pi/2 gates on each qubit. Presumably there will be additional entangling gates available; however, we do not want (or need) such gates in our fiducial gate strings. (Two-qubit operations will typically be of lower fidelity, so it is "safer" to use single-qubit operations for fiducials.)

In [3]:
#Build the gate set.  As mentioned above, no entangling operation is included; these results will be general for
#any two-qubit gate set that has access to the Gix, Giy, Gxi, and Gyi gates
#(and prepares in the state |00> and performs measurements in the computational basis).
gs_target = pygsti.construction.build_gateset( [4], [('Q0','Q1')],['Gix','Giy','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': (0,0), 'updn': (0,1), 'dnup': (0,2), 'dndn': (0,-1) },
basis="pp")

In [4]:
#Let's try to pick out a fiducial set.

#First, we generate a candidate set which we'll attempt to prune.
#We could look at all gate strings of up to a fixed length (using pygsti.construction.list_all_gatestrings),
#but that grows quite rapidly.
#Instead, we'll look at the tensor product of the standard 1-qubit fiducial set with itself.
#This product set we define below.

#{} x 1q fid list
emptyList = pygsti.construction.gatestring_list([
(),
('Gix',),
('Gix','Gix'),
('Gix','Gix','Gix'),
('Giy',),
('Giy','Giy','Giy')
])

#Gx x 1q fid list
XList = pygsti.construction.gatestring_list([
('Gxi',),
('Gxi','Gix',),
('Gxi','Gix','Gix'),
('Gxi','Gix','Gix','Gix'),
('Gxi','Giy',),
('Gxi','Giy','Giy','Giy')
])

#GxGx x 1q fid list
XXList = pygsti.construction.gatestring_list([
('Gxi','Gxi'),
('Gxi','Gxi','Gix',),
('Gxi','Gxi','Gix','Gix'),
('Gxi','Gxi','Gix','Gix','Gix'),
('Gxi','Gxi','Giy',),
('Gxi','Gxi','Giy','Giy','Giy')
])

#GxGxGx x 1q fid list
XXXList = pygsti.construction.gatestring_list([
('Gxi','Gxi','Gxi'),
('Gxi','Gxi','Gxi','Gix',),
('Gxi','Gxi','Gxi','Gix','Gix'),
('Gxi','Gxi','Gxi','Gix','Gix','Gix'),
('Gxi','Gxi','Gxi','Giy',),
('Gxi','Gxi','Gxi','Giy','Giy','Giy')
])

#Gy x 1q fid list
YList = pygsti.construction.gatestring_list([
('Gyi',),
('Gyi','Gix',),
('Gyi','Gix','Gix'),
('Gyi','Gix','Gix','Gix'),
('Gyi','Giy',),
('Gyi','Giy','Giy','Giy')
])

#Gy x 1q fid list
YYYList = pygsti.construction.gatestring_list([
('Gyi','Gyi'),
('Gyi','Gyi','Gyi','Gix',),
('Gyi','Gyi','Gyi','Gix','Gix'),
('Gyi','Gyi','Gyi','Gix','Gix','Gix'),
('Gyi','Gyi','Gyi','Giy',),
('Gyi','Gyi','Gyi','Giy','Giy','Giy')
])

testFidList = emptyList + XList + XXList + XXXList + YList + YYYList

In [5]:
#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.

In [6]:
#We know that we should be able to find a prep fiducial set that has no more than 16 elements,
#so if we are finding sets that are larger than that, we can always increase slackFrac or fixedSlack
start = time.time()
prepFidList1_all = FS.optimize_integer_fiducials_slack(gs_target,testFidList,prepOrMeas='prep',initialWeights=None,
scoreFunc='all',slackFrac=.275)
end = time.time()
print('')
print("Fiducial selection completed in", end-start, "seconds.")
print("\n".join(map(str,sorted(prepFidList1_all,key=len))))

Complete initial fiducial set succeeds.
Now searching for best fiducial set.
Starting fiducial set optimization. Lower score is better.
score = 1468.8
weights = [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 1 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1]
L1(weights) = 16
Final fiducial set succeeds.

Fiducial selection completed in 0.23148202896118164 seconds.
{}
GyiGiy
GxiGxiGiy
GyiGixGix
GxiGxiGxiGix
GxiGxiGxiGiy
GyiGixGixGix
GyiGiyGiyGiy
GyiGyiGyiGiy
GxiGxiGixGixGix
GxiGxiGiyGiyGiy
GxiGxiGxiGixGix
GyiGyiGyiGixGix
GxiGxiGxiGixGixGix
GyiGyiGyiGixGixGix
GyiGyiGyiGiyGiyGiy

In [7]:
#We know that we should be able to find a prep fiducial set that has no more than 16 elements,
#so if we are finding sets that are larger than that, we can always increase slackFrac or fixedSlack
start = time.time()
prepFidList1_worst = FS.optimize_integer_fiducials_slack(gs_target,testFidList,prepOrMeas='prep',initialWeights=None,
scoreFunc='worst',slackFrac=.275)
end = time.time()
print('')
print("Fiducial selection completed in", end-start, "seconds.")
print("\n".join(map(str,sorted(prepFidList1_worst,key=len))))

Complete initial fiducial set succeeds.
Now searching for best fiducial set.
Starting fiducial set optimization. Lower score is better.
score = 249.001323854
weights = [1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0]
L1(weights) = 16
Final fiducial set succeeds.

Fiducial selection completed in 0.2091379165649414 seconds.
{}
Giy
GyiGyi
GixGixGix
GxiGxiGix
GxiGxiGxi
GyiGixGix
GyiGixGixGix
GyiGiyGiyGiy
GyiGyiGyiGix
GyiGyiGyiGiy
GxiGxiGiyGiyGiy
GxiGxiGxiGixGix
GyiGyiGyiGixGix
GxiGxiGxiGixGixGix
GxiGxiGxiGiyGiyGiy

In [8]:
#We know that there might exist a fiducial measurement set with as few as 6 elements (as 6*3=18>16).
#However, repeated attempts to find one to date have failed.  We can reliably identify fiducial measurement sets
#with only 9 elements, so 9 should be considered an upper bound.  (If you do find a set with fewer than 9 elements,
#the pyGSTi team would love to hear from you!)
start = time.time()
measFidList1_all = FS.optimize_integer_fiducials_slack(gs_target,testFidList,prepOrMeas='meas',initialWeights=None,
scoreFunc='all',slackFrac=1)
end = time.time()
print('')
print("Fiducial selection completed in", end-start, "seconds.")
print("\n".join(map(str,sorted(measFidList1_all,key=len))))

Complete initial fiducial set succeeds.
Now searching for best fiducial set.
Starting fiducial set optimization. Lower score is better.
score = 316.130180659
weights = [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 0 1]
L1(weights) = 9
Final fiducial set succeeds.

Fiducial selection completed in 0.3222181797027588 seconds.
{}
GxiGxiGxiGiy
GyiGyiGyiGix
GxiGxiGixGixGix
GxiGxiGiyGiyGiy
GxiGxiGxiGixGix
GyiGyiGyiGixGix
GxiGxiGxiGixGixGix
GyiGyiGyiGiyGiyGiy

In [9]:
#Let's try the same as above, but with "worst" instead of "all" as the scoreFunc.
start = time.time()
measFidList1_worst = FS.optimize_integer_fiducials_slack(gs_target,testFidList,prepOrMeas='meas',initialWeights=None,
scoreFunc='worst',slackFrac=1)
end = time.time()
print('')
print("Fiducial selection completed in", end-start, "seconds.")
print("\n".join(map(str,sorted(measFidList1_worst,key=len))))

Complete initial fiducial set succeeds.
Now searching for best fiducial set.
Starting fiducial set optimization. Lower score is better.
score = 48.4574110103
weights = [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0]
L1(weights) = 9
Final fiducial set succeeds.

Fiducial selection completed in 0.314039945602417 seconds.
{}
GxiGxiGxiGix
GyiGyiGyiGiy
GxiGxiGixGixGix
GxiGxiGiyGiyGiy
GxiGxiGxiGixGix
GyiGyiGyiGixGix
GxiGxiGxiGiyGiyGiy
GyiGyiGyiGixGixGix

In [10]:
FS.test_fiducial_list(gs_target, prepFidList1_all,'prep', scoreFunc='all', returnAll=True)

Out[10]:
(True,
[0.035777874246103326,
0.094384513594891764,
0.12472064408483972,
0.18406325009774369,
0.21922359359558477,
0.24878825006702784,
0.32992842070695388,
0.49999999999999961,
0.60106985528731904,
0.8952780966140732,
1.0000000000000002,
1.2601374932937572,
1.5082401339544393,
1.9793250220729006,
2.280776406404414,
4.7382864459799485],
Score: 1152.0000000000025, N: 16)
In [11]:
print("prep fid_all spectrum:\n", FS.test_fiducial_list(gs_target, prepFidList1_all, 'prep',
returnAll=True)[1])
print("prep fid_all 'all-score':", FS.compute_composite_score(gs_target, prepFidList1_all, 'prep',
scoreFunc='all').score)
print("prep fid_all 'worst-score':", FS.compute_composite_score(gs_target, prepFidList1_all, 'prep',
scoreFunc='worst').score)

prep fid_all spectrum:
[0.035777874246103757, 0.094384513594892402, 0.12472064408484011, 0.18406325009774313, 0.21922359359558505, 0.24878825006702732, 0.32992842070695388, 0.5, 0.60106985528731915, 0.89527809661407276, 1.0, 1.2601374932937566, 1.5082401339544393, 1.9793250220729006, 2.2807764064044158, 4.7382864459799485]
prep fid_all 'all-score': 1152.0
prep fid_all 'worst-score': 447.20376314

In [12]:
print("prep fid_worst spectrum:\n", FS.test_fiducial_list(gs_target, prepFidList1_worst, 'prep',
returnAll=True)[1])
print("prep fid_worst 'all-score':", FS.compute_composite_score(gs_target, prepFidList1_worst, 'prep',
scoreFunc='all').score)
print("prep fid_worst 'worst-score':", FS.compute_composite_score(gs_target, prepFidList1_worst, 'prep',
scoreFunc='worst').score)

prep fid_worst spectrum:
[0.081927275262132032, 0.11388855211719048, 0.1320152782977117, 0.1951852156926718, 0.27205660994856667, 0.29289321881345226, 0.49999999999999944, 0.49999999999999967, 0.50000000000000011, 0.65555661468669424, 0.91892639567648526, 1.114993642934855, 1.7071067811865468, 2.1841101287185047, 2.1951284422577562, 4.6362118444074252]
prep fid_worst 'all-score': 832.0
prep fid_worst 'worst-score': 195.295155964

In [13]:
#Interestingly, using the option "worst" instead of "all" yields a better scoring fiducial set, by both the "worst"
#and "all".

In [14]:
print("prep meas_all spectrum:\n", FS.test_fiducial_list(gs_target, measFidList1_all, 'meas',
returnAll=True)[1])
print("prep meas_all 'all-score':", FS.compute_composite_score(gs_target, measFidList1_all, 'meas',
scoreFunc='all').score)
print("prep meas_all 'worst-score':", FS.compute_composite_score(gs_target, measFidList1_all, 'meas',
scoreFunc='worst').score)

prep meas_all spectrum:
[0.3714602085566765, 0.50000000000000056, 0.56368584497043994, 0.64534972638934507, 0.71248175049995766, 0.71922359359558552, 0.744102543127522, 0.74999999999999989, 0.75000000000000011, 1.9166803725509385, 2.0000000000000022, 2.1610191647597001, 2.5209138803963618, 2.7710731135452389, 2.780776406404418, 7.0932333952038285]
prep meas_all 'all-score': 158.065090329
prep meas_all 'worst-score': 24.2287055051

In [15]:
print("prep meas_worst spectrum:\n", FS.test_fiducial_list(gs_target, measFidList1_worst, 'meas',
returnAll=True)[1])
print("prep meas_worst 'all-score':", FS.compute_composite_score(gs_target, measFidList1_worst, 'meas',
scoreFunc='all').score)
print("prep meas_worst 'worst-score':", FS.compute_composite_score(gs_target, measFidList1_worst, 'meas',
scoreFunc='worst').score)

prep meas_worst spectrum:
[0.3714602085566765, 0.50000000000000056, 0.56368584497044039, 0.64534972638934496, 0.71248175049995877, 0.71922359359558552, 0.74410254312752211, 0.74999999999999978, 0.75000000000000089, 1.9166803725509376, 2.0000000000000022, 2.1610191647597015, 2.52091388039636, 2.7710731135452402, 2.780776406404418, 7.093233395203832]
prep meas_worst 'all-score': 158.065090329
prep meas_worst 'worst-score': 24.2287055051

In [16]:
for i in range(len(measFidList1_all)):
print(sorted(measFidList1_all,key=len)[i], '\t', sorted(measFidList1_worst,key=len)[i], '\t', sorted(measFidList1_all,key=len)[i] == sorted(measFidList1_worst,key=len)[i])

{} 	 {} 	 True
GxiGxiGxiGiy 	 GxiGxiGxiGix 	 False
GyiGyiGyiGix 	 GyiGyiGyiGiy 	 False
GxiGxiGixGixGix 	 GxiGxiGixGixGix 	 True
GxiGxiGiyGiyGiy 	 GxiGxiGiyGiyGiy 	 True
GxiGxiGxiGixGix 	 GxiGxiGxiGixGix 	 True
GyiGyiGyiGixGix 	 GyiGyiGyiGixGix 	 True
GxiGxiGxiGixGixGix 	 GxiGxiGxiGiyGiyGiy 	 False
GyiGyiGyiGiyGiyGiy 	 GyiGyiGyiGixGixGix 	 False

In [17]:
#We have the same scores for "all" and "worst" for measurement fiducials, even though the fiducial sets themselves
#are not quite the same.

In [18]:
#Lastly, let's see if we can find a minimal set of measurement fiducials (size 6), using the same input set as before.

In [19]:
start = time.time()
measFidList1_all_force6 = FS.optimize_integer_fiducials_slack(gs_target,testFidList,prepOrMeas='meas',initialWeights=None,fixedNum=6,
scoreFunc='all',slackFrac=1)
end = time.time()
print('')
print("Fiducial selection completed in", end-start, "seconds.")
print("\n".join(map(str,sorted(measFidList1_worst,key=len))))

Complete initial fiducial set succeeds.
Now searching for best fiducial set.
Starting fiducial set optimization. Lower score is better.
Output set is required to be of size6
Total number of fiducial sets to be checked is324632.0

WARNING: If this is very large, you may wish to abort.

Switching!
Switching!
Switching!
Switching!

Fiducial selection completed in 103.61686706542969 seconds.
{}
GxiGxiGxiGix
GyiGyiGyiGiy
GxiGxiGixGixGix
GxiGxiGiyGiyGiy
GxiGxiGxiGixGix
GyiGyiGyiGixGix
GxiGxiGxiGiyGiyGiy
GyiGyiGyiGixGixGix

In [20]:
FS.test_fiducial_list(gs_target,measFidList1_all_force6,'meas',scoreFunc='all',returnAll=True)

Out[20]:
(False,
[0.0,
2.4995843092626328e-16,
2.7770928464217906e-16,
3.8492813519304379e-16,
0.42672875571022356,
0.4934886775389975,
0.6563680176830814,
0.67679045677623229,
0.74999999999999989,
1.0000000000000004,
1.0000000000000013,
1.4193625622157191,
1.7710921857106274,
2.1291505505848565,
2.645811040896072,
5.0312077528842014],
Score: 78.11868131868127, N: 12)
In [21]:
#Sadly, this did not work!  However, one could try different input sets (or increasing fixedNum to 7 or 8, which would
#still be better than 9.)

In [ ]: