from .clifford import *
'''
Class of quantum error propagation graph
TODO: Optimize the algorithm to construct the QEPG
'''
[docs]
class QEPGpython:
def __init__(self,circuit:CliffordCircuit):
self._circuit = circuit
self._total_meas=self._circuit._totalMeas
self._total_noise=self._circuit._totalnoise
self._propMatrix=np.zeros((3*self._total_noise,len(self._circuit.parityMatchGroup)+1), dtype='uint8')
[docs]
def backword_graph_construction(self):
nqubit=self._circuit._qubit_num
#Keep track of the effect of X,Y,Z back propagation
column_size=len(self._circuit.parityMatchGroup)+1
current_x_prop=np.zeros((nqubit,column_size), dtype='uint8')
current_y_prop=np.zeros((nqubit,column_size), dtype='uint8')
current_z_prop=np.zeros((nqubit,column_size), dtype='uint8')
current_noise_index=self._circuit._totalnoise-1
current_meas_index=self._total_meas-1
total_noise=self._total_noise
T=len(self._circuit._gatelists)
for t in range(T-1,-1,-1):
#Update current_x_prop, current_y_prop, current_z_prop based on the current gate and measurement
gate=self._circuit._gatelists[t]
'''
If the gate is a oiginal noise, add edges to the graph based on current propogation
'''
if isinstance(gate, pauliNoise):
noiseindex=current_noise_index
#print("Noise!")
self._propMatrix[noiseindex,:]=current_x_prop[gate._qubitindex,:].copy()
self._propMatrix[total_noise+noiseindex,:]=current_y_prop[gate._qubitindex,:].copy()
self._propMatrix[total_noise*2+noiseindex,:]=current_z_prop[gate._qubitindex,:].copy()
current_noise_index-=1
continue
'''
When there is a measurement, update the current propogation based on the measurement
We just need to consider the propagation of X and Y because only
the X and Y error can be detected by the measurement
'''
if isinstance(gate, Measurement):
measureindex=current_meas_index
qindex=gate._qubitindex
if(measureindex in self._circuit.observable):
current_x_prop[qindex][column_size-1]=1
current_y_prop[qindex][column_size-1]=1
for parityIdx in self._circuit.get_measIdx_to_parityIdx(measureindex):
current_x_prop[qindex][parityIdx]=1
current_y_prop[qindex][parityIdx]=1
current_meas_index-=1
continue
if isinstance(gate,Reset):
current_x_prop[gate._qubitindex,:]=0
current_y_prop[gate._qubitindex,:]=0
current_z_prop[gate._qubitindex,:]=0
continue
'''
Deal with propagation by CNOT gate, we need to consider the propagation of X and Z
'''
if gate._name=="CNOT":
control=gate._control
target=gate._target
current_x_prop[control,:]=(current_x_prop[control,:]+current_x_prop[target,:])%2
current_z_prop[target,:]=(current_z_prop[control,:]+current_z_prop[target,:])%2
current_y_prop[control,:]=(current_y_prop[control,:]+current_x_prop[target,:])%2
current_y_prop[target,:]=(current_y_prop[target,:]+current_z_prop[control,:])%2
continue
'''
Deal with propagation by H gate
If there is a H gate, we need to swap the X and Z propagations
'''
if gate._name=="H":
qubitindex=gate._qubitindex
tmp_row=current_x_prop[qubitindex,:].copy()
current_x_prop[qubitindex,:]=current_z_prop[qubitindex,:]
current_z_prop[qubitindex,:]=tmp_row
continue
'''
Sample error and compute the detector value(Parity)
Return a result of the detected value
'''
[docs]
def sample_x_error(self, noise_index):
return list(self._propMatrix[noise_index,:])
[docs]
def sample_y_error(self, noise_index):
return list(self._propMatrix[self._total_noise+noise_index,:])
[docs]
def sample_z_error(self, noise_index):
return list(self._propMatrix[2*self._total_noise+noise_index,:])
[docs]
def sample_noise_vector(self,noise_vector):
assert len(noise_vector)==3*self._total_noise
return noise_vector@self._propMatrix
if __name__ == "__main__":
stim_str=""
filepath="C:/Users/username/Documents/Sampling/stimprograms/1cnot"
with open(filepath, "r", encoding="utf-8") as f:
stim_str = f.read()
circuit=CliffordCircuit(3)
circuit.set_error_rate(0.01)
circuit.compile_from_stim_circuit_str(stim_str)
graph=QEPGpython(circuit)
graph.backword_graph_construction()
print(graph.sample_noise_vector(np.array([1,0,0,0,0,0,0,0,0,0,0,0])))