Source code for qiskit_experiments.library.quantum_volume.qv_analysis
# 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."""Quantum Volume analysis class."""importmathimportwarningsfromtypingimportListimportnumpyasnpimportuncertaintiesfromqiskit_experiments.exceptionsimportAnalysisErrorfromqiskit_experiments.frameworkimport(BaseAnalysis,AnalysisResultData,Options,)fromqiskit_experiments.visualizationimportBasePlotter,MplDrawer
[docs]classQuantumVolumePlotter(BasePlotter):"""Plotter for QuantumVolumeAnalysis .. note:: This plotter only supports one series, named ``hops``, which it expects to have an ``individual`` data key containing the individual heavy output probabilities for each circuit in the experiment. Additional series will be ignored. """
[docs]@classmethoddefexpected_series_data_keys(cls)->List[str]:"""Returns the expected series data keys supported by this plotter. Data Keys: individual: Heavy-output probability fraction for each individual circuit """return["individual"]
[docs]@classmethoddefexpected_supplementary_data_keys(cls)->List[str]:"""Returns the expected figures data keys supported by this plotter. Data Keys: depth: The depth of the quantun volume circuits used in the experiment """return["depth"]
[docs]defset_supplementary_data(self,**data_kwargs):"""Sets supplementary data for the plotter. Args: data_kwargs: See :meth:`expected_supplementary_data_keys` for the expected supplementary data keys. """# Hook method to capture the depth for inclusion in the plot titleif"depth"indata_kwargs:self.set_figure_options(figure_title=(f"Quantum Volume experiment for depth {data_kwargs['depth']}"" - accumulative hop"),)super().set_supplementary_data(**data_kwargs)
[docs]classQuantumVolumeAnalysis(BaseAnalysis):r"""A class to analyze quantum volume experiments. # section: overview Calculate the quantum volume of the analysed system. The quantum volume is determined by the largest successful circuit depth. A depth is successful if it has `mean heavy-output probability` > 2/3 with confidence level > 0.977 (corresponding to z_value = 2), and at least 100 trials have been ran. we assume the error (standard deviation) of the heavy output probability is due to a binomial distribution. The standard deviation for binomial distribution is :math:`\sqrt{(np(1-p))}`, where :math:`n` is the number of trials and :math:`p` is the success probability. """@classmethoddef_default_options(cls)->Options:"""Return default analysis options. Analysis Options: plot (bool): Set ``True`` to create figure for fit result. ax (AxesSubplot): Optional. A matplotlib axis object to draw. plotter (BasePlotter): Plotter object to use for figure generation. """options=super()._default_options()options.plot=Trueoptions.ax=Noneoptions.plotter=QuantumVolumePlotter(MplDrawer())returnoptionsdef_run_analysis(self,experiment_data):data=experiment_data.data()num_trials=len(data)depth=Noneheavy_output_prob_exp=[]fordata_trialindata:trial_depth=data_trial["metadata"]["depth"]ifdepthisNone:depth=trial_deptheliftrial_depth!=depth:raiseAnalysisError("QuantumVolume circuits do not all have the same depth.")heavy_output=self._calc_ideal_heavy_output(data_trial["metadata"]["ideal_probabilities"],trial_depth)heavy_output_prob_exp.append(self._calc_exp_heavy_output_probability(data_trial,heavy_output))hop_result,qv_result=self._calc_quantum_volume(heavy_output_prob_exp,depth,num_trials)ifself.options.plot:self.options.plotter.set_series_data("hops",individual=hop_result.extra["HOPs"])self.options.plotter.set_supplementary_data(depth=hop_result.extra["depth"])figures=[self.options.plotter.figure()]else:figures=Nonereturn[hop_result,qv_result],figures@staticmethoddef_calc_ideal_heavy_output(probabilities_vector,depth):""" Calculate the bit strings of the heavy output for the ideal simulation Args: ideal_data (dict): the simulation result of the ideal circuit Returns: list: the bit strings of the heavy output """format_spec=f"{{0:0{depth}b}}"# Keys are bit strings and values are probabilities of observing those stringsall_output_prob_ideal={format_spec.format(b):float(np.real(probabilities_vector[b]))forbinrange(2**depth)}median_probabilities=float(np.real(np.median(probabilities_vector)))heavy_strings=list(filter(lambdax:all_output_prob_ideal[x]>median_probabilities,list(all_output_prob_ideal.keys()),))returnheavy_strings@staticmethoddef_calc_exp_heavy_output_probability(data,heavy_outputs):""" Calculate the probability of measuring heavy output string in the data Args: data (dict): the result of the circuit execution heavy_outputs (list): the bit strings of the heavy output from the ideal simulation Returns: int: heavy output probability """circ_shots=sum(data["counts"].values())# Calculate the number of heavy output counts in the experimentheavy_output_counts=sum(data["counts"].get(value,0)forvalueinheavy_outputs)# Calculate the experimental heavy output probabilityreturnheavy_output_counts/circ_shots@staticmethoddef_calc_z_value(mean,sigma):"""Calculate z value using mean and sigma. Args: mean (float): mean sigma (float): standard deviation Returns: float: z_value in standard normal distribution. """ifsigma==0:# Assign a small value for sigma if sigma = 0sigma=1e-10warnings.warn("Standard deviation sigma should not be zero.")z_value=(mean-2/3)/sigmareturnz_value@staticmethoddef_calc_confidence_level(z_value):"""Calculate confidence level using z value. Accumulative probability for standard normal distribution in [-z, +infinity] is 1/2 (1 + erf(z/sqrt(2))), where z = (X - mu)/sigma = (hmean - 2/3)/sigma Args: z_value (float): z value in in standard normal distribution. Returns: float: confidence level in decimal (not percentage). """confidence_level=0.5*(1+math.erf(z_value/2**0.5))returnconfidence_leveldef_calc_quantum_volume(self,heavy_output_prob_exp,depth,trials):""" Calc the quantum volume of the analysed system. quantum volume is determined by the largest successful depth. A depth is successful if it has `mean heavy-output probability` > 2/3 with confidence level > 0.977 (corresponding to z_value = 2), and at least 100 trials have been ran. we assume the error (standard deviation) of the heavy output probability is due to a binomial distribution. standard deviation for binomial distribution is sqrt(np(1-p)), where n is the number of trials and p is the success probability. Returns: dict: quantum volume calculations - the quantum volume, whether the results passed the threshold, the confidence of the result, the heavy output probability for each trial, the mean heavy-output probability, the error of the heavy output probability, the depth of the circuit, the number of trials ran """quantum_volume=1success=Falsemean_hop=np.mean(heavy_output_prob_exp)sigma_hop=(mean_hop*((1.0-mean_hop)/trials))**0.5z=2threshold=2/3+z*sigma_hopz_value=self._calc_z_value(mean_hop,sigma_hop)confidence_level=self._calc_confidence_level(z_value)ifconfidence_level>0.977:quality="good"else:quality="bad"# Must have at least 100 trialsiftrials<100:warnings.warn("Must use at least 100 trials to consider Quantum Volume as successful.")ifmean_hop>thresholdandtrials>=100:quantum_volume=2**depthsuccess=Truehop_result=AnalysisResultData("mean_HOP",value=uncertainties.ufloat(nominal_value=mean_hop,std_dev=sigma_hop),quality=quality,extra={"HOPs":heavy_output_prob_exp,"two_sigma":2*sigma_hop,"depth":depth,"trials":trials,},)qv_result=AnalysisResultData("quantum_volume",value=quantum_volume,quality=quality,extra={"success":success,"confidence":confidence_level,"depth":depth,"trials":trials,},)returnhop_result,qv_result