#!/usr/bin/env python # coding: utf-8 # # Circuit Lists Tutorial # 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](../Circuit.ipynb)). # In[ ]: import pygsti import pygsti.construction as pc # In[ ]: from pygsti.modelpacks import smq1Q_XY #a standard model, packaged with related information # ## List Construction Functions: `pygsti.construction` and `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.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: # In[ ]: 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. # In[ ]: 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. # In[ ]: 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. # In[ ]: 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))) # In[ ]: 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))) # ## Manipulating `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 # # - ab $\rightarrow$ AB' (if B follows A, prime B) # - BA $\rightarrow$ B''A (if B precedes A, double-prime B) # - CA $\rightarrow$ CA' (if A follows C, prime A) # - BC $\rightarrow$ BC' (if C follows B, prime C) # # are specified by the dictionary: # In[ ]: sequenceRules = [ (("A", "B"), ("A", "B'")), (("B", "A"), ("B''", "A")), (("C", "A"), ("C", "A'")), (("B", "C"), ("B", "C'"))] # Will produce the output: # - BAB $\rightarrow$ B''AB' # - ABA $\rightarrow$ AB'A (frustrated!) # - CAB $\rightarrow$ CA'B' # - ABC $\rightarrow$ AB'C' # In[ ]: 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)) # In[ ]: # 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])) # ## Gate Label "Aliases" # 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). # In[ ]: #TODO: remove Aliasing or provide examples?