Skip to content

Commit

Permalink
Started refactor to use dictionaries
Browse files Browse the repository at this point in the history
  • Loading branch information
ashenmill committed Jun 7, 2024
1 parent 582787d commit b4a1b66
Show file tree
Hide file tree
Showing 9 changed files with 591 additions and 14 deletions.
109 changes: 97 additions & 12 deletions jupyter_notebooks/Examples/Propagatable error gens tutorial.ipynb

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions pygsti/extras/errorgenpropagation/errordict_deprecated.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pygsti.extras.errorgenpropagation.propagatableerrorgen import propagatableerrorgen
from numpy import complex128

class errordict(dict):

def __setitem__(self, __key: any, __value: any) -> None:
if __key in self :
super().__setitem__(__key,self[__key]+__value)
else:
super().__setitem__(__key,__value)
4 changes: 2 additions & 2 deletions pygsti/extras/errorgenpropagation/errorpropagator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def ErrorPropagator(circ,errorModel,MultiGateDict={},BCHOrder=1,BCHLayerwise=Fal
if not ErrorLayerDef:
errorLayers=buildErrorlayers(circ,errorModel,len(circ.line_labels))
else:
errorLayers=[[errorModel]]*circ.depth
errorLayers=[[errorModel]]*circ.depth #this doesn't work

num_error_layers=len(errorLayers)
fully_propagated_layers=[]
Expand Down Expand Up @@ -209,7 +209,7 @@ def buildErrorlayers(circ,errorDict,qubits):
paulis.append(p2)
errorLayer.append(propagatableerrorgen(errType,paulis,gErrorDict[errs]))
ErrorGens.append([errorLayer])
print(ErrorGens)

return ErrorGens


Expand Down
172 changes: 172 additions & 0 deletions pygsti/extras/errorgenpropagation/errorpropagator_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import stim
from localstimerrorgen import *
from numpy import abs,zeros, complex128
from numpy.linalg import multi_dot
from scipy.linalg import expm
from pygsti.tools.internalgates import standard_gatenames_stim_conversions
from utilserrorgenpropagation import *


def ErrorPropagator(circ,errorModel,MultiGateDict=None,BCHOrder=1,BCHLayerwise=False,NonMarkovian=False,MultiGate=False,ErrorLayerDef=False):
if MultiGate and MultiGateDict is None:
MultiGateDict=dict()
stim_dict=standard_gatenames_stim_conversions()
if MultiGate:
for key in MultiGateDict:
stim_dict[key]=stim_dict[MultiGateDict[key]]
stim_layers=circ.convert_to_stim_tableau_layers(gate_name_conversions=stim_dict)
stim_layers.pop(0) #Immediatly toss the first layer because it is not important,

propagation_layers=[]
if not BCHLayerwise or NonMarkovian:
while len(stim_layers) != 0:
top_layer=stim_layers.pop(0)
for layer in stim_layers:
top_layer = layer*top_layer
propagation_layers.append(top_layer)
else:
propagation_layers = stim_layers

if not ErrorLayerDef:
errorLayers=buildErrorlayers(circ,errorModel,len(circ.line_labels))
else:
errorLayers=[[errorModel]]*circ.depth #this doesn't work

num_error_layers=len(errorLayers)

fully_propagated_layers=[]
for _ in range(0,num_error_layers-1):
err_layer=errorLayers.pop(0)
layer=propagation_layers.pop(0)
new_error_layer=[]
for err_order in err_layer:
new_error_dict=dict()
for key in err_order:
propagated_error_gen=key.propagate_error_gen_tableau(layer,err_order[key])
new_error_dict[propagated_error_gen[0]]=propagated_error_gen[1]
new_error_layer.append(new_error_dict)
if BCHLayerwise and not NonMarkovian:
following_layer = errorLayers.pop(0)
new_errors=BCH_Handler(err_layer,following_layer,BCHOrder)
errorLayers.insert(new_errors,0)
else:
fully_propagated_layers.append(new_error_layer)

fully_propagated_layers.append(errorLayers.pop(0))
if BCHLayerwise and not NonMarkovian:
final_error=dict()
for order in errorLayers[0]:
for error in order:
if error in final_error:
final_error[error]=final_error[error]+order[error]
else:
final_error[error]=order[error]
return final_error

elif not BCHLayerwise and not NonMarkovian:
simplified_EOC_errors=dict()
if BCHOrder == 1:
for layer in fully_propagated_layers:
for order in layer:
for error in order:
if error in simplified_EOC_errors:
simplified_EOC_errors[error]=simplified_EOC_errors[error]+order[error]
else:
simplified_EOC_errors[error]=order[error]

else:
Exception("Higher propagated through Errors are not Implemented Yet")
return simplified_EOC_errors

else:
return fully_propagated_layers



def buildErrorlayers(circ,errorDict,qubits):
ErrorGens=[]
#For the jth layer of each circuit
for j in range(circ.depth):
l = circ.layer(j) # get the layer
errorLayer=dict()
for _, g in enumerate(l): # for gate in layer l
gErrorDict = errorDict[g.name] #get the errors for the gate
p1=qubits*'I' # make some paulis why?
p2=qubits*'I'
for errs in gErrorDict: #for an error in the accompanying error dictionary
errType=errs[0]
paulis=[]
for ind,el in enumerate(g): #enumerate the gate ind =0 is name ind = 1 is first qubit ind = 2 is second qubit
if ind !=0: #if the gate element of concern is not the name
p1=p1[:el] + errs[1][ind-1] +p1[(el+1):]

paulis.append(stim.PauliString(p1))
if errType in "CA":
for ind,el in enumerate(g):
if ind !=0:
p2=p2[:el] + errs[2][ind-1] +p2[(el+1):]
paulis.append(stim.PauliString(p2))
errorLayer[localstimerrorgen(errType,paulis)]=gErrorDict[errs]
ErrorGens.append([errorLayer])
return ErrorGens
'''
Inputs:
_______
err_layer (list of dictionaries)
following_layer (list of dictionaries)
BCHOrder:
'''
def BCH_Handler(err_layer,following_layer,BCHOrder):
new_errors=[]
for curr_order in range(0,BCHOrder):
working_order=dict()
#add first order terms into new layer
if curr_order == 0:
for error_key in err_layer[curr_order]:
working_order[error_key]=err_layer[curr_order][error_key]
for error_key in following_layer[curr_order]:
working_order[error_key]=following_layer[curr_order[error_key]]
new_errors.append(working_order)

elif curr_order ==1:
working_order={}
for error1 in err_layer[curr_order-1]:
for error2 in following_layer[curr_order-1]:
errorlist = commute_errors(error1,error2,BCHweight=1/2*err_layer[error1]*following_layer[error2])
for error_tuple in errorlist:
working_order[error_tuple[0]]=error_tuple[1]
if len(err_layer)==2:
for error_key in err_layer[1]:
working_order[error_key]=err_layer[1][error_key]
if len(following_layer)==2:
for error_key in following_layer[1]:
working_order[error_key]=following_layer[1][error_key]
new_errors.append(working_order)

else:
Exception("Higher Orders are not Implemented Yet")
return new_errors

# There's a factor of a half missing in here.
def nm_propagators(corr, Elist,qubits):
Kms = []
for idm in range(len(Elist)):
Am=zeros([4**qubits,4**qubits],dtype=complex128)
for key in Elist[idm][0]:
Am += key.toWeightedErrorBasisMatrix()
# This assumes that Elist is in reverse chronological order
partials = []
for idn in range(idm, len(Elist)):
An=zeros([4**qubits,4**qubits],dtype=complex128)
for key2 in Elist[idn][0]:
An = key2.toWeightedErrorBasisMatrix()
partials += [corr[idm,idn] * Am @ An]
partials[0] = partials[0]/2
Kms += [sum(partials,0)]
return Kms

def averaged_evolution(corr, Elist,qubits):
Kms = nm_propagators(corr, Elist,qubits)
return multi_dot([expm(Km) for Km in Kms])
96 changes: 96 additions & 0 deletions pygsti/extras/errorgenpropagation/localstimerrorgen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from pygsti.baseobjs.errorgenlabel import ElementaryErrorgenLabel
from pygsti.extras.errorgenpropagation.utilspygstistimtranslator import *
import stim
from numpy import array,kron
from pygsti.tools import change_basis
from pygsti.tools.lindbladtools import create_elementary_errorgen

class localstimerrorgen(ElementaryErrorgenLabel):


'''
Initiates the errorgen object
Inputs:
______
errorgen_type: characture can be set to 'H' Hamiltonian, 'S' Stochastic, 'C' Correlated or 'A' active following the conventions
of the taxonomy of small markovian errorgens paper
basis_element_labels
Outputs:
Null
'''
def __init__(self,errorgen_type: str ,basis_element_labels: list):
self.errorgen_type=str(errorgen_type)
self.basis_element_labels=tuple(basis_element_labels)

'''
hashes the error gen object
'''
def __hash__(self):
pauli_hashable=[]
for pauli in self.basis_element_labels:
pauli_hashable.append(str(pauli))
return hash((self.errorgen_type,tuple(pauli_hashable)))

def labels_to_strings(self):
strings=[]
for paulistring in self.basis_element_labels:
strings.append(str(paulistring)[1:].replace('_',"I"))
return tuple(strings)


'''
checks and if two error gens have the same type and labels
'''
def __eq__(self, other):
return (self.errorgen_type == other.errorgen_type
and self.basis_element_labels == other.basis_element_labels)

'''
displays the errorgens as strings
'''
def __str__(self):
return self.errorgen_type + "(" + ",".join(map(str, self.basis_element_labels)) + ")"


def __repr__(self):
return str((self.errorgen_type, self.basis_element_labels))

'''
Returns the errorbasis matrix for the associated errorgenerator mulitplied by its error rate
input: A pygsti defined matrix basis by default can be pauli-product, gellmann 'gm' or then pygsti standard basis 'std'
functions defaults to pauli product if not specified
'''
def toWeightedErrorBasisMatrix(self,weight=1.0,matrix_basis='pp'):
PauliDict={
'I' : array([[1.0,0.0],[0.0,1.0]]),
'X' : array([[0.0j, 1.0+0.0j], [1.0+0.0j, 0.0j]]),
'Y' : array([[0.0, -1.0j], [1.0j, 0.0]]),
'Z' : array([[1.0, 0.0j], [0.0j, -1.0]])
}
paulis=[]
for paulistring in self.basis_element_labels:
for idx,pauli in enumerate(paulistring):
if idx == 0:
pauliMat = PauliDict[pauli]
else:
pauliMat=kron(pauliMat,PauliDict[pauli])
paulis.append(pauliMat)
if self.errorgen_type in 'HS':
return weight*change_basis(create_elementary_errorgen(self.errorgen_type,paulis[0]),'std',matrix_basis)
else:
return weight*change_basis(create_elementary_errorgen(self.errorgen_type,paulis[0],paulis[1]),'std',matrix_basis)

def propagate_error_gen_tableau(self, slayer,weight):
new_basis_labels = []
weightmod = 1
for pauli in self.basis_element_labels:
temp = slayer(pauli)
weightmod=weightmod*temp.sign
temp=temp*temp.sign
new_basis_labels.append(temp)

return (localstimerrorgen(self.errorgen_type,new_basis_labels),weightmod*weight)

15 changes: 15 additions & 0 deletions pygsti/extras/errorgenpropagation/propagatableerrorgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ def getP1(self):
'''
def getP2(self):
return self.basis_element_labels[1]

def ErrorWeight(self):
def Weight(pauli):
weight=0
for char in pauli:
if char is 'I':
continue
else:
weight+=1
return weight
if len(self.basis_element_labels)==1 or Weight(self.basis_element_labels[0]) > Weight(self.basis_element_labels[1]):
return Weight(self.basis_element_labels[0])
else:
return Weight(self.basis_element_labels[1])


'''
propagates a propagatableerrorgen object through a clifford layer, returns the created error gen
Expand Down
Loading

0 comments on commit b4a1b66

Please sign in to comment.