"""MBQC simulatorSimulates MBQC by executing the pattern."""importwarningsimportnumpyasnpfromgraphix.noise_modelsimportNoiseModelfromgraphix.sim.density_matriximportDensityMatrixBackendfromgraphix.sim.statevecimportStatevectorBackendfromgraphix.sim.tensornetimportTensorNetworkBackend
[docs]classPatternSimulator:"""MBQC simulator Executes the measurement pattern. """
[docs]def__init__(self,pattern,backend="statevector",noise_model=None,**kwargs):""" Parameters ----------- pattern: :class:`graphix.pattern.Pattern` object MBQC pattern to be simulated. backend: str, 'statevector', 'densitymatrix or 'tensornetwork' simulation backend (optional), default is 'statevector'. noise_model: kwargs: keyword args for specified backend. .. seealso:: :class:`graphix.sim.statevec.StatevectorBackend`\ :class:`graphix.sim.tensornet.TensorNetworkBackend`\ :class:`graphix.sim.density_matrix.DensityMatrixBackend`\ """# check that pattern has output nodes configured# assert len(pattern.output_nodes) > 0ifbackend=="statevector"andnoise_modelisNone:self.noise_model=Noneself.backend=StatevectorBackend(pattern,**kwargs)elifbackend=="densitymatrix":ifnoise_modelisNone:self.noise_model=None# no noise: no need to compute probabilitiesself.backend=DensityMatrixBackend(pattern,**kwargs)warnings.warn("Simulating using densitymatrix backend with no noise. To add noise to the simulation, give an object of `graphix.noise_models.Noisemodel` to `noise_model` keyword argument.")ifnoise_modelisnotNone:self.set_noise_model(noise_model)# if noise: have to compute the probabilitiesself.backend=DensityMatrixBackend(pattern,pr_calc=True,**kwargs)elifbackendin{"tensornetwork","mps"}andnoise_modelisNone:self.noise_model=Noneself.backend=TensorNetworkBackend(pattern,**kwargs)# TODO or just do the noiseless sim with a warning?elifbackendin{"statevector","tensornetwork","mps"}andnoise_modelisnotNone:raiseValueError(f"The backend {backend} doesn't support noise but noisemodel was provided.")else:raiseValueError("Unknown backend.")self.pattern=patternself.node_index=[]
[docs]defrun(self):"""Perform the simulation. Returns ------- state : the output quantum state, in the representation depending on the backend used. """self.backend.add_nodes(self.pattern.input_nodes)ifself.noise_modelisNone:forcmdinself.pattern:ifcmd[0]=="N":self.backend.add_nodes([cmd[1]])elifcmd[0]=="E":self.backend.entangle_nodes(cmd[1])elifcmd[0]=="M":self.backend.measure(cmd)elifcmd[0]=="X":self.backend.correct_byproduct(cmd)elifcmd[0]=="Z":self.backend.correct_byproduct(cmd)elifcmd[0]=="C":self.backend.apply_clifford(cmd)else:raiseValueError("invalid commands")self.backend.finalize()else:self.noise_model.assign_simulator(self)fornodeinself.pattern.input_nodes:self.backend.apply_channel(self.noise_model.prepare_qubit(),[node])forcmdinself.pattern:ifcmd[0]=="N":# prepare clean qubit and apply channelself.backend.add_nodes([cmd[1]])self.backend.apply_channel(self.noise_model.prepare_qubit(),[cmd[1]])elifcmd[0]=="E":# for "E" cmd[1] is already a tuypleself.backend.entangle_nodes(cmd[1])# for some reaon entangle doesn't get the whole commandself.backend.apply_channel(self.noise_model.entangle(),cmd[1])elifcmd[0]=="M":# apply channel before measuring, then measur and confuse_resultself.backend.apply_channel(self.noise_model.measure(),[cmd[1]])self.backend.measure(cmd)self.noise_model.confuse_result(cmd)elifcmd[0]=="X":self.backend.correct_byproduct(cmd)ifnp.mod(np.sum([self.results[j]forjincmd[2]]),2)==1:self.backend.apply_channel(self.noise_model.byproduct_x(),[cmd[1]])elifcmd[0]=="Z":self.backend.correct_byproduct(cmd)ifnp.mod(np.sum([self.results[j]forjincmd[2]]),2)==1:self.backend.apply_channel(self.noise_model.byproduct_z(),[cmd[1]])elifcmd[0]=="C":self.backend.apply_clifford(cmd)self.backend.apply_channel(self.noise_model.clifford(),[cmd[1]])elifcmd[0]=="T":# T command is a flag for one clock cycle in simulated experiment,# to be added via hardware-agnostic pattern modifierself.noise_model.tick_clock()else:raiseValueError("Invalid commands.")self.backend.finalize()returnself.backend.state