Source code for qiskit_experiments.test.t2hahn_backend
# This code is part of Qiskit.## (C) Copyright IBM 2022.## This code is licensed under the Apache License, Version 2.0. You may# obtain a copy of this license in the LICENSE.txt file in the root directory# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.## Any modifications or derivative works of this code must retain this# copyright notice, and modified files need to carry a notice indicating# that they have been altered from the originals."""T2HahnBackend class.Temporary backend to be used for t2hahn experiment"""importcopyfromtypingimportList,Optional,Sequence,UnionimportnumpyasnpfromqiskitimportQiskitError,QuantumCircuitfromqiskit.circuitimportDelay,Reset,Parameterfromqiskit.circuit.libraryimportMeasure,RZGate,SXGate,XGatefromqiskit.dagcircuitimportDAGCircuitfromqiskit.providersimportBackendV2,Job,Optionsfromqiskit.transpilerimportInstructionProperties,PassManager,Target,TransformationPassfromqiskit.utils.unitsimportapply_prefixfromqiskit_aerimportAerSimulatorfromqiskit_aer.noiseimportNoiseModel,ReadoutError,RelaxationNoisePass,reset_errorfromqiskit_experiments.test.utilsimportFakeJobclassResetQubits(TransformationPass):"""Pass to inject reset instructions for each qubit The resets are used to add qubit initialization error. """defrun(self,dag:DAGCircuit):new_dag=copy.deepcopy(dag)forqreginnew_dag.qregs.values():new_dag.apply_operation_front(Reset(),qreg,[])returnnew_dagclassQubitDrift(TransformationPass):"""Pass to rotate qubits during delays This pass adds rotations that mimic the qubit being detuned from the drive frequency (while assuming that the gate times are negligible; rotations are only added for delays). """def__init__(self,qubit_frequencies:Sequence[float],dt:float):super().__init__()self.qubit_frequencies=qubit_frequenciesself.dt=dtdefrun(self,dag:DAGCircuit):qubit_indices={bit:indexforindex,bitinenumerate(dag.qubits)}new_dag=dag.copy_empty_like()fornodeindag.topological_op_nodes():new_dag.apply_operation_back(node.op,node.qargs,node.cargs)ifnode.name=="delay":q0=qubit_indices[node.qargs[0]]duration=0ifself.qubit_frequencies[q0]isNone:continueifnode.op.unit=="dt":duration=node.op.duration*self.dtelifnode.op.unit!="s":duration=apply_prefix(node.op.duration,node.op.unit)angle=2*np.pi*self.qubit_frequencies[q0]*durationangle=angle%(2*np.pi)new_dag.apply_operation_back(RZGate(angle),[node.qargs[0]],[])returnnew_dag
[docs]classT2HahnBackend(BackendV2):""" A simple and primitive backend, to be run by the T2Hahn tests """def__init__(self,t2hahn:Union[float,Sequence[float]]=float("inf"),frequency:Union[float,Sequence[float]]=0.0,initialization_error:Union[float,Sequence[float]]=0.0,readout0to1:Union[float,Sequence[float]]=0.0,readout1to0:Union[float,Sequence[float]]=0.0,seed:int=9000,dt:float=1/4.5e9,num_qubits:Optional[int]=None,):""" Initialize the T2Hahn backend """super().__init__(name="T2Hahn_simulator",backend_version="0",)forargin(t2hahn,frequency,initialization_error,readout0to1,readout1to0):ifisinstance(arg,Sequence):ifnum_qubitsisNone:num_qubits=len(arg)eliflen(arg)!=num_qubits:raiseValueError(f"Input lengths are not consistent: {num_qubits} != {len(arg)}")ifnum_qubitsisNone:num_qubits=1self._t2hahn=t2hahnifisinstance(t2hahn,Sequence)else[t2hahn]*num_qubitsself._frequency=frequencyifisinstance(frequency,Sequence)else[frequency]*num_qubitsself._initialization_error=(initialization_errorifisinstance(initialization_error,Sequence)else[initialization_error]*num_qubits)self._readout0to1=(readout0to1ifisinstance(readout0to1,Sequence)else[readout0to1]*num_qubits)self._readout1to0=(readout1to0ifisinstance(readout1to0,Sequence)else[readout1to0]*num_qubits)self._seed=seedself._target=Target(dt=dt,num_qubits=num_qubits)forinstructionin(Measure(),Reset(),RZGate(Parameter("angle")),SXGate(),XGate()):self.target.add_instruction(instruction,properties={(q,):InstructionProperties(duration=0)forqinrange(num_qubits)},)self.target.add_instruction(Delay(Parameter("duration")))@propertydeftarget(self)->Target:returnself._target@propertydefmax_circuits(self)->None:returnNone@classmethoddef_default_options(cls)->Options:returnOptions()
[docs]defrun(self,run_input:Union[QuantumCircuit,List[QuantumCircuit]],shots:int=1024,**options)->Job:passes=[]ifisinstance(run_input,QuantumCircuit):circuits=[run_input]else:circuits=run_inputforcircuitincircuits:ifcircuit.num_qubits>self.num_qubits:raiseQiskitError(f"{self.__class__} can only run circuits that match its num_qubits")noise_model=NoiseModel()ifself._initialization_errorisnotNone:passes.append(ResetQubits())forqubit,errorinenumerate(self._initialization_error):iferrorisNone:continuenoise_model.add_quantum_error(reset_error(1-error,error),["reset"],[qubit])ifany(self._readout0to1)orany(self._readout1to0):forqubit,(err0to1,err1to0)inenumerate(zip(self._readout0to1,self._readout1to0)):error=ReadoutError([[1-err0to1,err0to1],[err1to0,1-err1to0]])noise_model.add_readout_error(error,[qubit])ifany(t2!=float("inf")fort2inself._t2hahn):# Make T1 huge so only T2 matterspasses.append(RelaxationNoisePass([t*100fortinself._t2hahn],self._t2hahn,self.dt,Delay))ifany(self._frequency):passes.append(QubitDrift(self._frequency,self.dt))pm=PassManager(passes)new_circuits=pm.run(circuits)sim=AerSimulator(noise_model=noise_model,seed_simulator=self._seed)job=sim.run(new_circuits,shots=shots,**options)returnFakeJob(self,job.result())