PyGSTi comes shipped with a number of standard modules. These standard modules live in pygsti.construction
and have names beginning with std
, so you import them like this:
from pygsti.construction import std1Q_XYI
Each standard module defines a 1- or 2-qubit model and number of related quantites. Sometimes you'll just want to use the Model
, and importing a standard module is just a convenient way to create a commonly used model for 1 or 2 qubits (the std1Q_XYI
module is for the 1-qubit model containing Idle, $X(\pi/2)$ and $Y(\pi/2)$ gates). A standard module's model always contains perfect (unitary) operations, and is called the target model because often times this is the model you wish described your system. You can get a copy of it by using the target_model
function:
mdl = std1Q_XYI.target_model()
print(mdl)
rho0 = FullSPAMVec with dimension 4 0.71 0 0 0.71 Mdefault = UnconstrainedPOVM with effect vectors: 0: FullSPAMVec with dimension 4 0.71 0 0 0.71 1: FullSPAMVec with dimension 4 0.71 0 0-0.71 Gi = FullDenseOp with shape (4, 4) 1.00 0 0 0 0 1.00 0 0 0 0 1.00 0 0 0 0 1.00 Gx = FullDenseOp with shape (4, 4) 1.00 0 0 0 0 1.00 0 0 0 0 0-1.00 0 0 1.00 0 Gy = FullDenseOp with shape (4, 4) 1.00 0 0 0 0 0 0 1.00 0 0 1.00 0 0-1.00 0 0
Now let's review a few things about this target model:
It's a copy. If you modify it, it won't change what's stored in the standard module. This means that you don't need to add a .copy()
(e.g. mdl = std1Q_XYI.target_model().copy()
).
It's fully parameterized. By default, target_model()
returns a fully-parameterized Model
, meaning that each of its operations contain an independent parameter for each one of their elements. If you want a different parameterization, such as a TP-constrained model, you can specify this as an argument:
mdl_TP = std1Q_XYI.target_model("TP")
.operations
dictionary) are simple strings like "Gx"
or "Gix"
or "Gcnot"
. That is, these names label layers rather than gates per se. A more multi-qubit friendly convention would be to label these operations ("Gx",0)
, ("Gx",1)
, or ("Gcnot",0,1)
, respectively. If you want to use a standard module using multi-qubit-friendly conventions, you can convert the standard module to a "standard multiqubit module" like this:import pygsti
pygsti.construction.stdmodule_to_smqmodule(std1Q_XYI) # makes "smq1Q_XYI" importable
from pygsti.construction import smq1Q_XYI
smq_mdl = smq1Q_XYI.target_model()
print(smq_mdl.operations.keys())
odict_keys([Label{Gi}, Label[Gx:0], Label[Gy:0]])
For convenience standard modules contain description
and gates
members giving a simple text description of the standard module's target model and its gates:
std1Q_XYI.description
'Idle, X(pi/2), and Y(pi/2) gates'
std1Q_XYI.gates
['Gi', 'Gx', 'Gy']
In addition to a target Model
, a standard module contains a number of Circuit
lists used for running Gate Set Tomography (GST). These include:
prepStrs
effectStrs
germs_lite
is a shorter list of germ circuits that amplify all the errors in the target model to first order. This is usually all that is needed to achieve the high-accuracy typically desired from GST results, and so we recommend starting with this list of germs since it's shorter.germs
is a longer list of germ circuits that amplify all the errors in the target model to higher orders. Although typically unnecessary, this "paranoid" set of germs can be particularly helpful when you expect and don't care about some departures (errors) from the target model.global_fidPairs_lite
and global_fidPairs
are lists of 2-tuples giving the indices (within prepStrs
and effectStrs
) of the fiducial circuits to keep when implementing global fiducial pair reduction.pergerm_fidPairsDict_lite
and pergerm_fidPairsDict
are dictionaries of lists-of-2-tuples giving the indices of the fiducial circuits to keep on a per-germ basis (dict keys are germ circuits) when implementing per-germ fiducial pair reduction.Here are a couple examples:
std1Q_XYI.prepStrs
[Circuit({}), Circuit(Gx), Circuit(Gy), Circuit(GxGx), Circuit(GxGxGx), Circuit(GyGyGy)]
std1Q_XYI.pergerm_fidPairsDict_lite
{('Gx',): [(1, 1), (3, 4), (4, 2), (5, 5)], ('Gi',): [(0, 3), (1, 1), (5, 5)], ('Gy',): [(0, 2), (2, 2), (2, 4), (4, 4)], ('Gx', 'Gy'): [(0, 0), (0, 4), (2, 5), (5, 4)], ('Gx', 'Gx', 'Gy'): [(1, 3), (1, 4), (3, 5), (5, 0), (5, 4), (5, 5)]}
smq1Q_XYI.prepStrs #multi-qubit friendly version
[Circuit({}), Circuit(Gx:0@(0)), Circuit(Gy:0@(0)), Circuit(Gx:0Gx:0@(0)), Circuit(Gx:0Gx:0Gx:0@(0)), Circuit(Gy:0Gy:0Gy:0@(0))]
Standard Clifford-based randomized benchmarking (RB) requires knowing how to "compile" the elements of the Clifford group from your native gate set. Most standard modules contain a clifford_compilation
dictionary that describes this compilation, and can be used when running Clifford RB (see the Clifford RB tutorial for more info).
std1Q_XYI.clifford_compilation
OrderedDict([('Gc0', ['Gi']), ('Gc1', ['Gy', 'Gx']), ('Gc2', ['Gx', 'Gx', 'Gx', 'Gy', 'Gy', 'Gy']), ('Gc3', ['Gx', 'Gx']), ('Gc4', ['Gy', 'Gy', 'Gy', 'Gx', 'Gx', 'Gx']), ('Gc5', ['Gx', 'Gy', 'Gy', 'Gy']), ('Gc6', ['Gy', 'Gy']), ('Gc7', ['Gy', 'Gy', 'Gy', 'Gx']), ('Gc8', ['Gx', 'Gy']), ('Gc9', ['Gx', 'Gx', 'Gy', 'Gy']), ('Gc10', ['Gy', 'Gx', 'Gx', 'Gx']), ('Gc11', ['Gx', 'Gx', 'Gx', 'Gy']), ('Gc12', ['Gy', 'Gx', 'Gx']), ('Gc13', ['Gx', 'Gx', 'Gx']), ('Gc14', ['Gx', 'Gy', 'Gy', 'Gy', 'Gx', 'Gx', 'Gx']), ('Gc15', ['Gy', 'Gy', 'Gy']), ('Gc16', ['Gx']), ('Gc17', ['Gx', 'Gy', 'Gx']), ('Gc18', ['Gy', 'Gy', 'Gy', 'Gx', 'Gx']), ('Gc19', ['Gx', 'Gy', 'Gy']), ('Gc20', ['Gx', 'Gy', 'Gy', 'Gy', 'Gx']), ('Gc21', ['Gy']), ('Gc22', ['Gx', 'Gx', 'Gx', 'Gy', 'Gy']), ('Gc23', ['Gx', 'Gy', 'Gx', 'Gx', 'Gx'])])