def _format_plot(hop_result: AnalysisResultData, ax: Optional["matplotlib.pyplot.AxesSubplot"] = None): """Format the QV plot Args: hop_result: the heavy output probability analysis result. ax: matplotlib axis to add plot to. Returns: AxesSubPlot: the matplotlib axes containing the plot. """ trials = hop_result.extra["trials"] heavy_probs = hop_result.extra["HOPs"] trial_list = np.arange(1, trials + 1) # x data hop_accumulative = np.cumsum(heavy_probs) / trial_list two_sigma = 2 * (hop_accumulative * (1 - hop_accumulative) / trial_list)**0.5 # Plot individual HOP as scatter ax = plot_scatter( trial_list, heavy_probs, ax=ax, s=3, zorder=3, label="Individual HOP", ) # Plot accumulative HOP ax.plot(trial_list, hop_accumulative, color="r", label="Cumulative HOP") # Plot two-sigma shaded area ax = plot_errorbar( trial_list, hop_accumulative, two_sigma, ax=ax, fmt="none", ecolor="lightgray", elinewidth=20, capsize=0, alpha=0.5, label="2$\\sigma$", ) # Plot 2/3 success threshold ax.axhline(2 / 3, color="k", linestyle="dashed", linewidth=1, label="Threshold") ax.set_ylim( max(hop_accumulative[-1] - 4 * two_sigma[-1], 0), min(hop_accumulative[-1] + 4 * two_sigma[-1], 1), ) ax.set_xlabel("Number of Trials", fontsize=14) ax.set_ylabel("Heavy Output Probability", fontsize=14) ax.set_title( "Quantum Volume experiment for depth " + str(hop_result.extra["depth"]) + " - accumulative hop", fontsize=14, ) # Re-arrange legend order handles, labels = ax.get_legend_handles_labels() handles = [handles[1], handles[2], handles[0], handles[3]] labels = [labels[1], labels[2], labels[0], labels[3]] ax.legend(handles, labels) return ax
def _run_analysis( self, experiment_data: ExperimentData, user_p0: Optional[Dict[str, float]] = None, user_bounds: Optional[Tuple[List[float], List[float]]] = None, plot: bool = False, ax: Optional["AxesSubplot"] = None, **kwargs, ) -> Tuple[List[AnalysisResultData], List["matplotlib.figure.Figure"]]: r"""Calculate T2Ramsey experiment. Args: experiment_data (ExperimentData): the experiment data to analyze user_p0: contains initial values given by the user, for the fit parameters :math:`(a, t2ramsey, f, \phi, b)` user_bounds: lower and upper bounds on the parameters in p0, given by the user. The first tuple is the lower bounds, The second tuple is the upper bounds. For both params, the order is :math:`a, t2ramsey, f, \phi, b`. plot: if True, create the plot, otherwise, do not create the plot. ax: the plot object **kwargs: additional parameters for curve fit. Returns: The analysis result with the estimated :math:`t2ramsey` and 'f' (frequency) The graph of the function. """ def osc_fit_fun(x, a, t2ramsey, f, phi, c): """Decay cosine fit function""" return a * np.exp( -x / t2ramsey) * np.cos(2 * np.pi * f * x + phi) + c def _format_plot(ax, unit, fit_result, conversion_factor): """Format curve fit plot""" # Formatting ax.tick_params(labelsize=14) ax.set_xlabel("Delay (s)", fontsize=12) ax.ticklabel_format(axis="x", style="sci", scilimits=(0, 0)) ax.set_ylabel("Probability of measuring 0", fontsize=12) t2ramsey = fit_result["popt"][1] / conversion_factor t2_err = fit_result["popt_err"][1] / conversion_factor box_text = "$T_2Ramsey$ = {:.2f} \u00B1 {:.2f} {}".format( t2ramsey, t2_err, unit) bbox_props = dict(boxstyle="square,pad=0.3", fc="white", ec="black", lw=1) ax.text( 0.6, 0.9, box_text, ha="center", va="center", size=12, bbox=bbox_props, transform=ax.transAxes, ) return ax # implementation of _run_analysis data = experiment_data.data() circ_metadata = data[0]["metadata"] unit = circ_metadata["unit"] conversion_factor = circ_metadata.get("dt_factor", None) osc_freq = circ_metadata.get("osc_freq", None) if conversion_factor is None: conversion_factor = 1 if unit in ("s", "dt") else apply_prefix(1, unit) xdata, ydata, sigma = process_curve_data( data, lambda datum: level2_probability(datum, "0")) t2ramsey_estimate = np.mean(xdata) p0, bounds = self._t2ramsey_default_params(conversion_factor, user_p0, user_bounds, t2ramsey_estimate, osc_freq) xdata *= conversion_factor fit_result = curve_fit(osc_fit_fun, xdata, ydata, p0=list(p0.values()), sigma=sigma, bounds=bounds) fit_result = dataclasses.asdict(fit_result) fit_result["circuit_unit"] = unit if osc_freq is not None: fit_result["osc_freq"] = osc_freq if unit == "dt": fit_result["dt"] = conversion_factor quality = self._fit_quality(fit_result["popt"], fit_result["popt_err"], fit_result["reduced_chisq"]) chisq = fit_result["reduced_chisq"] if plot: ax = plot_curve_fit(osc_fit_fun, fit_result, ax=ax) ax = plot_scatter(xdata, ydata, ax=ax) ax = plot_errorbar(xdata, ydata, sigma, ax=ax) _format_plot(ax, unit, fit_result, conversion_factor) figures = [ax.get_figure()] else: figures = None # Output unit is 'sec', regardless of the unit used in the input result_t2star = AnalysisResultData( "T2star", value=FitVal(fit_result["popt"][1], fit_result["popt_err"][1], "s"), quality=quality, chisq=chisq, extra=fit_result, ) result_freq = AnalysisResultData( "Frequency", value=FitVal(fit_result["popt"][2], fit_result["popt_err"][2], "Hz"), quality=quality, chisq=chisq, extra=fit_result, ) return [result_t2star, result_freq], figures