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 Circuit
s, 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.circuits as pc
from pygsti.modelpacks import smq1Q_XY #a standard model, packaged with related information
pygsti.circuits.create_circuits
¶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.circuits.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 create_circuits
, the 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)))
Circuits
¶Sometimes it is useful to manipulate a circuits
(or a list of them) via find & replace operations. The manipulate_circuit
and 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.circuits import Circuit
from pygsti.circuits 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.circuits.to_circuits([ tuple('BAB'), tuple('ABA'), tuple('CAB'), tuple('ABC')])
lst = pygsti.circuits.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?