Source code for qiskit_experiments.test.mock_iq_helpers
# This code is part of Qiskit.## (C) Copyright IBM 2021.## 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."""Probability and phase functions for the mock IQ backend."""fromabcimportabstractmethodfromtypingimportAny,Dict,List,Optional,TupleimportwarningsimportnumpyasnpfromqiskitimportQuantumCircuitfromqiskit.exceptionsimportQiskitErrorfromqiskit_aerimportAerSimulatorfromqiskit_experiments.frameworkimportBaseExperiment# Define an IQ point typing class.IQPoint=Tuple[float,float]
[docs]classMockIQExperimentHelper:"""Abstract class for the MockIQ helper classes. Different tests will use experiment specific helper classes which define the pattern of the IQ data that is then analyzed. """def__init__(self,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):"""Create a MockIQBackend helper object to define how the backend functions. :attr:`iq_cluster_centers` and :attr:`iq_cluster_width` define the base IQ cluster centers and standard deviations for each qubit in a :class:`MockIQBackend` instance. These are used by :meth:`iq_clusters` by default. Subclasses can override :meth:`iq_clusters` to return a modified version of :attr:`iq_cluster_centers` and :attr:`iq_cluster_width`. :attr:`iq_cluster_centers` is a list of tuples. For a given qubit ``i_qbt`` and computational state ``i_state`` (either `0` or `1`), the centers of the IQ clusters are found by indexing :attr:`iq_cluster_centers` as follows: .. code-block:: python iq_center = helper.iq_cluster_centers[i_qbt][i_state] center_inphase = iq_center[0] center_quadrature = iq_center[1] :attr:`iq_cluster_width` is indexed similarly except that there is only one width per qubit: i.e., the standard deviation of the IQ cluster for qubit ``i_qbt`` is .. code-block:: python iq_width = helper.iq_cluster_width[i_qbt] Subclasses must call ``super().__init__(iq_cluster_centers,iq_cluster_width)`` so that these properties are stored appropriately. Args: iq_cluster_centers: A list of tuples containing the clusters' centers in the IQ plane. There are different centers for different logical values of the qubit. Defaults to a single qubit with clusters in quadrants 1 and 3. iq_cluster_width: A list of standard deviation values for the sampling of each qubit. Defaults to widths of 1.0 for each qubit in :attr:`iq_cluster_centers`. """self._iq_cluster_centers=(iq_cluster_centersifiq_cluster_centersisnotNoneelse[((-1.0,-1.0),(1.0,1.0))])self._iq_cluster_width=(iq_cluster_widthifiq_cluster_widthisnotNoneelse[1.0]*len(self._iq_cluster_centers))@propertydefiq_cluster_centers(self)->List[Tuple[IQPoint,IQPoint]]:"""The base cluster centers in the IQ plane."""returnself._iq_cluster_centers@iq_cluster_centers.setterdefiq_cluster_centers(self,iq_cluster_centers:List[Tuple[IQPoint,IQPoint]]):"""Set the base cluster centers in the IQ plane."""self._iq_cluster_centers=iq_cluster_centers@propertydefiq_cluster_width(self)->List[float]:"""The base cluster widths in the IQ plane."""returnself._iq_cluster_width@iq_cluster_width.setterdefiq_cluster_width(self,iq_cluster_width:List[float]):"""Set the base cluster widths."""self._iq_cluster_width=iq_cluster_width
[docs]@abstractmethoddefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,Any]]:""" A function provided by the user which is used to determine the probability of each output of the circuit. The function returns a list of dictionaries, each containing output binary strings and their probabilities. Examples: **1 qubit circuit - excited state** In this experiment, we want to bring a qubit to its excited state and measure it. The circuit: .. parsed-literal:: ┌───┐┌─┐ q: ┤ X ├┤M├ └───┘└╥┘ c: 1/══════╩═ 0 The function that calculates the probability for this circuit doesn't need any calculation parameters: .. code-block:: @staticmethod def compute_probabilities(self, circuits: List[QuantumCircuit]) -> List[Dict[str, float]]: output_dict_list = [] for circuit in circuits: probability_output_dict = {"1": 1.0, "0": 0.0} output_dict_list.append(probability_output_dict) return output_dict_list **3 qubit circuit** In this experiment, we prepare a Bell state with the first and second qubit. In addition, we will bring the third qubit to its excited state. The circuit: .. parsed-literal:: ┌───┐ ┌─┐ q_0: ┤ H ├──■──┤M├─── └───┘┌─┴─┐└╥┘┌─┐ q_1: ─────┤ X ├─╫─┤M├ ┌───┐└┬─┬┘ ║ └╥┘ q_2: ┤ X ├─┤M├──╫──╫─ └───┘ └╥┘ ║ ║ c: 3/═══════╩═══╩══╩═ 2 0 1 When an output string isn't in the probability dictionary, the backend will assume its probability is 0. .. code-block:: @staticmethod def compute_probabilities(self, circuits: List[QuantumCircuit]) -> List[Dict[str, float]]: output_dict_list = [] for circuit in circuits: probability_output_dict = {} probability_output_dict["001"] = 0.5 probability_output_dict["111"] = 0.5 output_dict_list.append(probability_output_dict) return output_dict_list """
# pylint: disable=unused-argument
[docs]defiq_phase(self,circuits:List[QuantumCircuit])->List[float]:"""Sub-classes can override this method to introduce a phase in the IQ plane. This is needed, to test the resonator spectroscopy where the point in the IQ plane has a frequency-dependent phase rotation. """return[0.0]*len(circuits)
[docs]defiq_clusters(self,circuits:List[QuantumCircuit],)->List[Tuple[List[Tuple[IQPoint,IQPoint]],List[float]]]:"""Returns circuit-specific IQ cluster centers and widths in the IQ plane. Subclasses can override this function to modify the centers and widths of IQ clusters based on the circuits being simulated by a :class:`MockIQBackend`. The base centers and widths are stored internally within the helper object, and can be set in :meth:`__init__` or by modifying :attr:`iq_cluster_centers` and :attr:`iq_cluster_width`. The default behavior for :meth:`iq_clusters` is to return the centers and widths unmodified for each circuit in ``circuits``. Subclasses may return different centers and widths based on the circuits provided. The returned list contains a tuple per circuit. Each tuple contains the IQ centers and widths in the same format as :attr:`iq_cluster_centers` and :attr:`iq_cluster_width`, passed as arguments to :meth:`__init__`. The format of the centers and widths lists, in the argument list and in the returned tuples, must match the format of :attr:`iq_cluster_centers` and :attr:`iq_cluster_width` in :func:`qiskit_experiments.test.MockIQExperimentHelper.__init__`. Args: circuits: The quantum circuits for which the clusters should be modified. Returns: List: A list of tuples containing the circuit-specific IQ centers and widths for the provided circuits. """return[(self.iq_cluster_centers,self.iq_cluster_width)]*len(circuits)
[docs]classMockIQParallelExperimentHelper(MockIQExperimentHelper):"""Helper for Parallel experiment."""def__init__(self,exp_list:List[BaseExperiment],exp_helper_list:List[MockIQExperimentHelper],):""" Parallel Experiment Helper initializer. The class assumes `exp_helper_list` is ordered to match the corresponding experiment in `exp_list`. Note that :meth:`__init__` does not have :attr:`iq_cluster_centers` and :attr:`iq_cluster_width` as in :func:`MockIQExperimentHelper.__init__`. This is because the centers and widths for :class:`MockIQParallelBackend` are stored in multiple experiment helpers in the list `exp_helper_list`. Args: exp_list(List): List of experiments. exp_helper_list(List): Ordered list of :class:`.MockIQExperimentHelper` corresponding to the experiments in `exp_list`. Nested parallel experiment aren't supported currently. Raises: ValueError: Raised if the list are empty or if they don't have the same length. QiskitError: Raised if `exp_helper_list` contains an object of type ``MockIQParallelExperimentHelper``, because the parallel mock backend currently does not support parallel sub-experiments.`. Examples: **Parallel experiment for Resonator Spectroscopy** To run a parallel experiment of Resonator Spectroscopy on two qubits we will create two instances of `SpectroscopyHelper` objects (for each experiment) and an instance of `ParallelExperimentHelper` with them. .. code-block:: iq_cluster_centers = [ ((-1.0, 0.0), (1.0, 0.0)), ((0.0, -1.0), (0.0, 1.0)), ((3.0, 0.0), (5.0, 0.0)), ] parallel_backend = MockIQParallelBackend( experiment_helper=None, rng_seed=0, ) parallel_backend._configuration.basis_gates = ["x"] parallel_backend._configuration.timing_constraints = {"granularity": 16} # experiment parameters qubit1 = 0 qubit2 = 1 freq01 = parallel_backend.defaults().qubit_freq_est[qubit1] freq02 = parallel_backend.defaults().qubit_freq_est[qubit2] # experiments initialization frequencies1 = np.linspace(freq01 - 10.0e6, freq01 + 10.0e6, 23) frequencies2 = np.linspace(freq02 - 10.0e6, freq02 + 10.0e6, 21) exp_list = [ QubitSpectroscopy(qubit1, frequencies1), QubitSpectroscopy(qubit2, frequencies2), ] exp_helper_list = [ SpectroscopyHelper(iq_cluster_centers=iq_cluster_centers,), SpectroscopyHelper(iq_cluster_centers=iq_cluster_centers,), ] parallel_helper = ParallelExperimentHelper(exp_list, exp_helper_list) parallel_backend.experiment_helper = parallel_helper # initializing the parallel experiment par_experiment = ParallelExperiment(exp_list, backend=parallel_backend) par_experiment.set_run_options(meas_level=MeasLevel.KERNELED, meas_return="single") par_data = par_experiment.run().block_for_results() """# Set ParallelExperimentHelper iq_cluster_[centers,widths] to None as exp_helper_list contains# the necessary IQ cluster information.super().__init__(None,None)# check parametersself._verify_parameters(exp_list,exp_helper_list)self.exp_helper_list=exp_helper_listself.exp_list=exp_list
[docs]defcompute_probabilities(self,circuits:List[QuantumCircuit],)->List[Dict[str,Any]]:""" Run the compute_probabilities for each helper. Args: circuits: The quantum circuits for which the probabilities should be computed. Returns: List: A list of dictionaries containing computed probabilities and data for the given circuits. """# checking for legal parameters before computing output.self._verify_parameters(self.exp_list,self.exp_helper_list)# Splitting the circuitparallel_circ_list=self._parallel_exp_circ_splitter(circuits)number_of_experiments=len(self.exp_helper_list)prob_help_list=[{}for_inrange(number_of_experiments)]foridx,(exp_helper,experiment,experiment_circuits)inenumerate(zip(self.exp_helper_list,self.exp_list,parallel_circ_list)):# Get centers and widths for experiment_circuits and split into centers and widths lists.centers_and_widths=exp_helper.iq_clusters(experiment_circuits)exp_centers=[c_and_w[0]forc_and_wincenters_and_widths]exp_widths=[c_and_w[1]forc_and_wincenters_and_widths]prob_help_list[idx]={"physical_qubits":experiment.physical_qubits,"prob":exp_helper.compute_probabilities(experiment_circuits),"phase":exp_helper.iq_phase(experiment_circuits),"centers":exp_centers,"widths":exp_widths,"num_circuits":len(experiment_circuits),}returnprob_help_list
def_verify_parameters(self,exp_list:List[BaseExperiment]=None,exp_helper_list:List[MockIQExperimentHelper]=None,):"""Check parameters before computing probability"""ifexp_helper_listisNone:raiseValueError("Please set the experiment helper list.")ifexp_listisNone:raiseValueError("Please set the experiment list.")number_of_experiments=len(exp_list)number_of_helpers=len(exp_helper_list)ifnumber_of_experiments==0:raiseValueError("The experiment list cannot be empty.")ifnumber_of_helpers==0:raiseValueError("The experiment helper list cannot be empty.")ifnumber_of_experiments!=number_of_helpers:raiseValueError(f"The number of helpers {number_of_experiments} and the number of "f"experiment {number_of_helpers} don't match.")forhelperinexp_helper_list:# checking there is no nested parallel experiment.ifisinstance(helper,MockIQParallelExperimentHelper):raiseQiskitError("Nested parallel experiments aren't currently supported.")def_parallel_exp_circ_splitter(self,qc_list:List[QuantumCircuit]):""" Splits quantum circuits to their parallel components. Args: qc_list: The list of quantum circuits the backend gets as input. Returns: List: A list for each experiment. Each entry is a list of quantum circuits corresponding to that experiment. Raises: QiskitError: If an instruction is applied with qubits that don't belong to the same experiment. TypeError: The data type provided doesn't match the expected type (`tuple` or `int`). """exp_circuits_list=[[]for_inself.exp_list]qubits_expid_map={exp.physical_qubits:ifori,expinenumerate(self.exp_list)}forqcinqc_list:# initialize quantum circuit for each experiment for this instance of circuit to fill# with instructions.foriinrange(len(self.exp_list)):# we copy the circuit to ensure that the circuit properties (e.g. calibrations and qubit# frequencies) are the same in the new circuit.empty_qc=qc.copy_empty_like()empty_qc.metadata.clear()exp_circuits_list[i].append(empty_qc)# fixing metadataforexp_idx,sub_metadatainzip(qc.metadata["composite_index"],qc.metadata["composite_metadata"],):exp_circuits_list[exp_idx][-1].metadata=sub_metadata.copy()# sorting instructions by qubits indexes and inserting them into a circuit of the relevant# experimentfordatainqc.data:inst=data.operationqarg=data.qubitscarg=data.clbitsqubit_indices=set(qc.find_bit(qr).indexforqrinqarg)forqubits,exp_idxinqubits_expid_map.items():ifqubit_indices.issubset(qubits):exp_circuits_list[exp_idx][-1].append(inst,qarg,carg)breakelse:raiseQiskitError("A gate operates on two qubits that don't belong to the same experiment.")# deleting empty circuitsforexp_circuitsinexp_circuits_list:# 'exp_circuits' is a list of circuits of a specific experimentifnotexp_circuits[-1].data:exp_circuits.pop()returnexp_circuits_list
classMockIQDragHelper(MockIQExperimentHelper):"""Functions needed for test_drag"""def__init__(self,gate_name:str="Rp",ideal_beta:float=2.0,frequency:float=0.02,max_probability:float=1.0,offset_probability:float=0.0,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):""" Args: gate_name: name of the gate to count when determining the number of gate repetitions, i.e., positive rotation followed by negative rotation, in the circuit. ideal_beta: the beta where the minimum of the Drag patterns will be. frequency: controls the frequency of the oscillation in the measured Drag pattern. max_probability: a factor to scale the maximum probability of measuring an excited state to allow tests to factor in non-ideal situations. offset_probability: a constant offset applied to all probabilities to reflect non-ideal measurement situations. iq_cluster_centers: A list of tuples containing the clusters' centers in the IQ plane. There are different centers for different logical values of the qubit. iq_cluster_width: A list of standard deviation values for the sampling of each qubit. Raises: ValueError: If probability value is not valid. """super().__init__(iq_cluster_centers,iq_cluster_width)ifmax_probability+offset_probability>1:raiseValueError("Probabilities need to be between 0 and 1.")self.gate_name=gate_nameself.ideal_beta=ideal_betaself.frequency=frequencyself.max_probability=max_probabilityself.offset_probability=offset_probabilitydefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Returns the probability based on the beta, number of gates, and leakage."""gate_name=self.gate_nameideal_beta=self.ideal_betafreq=self.frequencymax_prob=self.max_probabilityoffset_prob=self.offset_probabilityifmax_prob+offset_prob>1:raiseValueError("Probabilities need to be between 0 and 1.")output_dict_list=[]forcircuitincircuits:probability_output_dict={}n_gates=circuit.count_ops()[gate_name]beta=next(iter(circuit.calibrations[gate_name].keys()))[1][0]# Dictionary of output string vectors and their probabilityprob=np.sin(2*np.pi*n_gates*freq*(beta-ideal_beta)/4)**2probability_output_dict["1"]=max_prob*prob+offset_probprobability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listclassMockIQFineDragHelper(MockIQExperimentHelper):"""Functions needed for Fine Drag Experiment"""def__init__(self,error:float=0.03,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):super().__init__(iq_cluster_centers,iq_cluster_width)self.error=errordefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Returns the probability based on error per gate."""error=self.erroroutput_dict_list=[]forcircuitincircuits:probability_output_dict={}n_gates=circuit.count_ops().get("rz",0)//2# Dictionary of output string vectors and their probabilityprobability_output_dict["1"]=0.5*np.sin(n_gates*error)+0.5probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listclassMockIQRabiHelper(MockIQExperimentHelper):"""Functions needed for Rabi experiment on mock IQ backend"""def__init__(self,amplitude_to_angle:float=np.pi,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):""" Args: amplitude_to_angle: maps a pulse amplitude to a rotation angle. """warnings.warn("MockIQRabiHelper has been deprecated. It will be removed ""in Qiskit Experiments 0.5.",DeprecationWarning,stacklevel=2,)super().__init__(iq_cluster_centers,iq_cluster_width)self.amplitude_to_angle=amplitude_to_angledefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Returns the probability based on the rotation angle and amplitude_to_angle."""amplitude_to_angle=self.amplitude_to_angleoutput_dict_list=[]forcircuitincircuits:probability_output_dict={}amp=next(iter(circuit.calibrations["Rabi"].keys()))[1][0]# Dictionary of output string vectors and their probabilityprobability_output_dict["1"]=np.sin(amplitude_to_angle*amp)**2probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listdefrabi_rate(self)->float:"""Returns the rabi rate."""returnself.amplitude_to_angle/np.piclassMockIQFineFreqHelper(MockIQExperimentHelper):"""Functions needed for Fine Frequency experiment on mock IQ backend"""def__init__(self,sx_duration:float=160,freq_shift:float=0,dt:float=1e-9,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):""" Args: sx_duration: duration of the single-qubit sx gate. freq_shift: the detuning from the ideal frequency that this mock backend will mimic. dt: duration of a sample. """super().__init__(iq_cluster_centers,iq_cluster_width)self.sx_duration=sx_durationself.freq_shift=freq_shiftself.dt=dtdefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Return the probability of being in the excited state."""sx_duration=self.sx_durationfreq_shift=self.freq_shiftdt=self.dtsimulator=AerSimulator(method="automatic")output_dict_list=[]forcircuitincircuits:probability_output_dict={}delay=Noneforinstructionincircuit.data:ifinstruction.operation.name=="delay":delay=instruction.operation.durationifdelayisNone:probability_output_dict={"1":1,"0":0}else:reps=delay//sx_durationqc=QuantumCircuit(1)qc.sx(0)qc.rz(np.pi*reps/2+2*np.pi*freq_shift*delay*dt,0)qc.sx(0)qc.measure_all()counts=simulator.run(qc,seed_simulator=1).result().get_counts(0)probability_output_dict["1"]=counts.get("1",0)/sum(counts.values())probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listclassMockIQFineAmpHelper(MockIQExperimentHelper):"""Functions needed for Fine Amplitude experiment on mock IQ backend"""def__init__(self,angle_error:float=0,angle_per_gate:float=0,gate_name:str="x",iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):""" Args: angle_error: rotation angle error per gate. angle_per_gate: the intended rotation angle per gate. gate_name: name of the gate that will be counted to determine the total rotation. """super().__init__(iq_cluster_centers,iq_cluster_width)self.angle_error=angle_errorself.angle_per_gate=angle_per_gateself.gate_name=gate_namedefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Return the probability of being in the excited state."""angle_error=self.angle_errorangle_per_gate=self.angle_per_gategate_name=self.gate_nameoutput_dict_list=[]forcircuitincircuits:probability_output_dict={}n_ops=circuit.count_ops().get(gate_name,0)angle=n_ops*(angle_per_gate+angle_error)ifgate_name!="sx":angle+=np.pi/2*circuit.count_ops().get("sx",0)ifgate_name!="x":angle+=np.pi*circuit.count_ops().get("x",0)# Dictionary of output string vectors and their probabilityprobability_output_dict["1"]=np.sin(angle/2)**2probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listclassMockIQRamseyXYHelper(MockIQExperimentHelper):"""Functions needed for Ramsey XY experiment on mock IQ backend"""def__init__(self,t2ramsey:float=100e-6,freq_shift:float=0,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):super().__init__(iq_cluster_centers,iq_cluster_width)self.t2ramsey=t2ramseyself.freq_shift=freq_shiftdefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Return the probability of being in the excited state."""t2ramsey=self.t2ramseyfreq_shift=self.freq_shiftoutput_dict_list=[]forcircuitincircuits:probability_output_dict={}series=circuit.metadata["series"]delay=circuit.metadata["xval"]ifseries=="X":phase_offset=0.0else:phase_offset=np.pi/2probability_output_dict["1"]=(0.5*np.exp(-delay/t2ramsey)*np.cos(2*np.pi*delay*freq_shift-phase_offset)+0.5)probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listclassMockIQSpectroscopyHelper(MockIQExperimentHelper):"""Functions needed for Spectroscopy experiment on mock IQ backend"""def__init__(self,gate_name:str="Spec",freq_offset:float=0.0,line_width:float=2e6,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):""" Args: gate_name: the gate name to look for when calculating frequency shift. freq_offset: frequency offset from resonance that this mock backend will mimic. line_width: line width of the resonance of the spectroscopy signal. """super().__init__(iq_cluster_centers,iq_cluster_width)self.freq_offset=freq_offsetself.line_width=line_widthself.gate_name=gate_namedefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Returns the probability based on the parameters provided."""freq_offset=self.freq_offsetline_width=self.line_widthoutput_dict_list=[]forcircuitincircuits:probability_output_dict={}ifself.gate_name=="measure":freq_shift=(next(iter(circuit.calibrations[self.gate_name].values())).blocks[0].frequency)elifself.gate_name=="Spec":freq_shift=next(iter(circuit.calibrations[self.gate_name]))[1][0]else:raiseValueError(f"The gate name {str(self.gate_name)} isn't supported.")delta_freq=freq_shift-freq_offsetprobability_output_dict["1"]=np.abs(1/(1+2.0j*delta_freq/line_width))probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listdefiq_phase(self,circuits:List[QuantumCircuit])->List[float]:"""Add a phase to the IQ point depending on how far we are from the resonance. This will cause the IQ points to rotate around in the IQ plane when we approach the resonance, introducing extra complication that the data processor has to properly handle. """delta_freq_list=[0.0]*len(circuits)ifself.gate_name=="measure":forcirc_idx,circinenumerate(circuits):freq_shift=next(iter(circ.calibrations["measure"].values())).blocks[0].frequencydelta_freq_list[circ_idx]=freq_shift-self.freq_offsetphase=[delta_freq/self.line_widthfordelta_freqindelta_freq_list]returnphaseclassMockIQReadoutAngleHelper(MockIQExperimentHelper):"""Functions needed for Readout angle experiment on mock IQ backend"""defcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Return the probability of being in the excited state."""output_dict_list=[]forcircuitincircuits:probability_output_dict={"1":1-circuit.metadata["xval"]}probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listclassMockIQHalfAngleHelper(MockIQExperimentHelper):"""Functions needed for Half Angle experiment on mock IQ backend"""def__init__(self,error:float=0,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):super().__init__(iq_cluster_centers,iq_cluster_width)self.error=errordefcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Return the probability of being in the excited state."""error=self.erroroutput_dict_list=[]forcircuitincircuits:probability_output_dict={}n_gates=circuit.metadata["xval"]# Dictionary of output string vectors and their probabilityprobability_output_dict["1"]=(0.5*np.sin((-1)**(n_gates+1)*n_gates*error)+0.5)probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_listclassMockIQT1Helper(MockIQExperimentHelper):"""Functions needed for T1 experiment on mock IQ backend"""def__init__(self,t1:float=None,iq_cluster_centers:Optional[List[Tuple[IQPoint,IQPoint]]]=None,iq_cluster_width:Optional[List[float]]=None,):super().__init__(iq_cluster_centers,iq_cluster_width)self._t1=t1or90e-6defcompute_probabilities(self,circuits:List[QuantumCircuit])->List[Dict[str,float]]:"""Return the probability of being in the excited state."""output_dict_list=[]forcircuitincircuits:probability_output_dict={}# extracting information from the circuit.delay=circuit.metadata["xval"]# creating a probability dict.probability_output_dict["1"]=np.exp(-delay/self._t1)probability_output_dict["0"]=1-probability_output_dict["1"]output_dict_list.append(probability_output_dict)returnoutput_dict_list