This tutorial will show you how to create lists of
Circuit objects. In some cases, we'll want to construct and use a list (or even a list of lists!) of
Circuits, so we've dedicated this tutorial to talking about "circuit lists".
We utilize the fact that
Circuit objects behave and compose as tuples of layer-labels (see the Circuit tutorial).
import pygsti import pygsti.construction as pc
from pygsti.modelpacks import smq1Q_XY #a standard model, packaged with related information
You'll often be working with entire lists of
Circuit objects which define some part of the experiments utilized by algorithms such as Gate Set Tomography. pyGSTi provides several functions for constructing circuit lists, which we not demonstrate.
The workhorse function is
pygsti.construction.create_circuits, which executes its positional arguments within a nested loop given by iterable keyword arguments. That's a mouthful, so let's look at a few examples:
As = [('a1',),('a2',)] Bs = [('b1','b2'), ('b3','b4')] def rep2(x): return x+x list1 = pc.create_circuits("a", a=As) list2 = pc.create_circuits("a+b", a=As, b=Bs, order=['a','b']) list3 = pc.create_circuits("R(a)+c", a=As, c=[('c',)], R=rep2) print("list1 = %s" % list(map(tuple, list1))) print("list2 = %s" % list2) print("list3 = %s" % list(map(str,list3)))
Many of the operation sequences used by Gate Set Tomography are composed of three parts. A "preparation fiducial" sequence is followed by a "repeated germ" sequence, which is followed by a "measurement fiducial" sequence. We won't get into why this structure is used, but simply use this fact to motivate looking at operation sequences of the form $f_1 + R(g) + f_2$, where the $f_1$ and $f_2$ fiducial sequences are simple short sequences are $R(g)$ is a possibly long sequence that is generated by repeating a short sequence $g$ called a "germ".
It is possible to generate "repeated germ" sequences in several ways using the functions
pygsti.construction.repeat_xxx . In modern GST, germ sequences are always repeated an integer number of times rather than being truncated to a precise length, so
repeat_with_max_length is used instead of
repeat_and_truncate. Below we demonstrate the use of these functions.
print(pc.repeat_and_truncate(('A', 'B', 'C'), 5)) #args (x,N): repeat x until it is exactly length N print(pc.repeat_with_max_length(('A', 'B', 'C'), 5)) #args (x,N): repeat x the maximum integer number of times so len(x) < N print(pc.repeat_count_with_max_length(('A', 'B', 'C'), 5)) #args (x,N): the maximum integer number of times so len(x) < N
We can combine a repeated germ sequence between two fiducial sequences using
create_circuits. This demonstrates the power of the
create_circuits to perform nested loops. We also introduce the "bulk-conversion" function
circuit_list, which creates a list of
Circuit objects from a list of tuples.
fids = pc.to_circuits( [ ('Gf0',), ('Gf1',) ] ) #fiducial strings germs = pc.to_circuits( [ ('G0',), ('G1a','G1b')] ) #germ strings circuits1 = pc.create_circuits("f0+germ*e+f1", f0=fids, f1=fids, germ=germs, e=2, order=["germ","f0","f1"]) print("circuits1 = \n", "\n".join(map(str,circuits1)),"\n") circuits2 = pc.create_circuits("f0+T(germ,N)+f1", f0=fids, f1=fids, germ=germs, N=3, T=pc.repeat_and_truncate, order=["germ","f0","f1"]) print("circuits2 = \n", "\n".join(map(str,circuits2)),"\n") circuits3 = pc.create_circuits("f0+T(germ,N)+f1", f0=fids, f1=fids, germ=germs, N=3, T=pc.repeat_with_max_length, order=["germ","f0","f1"]) print("circuits3 = \n", "\n".join(map(str,circuits3)), "\n")
In addition to
pygsti.construction.list_xxx functions provide ways of constructing common operation sequence lists. The example below shows how to construct all possible operation sequences within a certain length range, as well as how to construct the set of operation sequences needed to run Linear LinearOperator Set Tomography given a set of fiducial strings.
myGates = [ 'Gx', 'Gy' ] #operation labels -- often just model.operations.keys() allStringsInLengthRange = pc.list_all_circuits(myGates, minlength=0, maxlength=2) print("\nAll strings using %s up to length 2 = \n" \ % str(myGates), "\n".join(map(str,allStringsInLengthRange)))
myFiducialList = pc.to_circuits([ ('Gf1',), ('Gf2',) ]) #list of fiducials lgstStrings = pc.create_lgst_circuits(myFiducialList,myFiducialList,myGates) print("\nLGST strings = \n","\n".join(map(str,lgstStrings)))
Sometimes it is useful to manipulate a
circuits (or a list of them) via find & replace operations. The
manipulate_circuits functions take as input a set of replacement "rules" and process one or more
circuits objects accordingly. For example, the rules
are specified by the dictionary:
sequenceRules = [ (("A", "B"), ("A", "B'")), (("B", "A"), ("B''", "A")), (("C", "A"), ("C", "A'")), (("B", "C"), ("B", "C'"))]
Will produce the output:
from pygsti.objects import Circuit from pygsti.construction import manipulate_circuit print(manipulate_circuit(Circuit(tuple('BAB')), sequenceRules)) print(manipulate_circuit(Circuit(tuple('ABA')), sequenceRules)) print(manipulate_circuit(Circuit(tuple('CAB')), sequenceRules)) print(manipulate_circuit(Circuit(tuple('ABC')), sequenceRules))
# You can also process an entire list of operation sequences in bulk orig_lst = pygsti.construction.to_circuits([ tuple('BAB'), tuple('ABA'), tuple('CAB'), tuple('ABC')]) lst = pygsti.construction.manipulate_circuits(orig_lst, sequenceRules) print('\n'.join([str(s) for s in lst]))
A similar but simpler type of manipulation called "operation label aliasing" is used in pyGSTi to map a operation label into another operation label only for
DataSet lookups. The mapping is similar to
manipulate_circuit's find & replace functionality, except that (at least currently) the string to find can be only a single operation label (and so isn't even a string at all). The support for operation label aliasing within pyGSTi's algorithms aids in mapping many
Model models onto the same data (often with simpler gate labelling).
#TODO: remove Aliasing or provide examples?