Source code for qiskit_experiments.curve_analysis.standard_analysis.decay

# 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.
"""Decay analysis class."""

from typing import List, Union, Optional

import lmfit
import numpy as np

import qiskit_experiments.curve_analysis as curve


[docs] class DecayAnalysis(curve.CurveAnalysis): r"""A class to analyze general exponential decay curve. # section: fit_model The fit is based on the following decay function. .. math:: F(x) = {\rm amp} \cdot e^{-x/\tau} + {\rm base} # section: fit_parameters defpar \rm amp: desc: Height of the decay curve. init_guess: Determined by :func:`~qiskit_experiments.curve_analysis.guess.min_height`. bounds: None defpar \rm base: desc: Base line of the decay curve. init_guess: Determined by the difference of minimum and maximum points. bounds: None defpar \tau: desc: This is the fit parameter of main interest. init_guess: Determined by :func:`~qiskit_experiments.curve_analysis.guess.exp_decay`. bounds: None """ def __init__( self, name: Optional[str] = None, ): super().__init__( models=[ lmfit.models.ExpressionModel( expr="amp * exp(-x/tau) + base", name="exp_decay", ) ], name=name, ) def _generate_fit_guesses( self, user_opt: curve.FitOptions, curve_data: curve.ScatterTable, ) -> Union[curve.FitOptions, List[curve.FitOptions]]: """Create algorithmic initial fit guess from analysis options and curve data. Args: user_opt: Fit options filled with user provided guess and bounds. curve_data: Formatted data collection to fit. Returns: List of fit options that are passed to the fitter function. """ user_opt.p0.set_if_empty(base=curve.guess.min_height(curve_data.y)[0]) alpha = curve.guess.exp_decay(curve_data.x, curve_data.y) if alpha != 0.0: user_opt.p0.set_if_empty( tau=-1 / alpha, amp=curve.guess.max_height(curve_data.y)[0] - user_opt.p0["base"], ) else: # Likely there is no slope. Cannot fit constant line with this model. # Set some large enough number against to the scan range. user_opt.p0.set_if_empty( tau=100 * np.max(curve_data.x), amp=curve.guess.max_height(curve_data.y)[0] - user_opt.p0["base"], ) return user_opt def _evaluate_quality(self, fit_data: curve.CurveFitResult) -> Union[str, None]: """Algorithmic criteria for whether the fit is good or bad. A good fit has: - a reduced chi-squared lower than three and greater than zero - tau error is less than its value """ tau = fit_data.ufloat_params["tau"] criteria = [ 0 < fit_data.reduced_chisq < 3, curve.utils.is_error_not_significant(tau), ] if all(criteria): return "good" return "bad"