def _t_off_initial_guess(self) -> float: """Return initial guess for time offset. This method assumes the :py:class:`~qiskit.pulse.library.parametric_pulses.GaussianSquare` envelope with the Gaussian rising and falling edges with the parameter ``sigma``. This is intended to be overridden by a child class so that rest of the analysis class logic can be reused for the fitting that assumes other pulse envelopes. Returns: An initial guess for time offset parameter ``t_off`` in SI units. Raises: AnalysisError: When time unit is ``dt`` but the backend doesn't report the time resolution of waveforms. """ n_pulses = self._extra_metadata().get("n_cr_pulses", 1) sigma = self._experiment_options().get("sigma", 0) unit = self._experiment_options().get("unit") # Convert sigma unit into SI if unit == "dt": try: prefactor = self._backend.configuration().dt except AttributeError as ex: raise AnalysisError( "Backend configuration does not provide time resolution." ) from ex elif unit != "s": prefactor = apply_prefix(1.0, unit) else: prefactor = 1.0 return np.sqrt(2 * np.pi) * prefactor * sigma * n_pulses
def test_get_same_value_after_attach_detach(self, value: float): """Test if same value can be obtained.""" unit = "Hz" for prefix in ["P", "T", "G", "k", "m", "µ", "n", "p", "f"]: scaled_val = apply_prefix(value, prefix + unit) test_val, ret_prefix = detach_prefix(scaled_val) self.assertAlmostEqual(test_val, value) self.assertEqual(prefix, ret_prefix)
def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: """Return a list of experiment circuits. Each circuit consists of a Hadamard gate, followed by a fixed delay, a phase gate (with a linear phase), and an additional Hadamard gate. Args: backend: Optional, a backend object Returns: The experiment circuits Raises: AttributeError: if unit is 'dt', but 'dt' parameter is missing in the backend configuration. """ conversion_factor = 1 if self.experiment_options.unit == "dt": try: dt_factor = getattr(backend._configuration, "dt") conversion_factor = dt_factor except AttributeError as no_dt: raise AttributeError("Dt parameter is missing in backend configuration") from no_dt elif self.experiment_options.unit != "s": conversion_factor = apply_prefix(1, self.experiment_options.unit) circuits = [] for delay in self.experiment_options.delays: circ = qiskit.QuantumCircuit(1, 1) circ.h(0) circ.delay(delay, 0, self.experiment_options.unit) rotation_angle = ( 2 * np.pi * self.experiment_options.osc_freq * conversion_factor * delay ) circ.rz(rotation_angle, 0) circ.barrier(0) circ.h(0) circ.barrier(0) circ.measure(0, 0) circ.metadata = { "experiment_type": self._type, "qubit": self.physical_qubits[0], "osc_freq": self.experiment_options.osc_freq, "xval": delay, "unit": self.experiment_options.unit, } if self.experiment_options.unit == "dt": circ.metadata["dt_factor"] = dt_factor circuits.append(circ) return circuits
def __init__( self, qubit: int, frequencies: Iterable[float], unit: str = "Hz", absolute: bool = True, ): """ A spectroscopy experiment run by setting the frequency of the qubit drive. The parameters of the GaussianSquare spectroscopy pulse can be specified at run-time. The spectroscopy pulse has the following parameters: - amp: The amplitude of the pulse must be between 0 and 1, the default is 0.1. - duration: The duration of the spectroscopy pulse in samples, the default is 1000 samples. - sigma: The standard deviation of the pulse, the default is duration / 4. - width: The width of the flat-top in the pulse, the default is 0, i.e. a Gaussian. Args: qubit: The qubit on which to run spectroscopy. frequencies: The frequencies to scan in the experiment. unit: The unit in which the user specifies the frequencies. Can be one of 'Hz', 'kHz', 'MHz', 'GHz'. Internally, all frequencies will be converted to 'Hz'. absolute: Boolean to specify if the frequencies are absolute or relative to the qubit frequency in the backend. Raises: QiskitError: if there are less than three frequency shifts or if the unit is not known. """ super().__init__([qubit]) if len(frequencies) < 3: raise QiskitError( "Spectroscopy requires at least three frequencies.") if unit == "Hz": self._frequencies = frequencies else: self._frequencies = [ apply_prefix(freq, unit) for freq in frequencies ] self._absolute = absolute if not self._absolute: self.set_analysis_options(xlabel="Frequency shift") else: self.set_analysis_options(xlabel="Frequency") self.set_analysis_options(ylabel="Signal [arb. unit]")
def test_apply_prefix(self): """Test applying prefix to value.""" ref_values = [ ([1.0, "THz"], 1e12), ([1.0, "GHz"], 1e9), ([1.0, "MHz"], 1e6), ([1.0, "kHz"], 1e3), ([1.0, "mHz"], 1e-3), ([1.0, "µHz"], 1e-6), ([1.0, "uHz"], 1e-6), ([1.0, "nHz"], 1e-9), ([1.0, "pHz"], 1e-12), ] for args, ref_ret in ref_values: self.assertEqual(apply_prefix(*args), ref_ret)
def test_t2ramsey_run_end2end(self): """ Run the T2Ramsey backend on all possible units """ for unit in ["s", "ms", "us", "ns", "dt"]: if unit in ("s", "dt"): dt_factor = 1 else: dt_factor = apply_prefix(1, unit) osc_freq = 0.1 / dt_factor estimated_t2ramsey = 20 estimated_freq = osc_freq * 1.001 # Set up the circuits qubit = 0 if unit == "dt": # dt requires integer values for delay delays = list(range(1, 46)) else: delays = np.append( (np.linspace(1.0, 15.0, num=15)).astype(float), (np.linspace(16.0, 45.0, num=59)).astype(float), ) exp = T2Ramsey(qubit, delays, unit=unit, osc_freq=osc_freq) default_p0 = { "A": 0.5, "T2star": estimated_t2ramsey, "f": estimated_freq, "phi": 0, "B": 0.5, } for user_p0 in [default_p0, None]: exp.set_analysis_options(user_p0=user_p0, plot=True) backend = T2RamseyBackend( p0={ "A": [0.5], "T2star": [estimated_t2ramsey], "f": [estimated_freq], "phi": [0.0], "B": [0.5], }, initial_prob_plus=[0.0], readout0to1=[0.02], readout1to0=[0.02], conversion_factor=dt_factor, ) expdata = exp.run(backend=backend, shots=2000) expdata.block_for_results() # Wait for job/analysis to finish. result = expdata.analysis_results() self.assertAlmostEqual( result[0].value.value, estimated_t2ramsey * dt_factor, delta=TestT2Ramsey.__tolerance__ * result[0].value.value, ) self.assertAlmostEqual( result[1].value.value, estimated_freq, delta=TestT2Ramsey.__tolerance__ * result[1].value.value, ) for res in result: self.assertEqual(res.quality, "good", "Result quality bad for unit " + str(unit))
def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: """Return a list of experiment circuits. Args: backend: The target backend. Returns: A list of :class:`QuantumCircuit`. Raises: AttributeError: When the backend doesn't report the time resolution of waveforms. """ opt = self.experiment_options prefactor = 1.0 try: dt_factor = backend.configuration().dt except AttributeError as ex: raise AttributeError( "Backend configuration does not provide time resolution." ) from ex if opt.unit != "dt": if opt.unit != "s": prefactor *= apply_prefix(1.0, opt.unit) prefactor /= dt_factor # Parametrized duration cannot be used because total duration is computed # on the fly with granularity validation. This validation requires # duration value that is not a parameter expression. # Note that this experiment scans flat top width rather than total duration. expr_circs = list() for flat_top_width in np.asarray(opt.flat_top_widths, dtype=float): # circuit duration is shown in given units (just for visualization) cr_gate = circuit.Gate( "cr_gate", num_qubits=2, params=[flat_top_width / self.__n_cr_pulses__], ) for control_state in (0, 1): for meas_basis in ("x", "y", "z"): tomo_circ = QuantumCircuit(2, 1) # state prep if control_state: tomo_circ.x(0) # add cross resonance tomo_circ.compose( other=self._build_cr_circuit(cr_gate), qubits=[0, 1], inplace=True, ) # measure if meas_basis == "x": tomo_circ.h(1) elif meas_basis == "y": tomo_circ.sdg(1) tomo_circ.h(1) tomo_circ.measure(1, 0) # add metadata tomo_circ.metadata = { "experiment_type": self.experiment_type, "qubits": self.physical_qubits, "xval": prefactor * flat_top_width * dt_factor, # in units of sec "control_state": control_state, "meas_basis": meas_basis, } # Create schedule and add it to the circuit. # The flat top width and sigma are in units of dt # width is divided by number of tones to keep total duration consistent tomo_circ.add_calibration( gate=cr_gate, qubits=self.physical_qubits, schedule=self._build_cr_schedule( backend=backend, flat_top_width=prefactor * flat_top_width / self.__n_cr_pulses__, sigma=prefactor * opt.sigma, ), ) expr_circs.append(tomo_circ) return expr_circs
def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: """Create the circuits for the Ramsey XY calibration experiment. Args: backend: A backend object. Returns: A list of circuits with a variable delay. Raises: AttributeError: if unit is 'dt', but 'dt' the parameter is missing from the backend's configuration. """ conversion_factor = 1 if self.experiment_options.unit == "dt": try: conversion_factor = getattr(backend.configuration(), "dt") except AttributeError as no_dt: raise AttributeError( "Dt parameter is missing from the backend's configuration." ) from no_dt elif self.experiment_options.unit != "s": conversion_factor = apply_prefix(1, self.experiment_options.unit) # Compute the rz rotation angle to add a modulation. p_delay = Parameter("delay") rotation_angle = 2 * np.pi * self.experiment_options.osc_freq * conversion_factor * p_delay # Create the X and Y circuits. metadata = { "experiment_type": self._type, "qubits": self.physical_qubits, "osc_freq": self.experiment_options.osc_freq, "unit": "s", } ram_x = self._pre_circuit() ram_x.sx(0) ram_x.delay(p_delay, 0, self.experiment_options.unit) ram_x.rz(rotation_angle, 0) ram_x.sx(0) ram_x.measure_active() ram_x.metadata = metadata.copy() ram_y = self._pre_circuit() ram_y.sx(0) ram_y.delay(p_delay, 0, self.experiment_options.unit) ram_y.rz(rotation_angle - np.pi / 2, 0) ram_y.sx(0) ram_y.measure_active() ram_y.metadata = metadata.copy() # Add the schedule if any. schedule = self.experiment_options.schedule if schedule is not None: for circ in [ram_x, ram_y]: circ.add_calibration("sx", self.physical_qubits, schedule) circs = [] for delay in self.experiment_options.delays: # create ramsey x assigned_x = ram_x.assign_parameters({p_delay: delay}, inplace=False) assigned_x.metadata["series"] = "X" assigned_x.metadata["xval"] = delay * conversion_factor # create ramsey y assigned_y = ram_y.assign_parameters({p_delay: delay}, inplace=False) assigned_y.metadata["series"] = "Y" assigned_y.metadata["xval"] = delay * conversion_factor circs.extend([assigned_x, assigned_y]) return circs
def _run_analysis( self, experiment_data, t1_guess=None, amplitude_guess=None, offset_guess=None, plot=True, ax=None, ) -> Tuple[List[AnalysisResultData], List["matplotlib.figure.Figure"]]: """ Calculate T1 Args: experiment_data (ExperimentData): the experiment data to analyze t1_guess (float): Optional, an initial guess of T1 amplitude_guess (float): Optional, an initial guess of the coefficient of the exponent offset_guess (float): Optional, an initial guess of the offset plot (bool): Generator plot of exponential fit. ax (AxesSubplot): Optional, axes to add figure to. Returns: The analysis result with the estimated T1 Raises: AnalysisError: if the analysis fails. """ data = experiment_data.data() unit = data[0]["metadata"]["unit"] conversion_factor = data[0]["metadata"].get("dt_factor", None) qubit = data[0]["metadata"]["qubit"] if conversion_factor is None: conversion_factor = 1 if unit == "s" else apply_prefix(1, unit) xdata, ydata, sigma = process_curve_data(data, lambda datum: level2_probability(datum, "1")) xdata *= conversion_factor if t1_guess is None: t1_guess = np.mean(xdata) else: t1_guess = t1_guess * conversion_factor if offset_guess is None: offset_guess = ydata[-1] if amplitude_guess is None: amplitude_guess = ydata[0] - offset_guess # Perform fit def fit_fun(x, a, tau, c): return a * np.exp(-x / tau) + c init = {"a": amplitude_guess, "tau": t1_guess, "c": offset_guess} fit_result = curve_fit(fit_fun, xdata, ydata, init, sigma=sigma) fit_result = dataclasses.asdict(fit_result) fit_result["circuit_unit"] = unit if unit == "dt": fit_result["dt"] = conversion_factor # Construct analysis result name = "T1" unit = "s" value = FitVal(fit_result["popt"][1], fit_result["popt_err"][1], unit="s") chisq = fit_result["reduced_chisq"] quality = self._fit_quality( fit_result["popt"], fit_result["popt_err"], fit_result["reduced_chisq"] ) analysis_results = [ AnalysisResultData( name, value, chisq=chisq, quality=quality, extra=fit_result, ) ] # Generate fit plot figures = [] if plot: ax = plot_curve_fit(fit_fun, fit_result, ax=ax, fit_uncertainty=True) ax = plot_errorbar(xdata, ydata, sigma, ax=ax) self._format_plot(ax, fit_result, qubit=qubit) figures.append(ax.get_figure()) return analysis_results, figures
def _run_analysis( self, experiment_data, t1_guess=None, amplitude_guess=None, offset_guess=None, t1_bounds=None, amplitude_bounds=None, offset_bounds=None, **kwargs, ) -> Tuple[AnalysisResult, None]: """ Calculate T1 Args: experiment_data (ExperimentData): the experiment data to analyze t1_guess (float): Optional, an initial guess of T1 amplitude_guess (float): Optional, an initial guess of the coefficient of the exponent offset_guess (float): Optional, an initial guess of the offset t1_bounds (list of two floats): Optional, lower bound and upper bound to T1 amplitude_bounds (list of two floats): Optional, lower bound and upper bound to the amplitude offset_bounds (list of two floats): Optional, lower bound and upper bound to the offset kwargs: Trailing unused function parameters Returns: The analysis result with the estimated T1 """ unit = experiment_data._data[0]["metadata"]["unit"] conversion_factor = experiment_data._data[0]["metadata"].get("dt_factor", None) if conversion_factor is None: conversion_factor = 1 if unit == "s" else apply_prefix(1, unit) xdata, ydata, sigma = process_curve_data( experiment_data._data, lambda datum: level2_probability(datum, "1") ) xdata *= conversion_factor if t1_guess is None: t1_guess = np.mean(xdata) else: t1_guess = t1_guess * conversion_factor if offset_guess is None: offset_guess = ydata[-1] if amplitude_guess is None: amplitude_guess = ydata[0] - offset_guess if t1_bounds is None: t1_bounds = [0, np.inf] if amplitude_bounds is None: amplitude_bounds = [0, 1] if offset_bounds is None: offset_bounds = [0, 1] fit_result = curve_fit( lambda x, a, tau, c: a * np.exp(-x / tau) + c, xdata, ydata, [amplitude_guess, t1_guess, offset_guess], sigma, tuple( [amp_bnd, t1_bnd, offset_bnd] for amp_bnd, t1_bnd, offset_bnd in zip(amplitude_bounds, t1_bounds, offset_bounds) ), ) analysis_result = AnalysisResult( { "value": fit_result["popt"][1], "stderr": fit_result["popt_err"][1], "unit": "s", "label": "T1", "fit": fit_result, "quality": self._fit_quality( fit_result["popt"], fit_result["popt_err"], fit_result["reduced_chisq"] ), } ) analysis_result["fit"]["circuit_unit"] = unit if unit == "dt": analysis_result["fit"]["dt"] = conversion_factor return analysis_result, None
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
def test_not_convert_meter(self): """Test not apply prefix to meter.""" self.assertEqual(apply_prefix(1.0, "m"), 1.0)