ionics-fits API
Common
- class ionics_fits.common.Fitter(x: float | List | ndarray, y: float | ndarray, model: Model)
Base class for fitters.
Fitters perform maximum likelihood parameter estimation on a dataset under the assumption of a certain model and statistics (normal, binomial, etc) and store the results as attributes.
For details about the various fitter subclasses provided by
ionics_fits
, see Fitters.Usage
Basic usage:
import numpy as np from matplotlib import pyplot as plt from ionics_fits.models.polynomial import Line from ionics_fits.normal import NormalFitter a = 3.2 y0 = -9 x = np.linspace(-10, 10) y = a * x + y0 fit = NormalFitter(x, y, model=Line()) print(f"Fitted: y = {fit.values['a']:.3f} * x + {fit.values['y0']:.3f}") plt.plot(x, y) plt.plot(*fit.evaluate()) plt.show()
The fit may be configured by modifying the
Model
‘sparameters
dictionary. This allows one to:set upper and lower bounds for each parameter
control which parameters are fixed / floated
provide user estimates to be used instead of the
Model
‘s heuristics
As an example, let’s fit a sinusoid, whose frequency is already known:
import numpy as np from matplotlib import pyplot as plt from ionics_fits.models.sinusoid import Sinusoid from ionics_fits.normal import NormalFitter omega = 2 * np.pi model = Sinusoid() model.parameters["omega"].fixed_to = omega params = { "a": 2, "omega": omega, "phi": 0.5 * np.pi, "y0": 0, "x0": 0, "tau": np.inf, } x = np.linspace(-3, 3, 100) y = model(x, True, **params) fit = NormalFitter(x, y, model=model) print(f"Amplitude: dataset = {params['a']:.3f}, fit = {fit.values['a']:.3f}") print(f"Phase: dataset = {params['phi']:.3f}, fit = {fit.values['phi']:.3f}") plt.plot(*fit.evaluate(None, True), '-.o', label="fit") plt.plot(x, y, label="data") plt.grid() plt.legend() plt.show()
Amplitude: dataset = 2.000, fit = 2.000 Phase: dataset = 1.571, fit = 1.571
ionics_fits
supports fitting datasets with arbitrary x-axis and y-axis dimensions. Thetransformations
module provides a number of classes which allow higher-dimensional models to be constructed from lower-dimension models.Example of fitting a dataset with a 2D x-axis:
import numpy as np from matplotlib import pyplot as plt from ionics_fits.models.multi_x import Gaussian2D from ionics_fits.normal import NormalFitter omega = 2 * np.pi # we know the frequency model = Gaussian2D() params = { "x0_x0": 0, "x0_x1": 3, "sigma_x0": 2, "sigma_x1": 3, "y0": 0, "a": 9, } x_0_ax = np.linspace(-3, 3, 50) x_1_ax = np.linspace(-10, 10, 100) x_0_mesh, x_1_mesh = np.meshgrid(x_0_ax, x_1_ax) x_shape = x_0_mesh.shape x = np.vstack((x_0_mesh.ravel(), x_1_mesh.ravel())) y = model(x, **params) fit = NormalFitter(x, y, model=model) _, y_fit = fit.evaluate(x) y_fit = y_fit.reshape(x_shape) _, axs = plt.subplots(2, 1) axs[0].pcolormesh(x_0_mesh, x_1_mesh, y.reshape(x_shape)) axs[1].pcolormesh(x_0_mesh, x_1_mesh, y_fit) axs[0].title.set_text("Model") axs[1].title.set_text("Fit") axs[0].grid() axs[1].grid() plt.show()
As an example of fitting a dataset with a 2D y-axis, here’s how to fit Rabi flopping on a pair of qubits. We’ll assume all parameters are the same for the two qubits, other than the Rabi frequencies:
import pprint import numpy as np from matplotlib import pyplot as plt from ionics_fits.models.rabi import RabiFlopTime from ionics_fits.models.transformations.repeated_model import RepeatedModel from ionics_fits.normal import NormalFitter params = { "omega_0": 2 * np.pi * 1, "omega_1": 2 * np.pi * 2, "delta": 0, "P_readout_e": 1, "P_readout_g": 0 } rabi_model = RabiFlopTime(start_excited=False) model = RepeatedModel( model=rabi_model, num_repetitions=2, common_params=[ param_name for param_name in rabi_model.parameters.keys() if param_name != "omega" ] ) t = np.linspace(0, 3, 100) y = model(t, **params) fit = NormalFitter(t, y, model) pprint.pprint(fit.values) plt.plot(t, y.T, ".") plt.legend(("qubit 0", "qubit 1")) plt.gca().set_prop_cycle(None) plt.plot(*fit.evaluate(None, True)) plt.grid() plt.show()
{'P_readout_e': np.float64(0.9999999999), 'P_readout_g': np.float64(1e-10), 'delta': np.float64(0.0), 'omega_0': np.float64(6.283185307179586), 'omega_1': np.float64(12.56637061436225), 't_dead': np.float64(0.0), 'tau': np.float64(inf)}
Class Attributes
- x
x-axis data. The input data is sorted along the x-axis dimensions and filtered to contain only the “valid” points where x and y are finite.
- Type:
float | List | numpy.ndarray
- y
y-axis data. The input data is sorted along the x-axis dimensions x and filtered to contain only the “valid” points where x and y are finite.
- Type:
float | numpy.ndarray
- sigma
standard errors for each point. This is stored as an array with the same shape as y.
- Type:
float | numpy.ndarray | None
- values
dictionary mapping model parameter names to their fitted values
- Type:
Dict[str, float]
- uncertainties
dictionary mapping model parameter names to their fit uncertainties. For sufficiently large datasets, well-formed problems and ignoring covariances these are the 1-sigma confidence intervals (roughly: there is a 1/3 chance that the true parameter values differ from their fitted values by more than this much). These uncertainties are generally only useful when the
covariances
are small.- Type:
Dict[str, float]
- derived_values
dictionary mapping names of derived parameters (parameters which are not part of the fit, but are calculated by the model from the fitted parameter values) to their values
- Type:
Dict[str, float]
- derived_uncertainties
dictionary mapping names of derived parameters to their fit uncertainties
- Type:
Dict[str, float]
- initial_values
dictionary mapping model parameter names to the initial values used to seed the fit.
- Type:
Dict[str, float]
- model
the fit model
- Type:
- covariances
matrix of covariances between the floated parameters. The ordering of parameters in the covariance matrix matches
free_parameters
. The fit uncertainties are calculated as the square root of the diagonals of the covariance matrix.- Type:
numpy.ndarray
- free_parameters
list of names of the model parameters floated during the fit
- Type:
List[str]
- x_scales
the applied x-axis scale factors
- Type:
numpy.ndarray
- y_scales
the applied y-axis scale factors
- Type:
numpy.ndarray
- __init__(x: float | List | ndarray, y: float | ndarray, model: Model)
Fits a model to a dataset and stores the results.
- Parameters:
x – x-axis data. For models with more than one x-axis dimension,
x
should be in the form(num_x_axes, num_samples)
.y – y-axis data.For models with more than one y-axis dimension,
y
should be in the form(num_y_axes, num_samples)
.model – the model function to fit to. The model’s parameter dictionary is used to configure the fit (set parameter bounds etc). Modify this before fitting to change the fit behaviour from the model class’ defaults. The model is (deep) copied and stored as an attribute.
- _fit(x: float | List | ndarray, y: float | ndarray, parameters: Dict[str, ModelParameter], free_func: Callable[[...], float | ndarray]) Tuple[Dict[str, float], Dict[str, float], ndarray]
Implementation of the parameter estimation.
Fitter
implementations must override this method to provide a fit with appropriate statistics.- Parameters:
x – rescaled x-axis data, must be a 1D array
y – rescaled y-axis data
parameters – dictionary of rescaled model parameters
free_func – convenience wrapper for the model function, taking only values for the fit’s free parameters
- Returns:
tuple of dictionaries mapping model parameter names to their fitted values and uncertainties.
- calc_sigma() float | ndarray | None
Returns an array of standard error values for each y-axis data point
Subclasses must override this.
- evaluate(x_fit: float | List | ndarray | None = None, transpose_and_squeeze=False) Tuple[float | List | ndarray, float | ndarray]
Evaluates the model function using the fitted parameter set.
- Parameters:
x_fit – optional x-axis points to evaluate the model at. If
None
, we use the stored value ofx
.transpose_and_squeeze – if
True
, the results arrays are transposed and squeezed prior to being returned. This is intended to be used for plotting, since matplotlib requires different y-series to be stored as columns.
- Returns:
tuple of x-axis values used and corresponding y-axis found by evaluating the model.
- residuals() float | ndarray
Returns an array of fit residuals.
- class ionics_fits.common.Model(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Base class for fit models.
A model groups a function to be fitted with associated metadata (parameter names, default bounds etc) and heuristics. It is agnostic about the method of fitting or the data statistics.
Models may be used either as part of a fit (see
Fitter
) or as a standalone function (see__call__()
).Class Attributes
- parameters
dictionary mapping parameter names to
ModelParameter
s. The parameters may be modified to alter their properties (bounds, user estimates, etc.).- Type:
Dict[str, ionics_fits.common.ModelParameter]
- internal_parameters
list of “internal” model parameters. These are not directly used during the fit, but are rescaled in the same way as regular model parameters. These are used, for example, by
transformations
models.- Type:
- __call__(x: float | List | ndarray, transpose_and_squeeze=False, **kwargs: float) float | ndarray
Evaluates the model at a given set of x-axis points and with a given set of parameter values and returns the results.
Example:
import numpy as np from matplotlib import pyplot as plt from ionics_fits.models.sinusoid import Sinusoid model = Sinusoid() x = np.linspace(0, 1) y = model(x, True, a=1, omega=2*np.pi, phi=0, y0=0) plt.plot(x, y)
- Parameters:
x – x-axis data. For models with more than one x-axis, the data should be shaped
(num_x_axes, num_samples)
. For models with a single x-axis dimension, a 1D array may be used instead.transpose_and_squeeze – if True, the results arrays are transposed and squeezed proior to being returned. This is intended to be used for plotting, since matplotlib requires different y-series to be stored as columns.
**kwargs – values for model parameters. All model parameters which are not
fixed_to
a value must be specified. Any parameters which are not specified default to their fixed values.
- Returns:
the model function values
- __init__(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
- Parameters:
parameters – optional dictionary mapping parameter names to
ModelParameter
s. This should beNone
(default) if the model has a static set of parameters, in which case the parameter dictionary is generated from the call signature of_func()
. The model parameters are stored asparameters
and may be modified after construction to change the model behaviour during fitting (e.g. to change the bounds, fixed parameters, etc).internal_parameters – optional list of “internal” model parameters, which are not exposed to the user as arguments of
func()
. Internal parameters are rescaled in the same way as regular model parameters, but are not otherwise used byModel
. These are used bytransformations
models, which modify the behaviour of other models.
- _func(x: float | List | ndarray) float | ndarray
Evaluates the model at a given set of x-axis points and with a given set of parameter values and returns the results.
Overload this in preference to
func()
unless theModel
takes a dynamic set of parameters.ModelParameter
s should be used as the type annotations for the parameters arguments to define the model’s parameters. These are used in the construction to generate theparameters
dictionary:from ionics_fits.utils import scale_x, scale_y def _func( self, x, a: ModelParameter(lower_bound=-1., scale_func=scale_y()), x0: ModelParameter(fixed_to=0, scale_func=scale_x()) ): ...
- Parameters:
x – x-axis data
- Returns:
array of model values
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
Returns dictionaries of values and uncertainties for the derived model parameters (parameters which are calculated from the fit results rather than being directly part of the fit) based on values of the fitted parameters and their uncertainties.
- Parameters:
x – x-axis data
y – y-axis data
fitted_params – dictionary mapping model parameter names to their fitted values.
fit_uncertainties – dictionary mapping model parameter names to their fit uncertainties.
- Returns:
tuple of dictionaries containing the derived parameter values and uncertainties.
- can_rescale() Tuple[List[bool], List[bool]]
Returns a tuple of lists of bools specifying whether the model can be rescaled along each x- and y-axes dimension.
- clear_heuristics()
Clear the heuristics for all model parameters (both exposed and internal).
This is mainly used in
transformations
-type models where the parameter estimator my be run multiple times for the same model instance.
- estimate_parameters(x: float | List | ndarray, y: float | ndarray)
Set heuristic values for model parameters.
Typically called by
Fitter
.Implementations of this method must ensure that all parameters have an initial value set (at least one of
fixed_to
,user_estimate
orheuristic
must not beNone
for each parameter).Implementations should aim to make use of all information supplied by the user (bounds, user estimates, fixed values) to provide the best initial guesses for all parameters.
The x and y data is sorted along the x-axis dimensions and is filtered to remove points with non-finite x or y values and are is rescaled if supported by the model.
- Parameters:
x – x-axis data
y – y-axis data
- func(x: float | List | ndarray, param_values: Dict[str, float]) float | ndarray
Evaluates the model at a given set of x-axis points and with a given set of parameter values and returns the result.
To use the model as a function outside of a fit,
__call__()
generally provides a more convenient interface.Overload this to provide a model function with a dynamic set of parameters, otherwise prefer to override
_func()
.- Parameters:
x – x-axis data
param_values – dictionary of parameter values
- Returns:
array of model values
- get_num_x_axes() int
Returns the number of x-axis dimensions the model has.
- get_num_y_axes() int
Returns the number of y-axis dimensions the model has.
- rescale(x_scales: ndarray, y_scales: ndarray)
Rescales the model parameters based on the specified x and y data scale factors.
All
parameters
andinternal_parameters
are rescaled.- Parameters:
x_scales – array of x-axis scale factors
y_scales – array of y-axis scale factors
- unscale()
Disables rescaling of the model parameters.
- class ionics_fits.common.ModelParameter(scale_func: Callable[[ndarray, ndarray], float], lower_bound: float = -inf, upper_bound: float = inf, fixed_to: float | None = None, user_estimate: float | None = None, heuristic: float | None = None)
Represents a model parameter.
- scale_func
callable returning a scale factor which the parameter must be multiplied by if it was fitted using
x
andy
data that has been multiplied by the given scale factors. Scale factors are used to improve numerical stability by avoiding asking the optimizer to work with very large or very small values ofx
andy
. The callable takes the x-axis and y-axis scale factors as arguments. A number of default scale functions are provided for convenience inionics_fits.utils
.- Type:
Callable[[numpy.ndarray, numpy.ndarray], float]
- lower_bound
lower bound for the parameter. Fitted values are guaranteed to be greater than or equal to the lower bound. Parameter bounds may be used by fit heuristics to help find good starting points for the optimizer.
- Type:
float
- upper_bound
upper bound for the parameter. Fitted values are guaranteed to be lower than or equal to the upper bound. Parameter bounds may be used by fit heuristics to help find good starting points for the optimizer.
- Type:
float
- fixed_to
if not
None
, the model parameter is fixed to this value during fitting instead of being floated. This value may additionally be used by the heuristics to help find good initial values for other model parameters. The value offixed_to
must lie within the bounds of the parameter.- Type:
float | None
- user_estimate
if not
None
and the parameter is not fixed, this value is used as an initial value during fitting rather than obtaining a value from the heuristics. This value may additionally be used by the heuristics to help find good initial values for other model parameters. The value ofuser_estimate
must lie within the bounds of the parameter.- Type:
float | None
- heuristic
if both of
fixed_to
anduser_estimate
areNone
, this value is used as an initial value during fitting. It is set by theModel
’ sestimate_parameters()
method and should not be set by the user.- Type:
float | None
- __init__(scale_func: Callable[[ndarray, ndarray], float], lower_bound: float = -inf, upper_bound: float = inf, fixed_to: float | None = None, user_estimate: float | None = None, heuristic: float | None = None) None
- clip(value: float) float
Clips a value to lie between the parameter’s lower and upper bounds.
- Parameters:
value – value to be clipped
- Returns:
clipped value
- get_initial_value(default: float | None = None) float
Returns the parameter’s initial value.
For fixed parameters, this is the value the parameter is fixed to. For floated parameters, it is the value used to seed the fit. In the latter case, the initial value is retrieved from the
user_estimate
if available, otherwise theheuristic
is used.- Parameters:
default – optional value to use if no other value is available
- has_initial_value() bool
Returns
True
if the parameter is fixed, has a user estimate or a heuristic.
- has_user_initial_value() bool
Returns
True
if the parameter is fixed or has a user estimate
- rescale(x_scales: ndarray, y_scales: ndarray)
Rescales the parameter metadata based on the specified x and y data scale factors.
Rescaling affects the values of
lower_bound
,upper_bound
,fixed_to
,user_estimate
, andheuristic
.- Parameters:
x_scales – array of x-axis scale factors
y_scales – array of y-axis scale factors
- unscale()
Disables rescaling of the parameter metadata
Utils
- class ionics_fits.utils.Array
Subclass of numpy’s NDArray used purely for type annotation convenience.
Type annotations can have arbitrary subscripts, e.g.
Array[(4,), "float64"]
.
- class ionics_fits.utils.ArrayLike
Used for types that can be a numpy array or a list that’s then converted to an array
- ionics_fits.utils.scale_invariant(x_scales: ndarray, y_scales: ndarray) float
Scale function for
ModelParameter
s whose value is invariant under rescaling of the x- and y-axes- Parameters:
x_scales – array of x-axis scale factors
y_scales – array of y-axis scale factors
- Returns:
1
- ionics_fits.utils.scale_no_rescale(x_scales: ndarray, y_scales: ndarray) float
Scale function for
ModelParameter
s which cannot be rescaled.Raises a
RuntimeError
if any of the x-axis or y-axis scale factors are not equal to1
.- Parameters:
x_scales – array of x-axis scale factors
y_scales – array of y-axis scale factors
- Returns:
1
.
- ionics_fits.utils.scale_power(x_power: int, y_power: int, x_axis: int = 0, y_axis: int = 0) Callable[[ndarray, ndarray], float]
Returns a scale function for
ModelParameter
s whose value scales as a function of one x-axis and one y-axis dimension.The parameter scale factor is calculated as:
scale_factor = (x_scales[x_axis] ** x_power) * (y_scales[y_axis] ** y_power)
- Parameters:
x_power – x-axis power
y_power – y-axis power
x_axis – index of the x-axis dimension the parameter scales with
y_axis – index of the y-axis dimension the parameter scales with
- Returns:
scale function
- ionics_fits.utils.scale_undefined(x_scales: ndarray, y_scales: ndarray) float
Scale function for
ModelParameter
s whose scaling is not known yet.This scale function is typically used for parameters whose scale factor is not known until runtime.
- Parameters:
x_scales – array of x-axis scale factors
y_scales – array of y-axis scale factors
- ionics_fits.utils.scale_x(x_axis: int = 0) Callable[[ndarray, ndarray], float]
Returns a scale function for
ModelParameter
s whose value scales linearly with one x-axis dimension.- Parameters:
x_axis – index of the x-axis dimension the parameter scales with
- Returns:
scale function
- ionics_fits.utils.scale_x_inv(x_axis: int = 0) Callable[[ndarray, ndarray], float]
Returns a scale function for
ModelParameter
s whose value scales inversely with one x-axis dimension.- Parameters:
x_axis – index of the x-axis dimension the parameter scales with
- Returns:
scale function
- ionics_fits.utils.scale_y(y_axis: int = 0) Callable[[ndarray, ndarray], float]
Returns a scale function for
ModelParameter
s whose value scales linearly with one y-axis dimension.- Parameters:
y_axis – index of the y-axis dimension the parameter scales with
- Returns:
scale function
Fitters
- class ionics_fits.normal.NormalFitter(x: float | List | ndarray, y: float | ndarray, model: Model, sigma: float | ndarray | None = None, curve_fit_args: Dict | None = None)
Fitter for Normally-distributed data.
We use least-squares fitting as a maximum-likelihood parameter estimator for normally distributed data. For data that is close to normal this is usually a pretty good approximation of a true MLE estimator.
See
Fitter
for further details.- __init__(x: float | List | ndarray, y: float | ndarray, model: Model, sigma: float | ndarray | None = None, curve_fit_args: Dict | None = None)
Fits a model to a dataset and stores the results.
- Parameters:
x – x-axis data
y – y-axis data
sigma – optional y-axis standard deviations.
model – the model function to fit to. The model’s parameter dictionary is used to configure the fit (set parameter bounds etc). Modify this before fitting to change the fit behaviour from the model class’ defaults. The model is (deep) copied and stored as an attribute.
curve_fit_args – optional dictionary of keyword arguments to be passed into
scipy.curve_fit
.
- calc_sigma() float | List | ndarray | None
Returns an array of standard error values for each y-axis data point if available.
- chi_squared(x: float | List | ndarray, y: float | ndarray, sigma: float | ndarray) float
Returns the Chi-squared fit significance for the fitted model compared to a given dataset as a number between 0 and 1.
The significance gives the probability that fit residuals as large as the ones we observe could have arisen through chance given our assumed statistics and assuming that the fitted model perfectly represents the probability distribution.
A value of
1
indicates a perfect fit (all data points lie on the fitted curve) a value close to0
indicates super-statistical deviations of the dataset from the fitted model.
- class ionics_fits.MLE.MLEFitter(x: float | List | ndarray, y: float | ndarray, model: Model, step_size: float = 0.0001, minimizer_args: Dict | None = None)
Base class for maximum Likelihood Parameter Estimation fitters.
Implementations should override the
log_likelihood()
andcalc_sigma()
methods.See
Fitter
for further details.- __init__(x: float | List | ndarray, y: float | ndarray, model: Model, step_size: float = 0.0001, minimizer_args: Dict | None = None)
Fits a model to a dataset and stores the results.
- Parameters:
x – x-axis data
y – y-axis data
model – the model function to fit to. The model’s parameter dictionary is used to configure the fit (set parameter bounds etc). Modify this before fitting to change the fit behaviour from the model class’ defaults. The model is (deep) copied and stored as an attribute.
step_size – step size used when calculating the log likelihood’s Hessian as part of finding the fitted parameter standard errors. Where finite parameter bounds are provided, they are used to scale the step size appropriately for each parameter.
minimizer_args – optional dictionary of keyword arguments to be passed into
scipy.optimize.minimize
. By default we setmaxls
to 100.
- log_likelihood(free_param_values: ndarray, x: float | List | ndarray, y: float | ndarray, free_func: Callable[[...], float | ndarray]) float
Returns the negative log-likelihood of a given dataset
- Parameters:
free_param_values – array of floated parameter values
x – x-axis data
y – y-axis data
free_func – convenience wrapper for the model function, taking only values for the fit’s free parameters
- class ionics_fits.binomial.BinomialFitter(x: float | List | ndarray, y: float | ndarray, num_trials: int, model: Model, step_size: float = 0.0001, minimizer_args: Dict | None = None)
Maximum-likelihood parameter estimator for Binomially-distributed data.
The model is interpreted as giving the success probability for a Bernoulli trial under a given set of parameters:
p = M(x; params)
.The y-axis data is interpreted as the success fraction, such that the total number of successes is equal to
k = y * num_trails
.See
Fitter
andMLEFitter
for further details.- __init__(x: float | List | ndarray, y: float | ndarray, num_trials: int, model: Model, step_size: float = 0.0001, minimizer_args: Dict | None = None)
Fits a model to a dataset and stores the results.
- Parameters:
x – x-axis data
y – y-axis data
model – the model function to fit to. The model’s parameter dictionary is used to configure the fit (set parameter bounds etc). Modify this before fitting to change the fit behaviour from the model class’ defaults. The model is (deep) copied and stored as an attribute.
num_trials – number of Bernoulli trails for each sample
step_size – see
MLEFitter
.minimizer_args – optional dictionary of keyword arguments to be passed into
scipy.optimize.minimize
.
- calc_sigma() float | ndarray
Return an array of standard error values for each y-axis data point.
- log_likelihood(free_param_values: ndarray, x: float | List | ndarray, y: float | ndarray, free_func: Callable[[...], float | ndarray]) float
Returns the negative log-likelihood of a given dataset
- Parameters:
free_param_values – array of floated parameter values
x – x-axis data
y – y-axis data
free_func – convenience wrapper for the model function, taking only values for the fit’s free parameters
Validators
It’s not enough to just fit data, we want to know whether we can trust the fit results. This is where validators come in.
There are two distinct aspects to the validation problem:
did the fit find the model parameters which best match the data (as opposed to getting stuck in a local minimum in parameter space far from the global optimum)?
Are the fitted parameter values consistent with our prior knowledge of the system (e.g. we know that a fringe contrast must lie within certain bounds).
The approach taken in ionics_fits
is as follows. First any prior knowledge about the
system should be encoded into the ModelParameter
configuration by setting parameter bounds, fixing parameters, etc.
After that, the fit is validated using a FitValidator
. Validators provide a
flexible and extensible framework for using statistical tests to validate fits.
- class ionics_fits.validators.FitValidator
Base class for fit validators
- class ionics_fits.validators.NSigmaValidator(n_sigma: float = 3.0, significance_threshold: float = 0.75)
- __init__(n_sigma: float = 3.0, significance_threshold: float = 0.75)
Fit validator which checks that at least
significance_threshold
of points lie withinn_sigma
standard errors of the fitted value.This is a relatively forgiving (easy to configure in a way that gives minimal “good” fits which fail validation) general-purpose fit validator.
- Parameters:
n_sigma – number of standard errors that points allowed to differ from the model.
significance_threshold – fraction of points which must lie within
n_sigma
of the model for the fit to be considered successful.
Models
Benchmarking
- class ionics_fits.models.benchmarking.Benchmarking(num_qubits)
Benchmarking success probability decay model according to:
y = (y0 - y_inf)*p^x + y_inf
where
x
is the sequence length (number of Clifford operations).See
_func()
for parameter details.- __init__(num_qubits)
- Parameters:
num_qubits – The number of qubits involved in the benchmarking sequence.
- _func(x: float | ~typing.List | ~numpy.ndarray, p: <ModelParameter(lower_bound=0.0, upper_bound=1.0, scale_func=scale_no_rescale)>, y0: <ModelParameter(lower_bound=0.0, upper_bound=1.0, scale_func=scale_no_rescale)>, y_inf: <ModelParameter(lower_bound=0, upper_bound=1, scale_func=scale_no_rescale)>) float | ndarray
Fit parameters
- Parameters:
p – depolarisation parameter
y0 – SPAM fidelity estimate
y_inf – depolarisation offset (y-axis asymptote) (fixed to
1/2^n
by default)
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
Derived parameters:
e
: error per Clifforde = (1 - p) / alpha_n
wherealpha_n = 2^n / (2^n - 1)
e_spam
: estimated SPAM errore_spam = 1 - y0
Cone
- class ionics_fits.models.cone.ConeSlice(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Slice through a cone according to:
z = sign * sqrt( (k_x * (x - x0))**2 + (k_y * (y - y0)) ** 2)) + z0
This model represents a slice through the cone with fixed
y
, given by:z = sign(k_x) * sqrt( (k_x * (x - x0))**2 + alpha ** 2 ) + z0
where:
alpha = k_y * (y - y0)
we use the sign of
k_x
to set the sign for the cone
Floating
z0
andalpha
without a user-estimate for either may result in an unreliable fit.See
_func()
for parameter details.- _func(x: float | ~typing.List | ~numpy.ndarray, x0: <ModelParameter(scale_func=scale_no_rescale)>, z0: <ModelParameter(fixed_to=0, scale_func=scale_no_rescale)>, k: <ModelParameter(scale_func=scale_no_rescale)>, alpha: <ModelParameter(lower_bound=0, scale_func=scale_no_rescale)>) float | ndarray
- Parameters:
x0 – x-axis offset
z0 – vertical offset to the cone
k – slope along
x
alpha – offset due to being off-centre in the y-axis
Exponential
- class ionics_fits.models.exponential.Exponential(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Exponential function according to:
y(x < x_dead) = y0 y(x >= x_dead) = y0 + (y_inf-y0)*(1-exp(-(x-x_dead)/tau))
See
_func()
for parameter details.- _func(x: float | ~typing.List | ~numpy.ndarray, x_dead: <ModelParameter(lower_bound=0, fixed_to=0, scale_func=scale_x)>, y0: <ModelParameter(scale_func=scale_y)>, y_inf: <ModelParameter(scale_func=scale_y)>, tau: <ModelParameter(lower_bound=0, scale_func=scale_x)>) float | ndarray
- Parameters:
x_dead – x-axis “dead time” (fixed to
0
by default)y0 – initial (
x = x_dead
) y-axis offsety_inf – y-axis asymptote (i.e.
y(x - x_dead >> tau) => y_inf
)tau – decay constant
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
Derived parameters:
x_1_e
: x-axis value for1/e
decay including dead time (x_1_e = x_dead + tau
)
Gaussian
- class ionics_fits.models.gaussian.Gaussian(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Gaussian model according to:
y = a / (sigma * sqrt(2*pi)) * exp(-0.5*((x-x0)/(sigma))^2) + y0
See
_func()
for parameter details.- _func(x: float | ~typing.List | ~numpy.ndarray, x0: <ModelParameter(scale_func=scale_x)>, y0: <ModelParameter(scale_func=scale_y)>, a: <ModelParameter(scale_func=scale_power)>, sigma: <ModelParameter(lower_bound=0, scale_func=scale_x)>) float | ndarray
- Parameters:
x0 – x-axis offset
y0 – y-axis offset
a – y-axis scale factor. The Gaussian is normalized such that its integral is equal to
a
.sigma – distribution half-width at
1/e
of maximum height is2*sigma
(sigma
is the1/sqrt(e)
radius).
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
Derived parameters:
FWHMH
: full width at half-maximum heightpeak
: peak height abovey0
w0
: full width at1/e
max height. For Gaussian beams this is the beam waist
Heuristics
Helper functions for writing robust heuristics.
- ionics_fits.models.heuristics.find_x_offset_fft(x: float | List | ndarray, omega: List | ndarray, spectrum: List | ndarray, omega_cut_off: float) float
Finds the x-axis offset of a dataset from the phase of an FFT.
This function uses the FFT shift theorem to extract the offset from the phase slope of an FFT.
This heuristic does not require any model parameters to have value estimates. It has good noise robustness (e.g. it’s not thrown off by a small number of outliers). It is generally accurate when the dataset is roughly regularly sampled along
x
, when the model is roughly zero outside of the dataset and when the model’s spectral content is below the dataset’s Nyquist frequency.This heuristic supports datasets with a single x- and y-axis dimension.
See also
get_spectrum()
.- Parameters:
omega – FFT frequency axis
spectrum – complex FFT data
omega_cut_off – highest value of omega to use in offset estimation
- Returns:
an estimate of the x-axis offset
- ionics_fits.models.heuristics.find_x_offset_sampling(model: Model, x: float | List | ndarray, y: float | ndarray, width: float, x_offset_param_name: str = 'x0') float
Finds the x-axis offset of a dataset by stepping through a range of potential offset values and picking the one that gives the lowest residuals.
This function takes a more brute-force approach by evaluating the model at a range of offset values, picking the one that gives the lowest residuals. This may be appropriate where one needs the estimate to be highly robust in the face of noisy, irregularly sampled data.
- Parameters:
x – x-axis data
y – y-axis data
width – width of the feature we’re trying to find (e.g. FWHMH). Used to pick the spacing between offset values to try.
x_offset_param_name – name of the x-axis offset parameter
- Returns:
an estimate of the x-axis offset
- ionics_fits.models.heuristics.find_x_offset_sym_peak_fft(model: Model, x: float | List | ndarray, y: float | ndarray, omega: List | ndarray, spectrum: List | ndarray, omega_cut_off: float, test_pts: List | ndarray | None = None, x_offset_param_name: str = 'x0', y_offset_param_name: str = 'y0', defaults: Dict[str, float] | None = None)
Finds the x-axis offset for symmetric, peaked (maximum deviation from the baseline occurs at the symmetry point) functions.
This heuristic combines the following heuristics, picking the one that gives the best fit (in the “lowest sum squared residuals” sense):
A limitation of this heuristic is that all other parameters must already have a known value (fixed value, user estimate, heuristic or via the
defaults
argument).This heuristic supports datasets with a single x- and y-axis dimension.
Tests all points in the top quartile of deviation from the baseline
Optionally, user-provided “test points”, taken from another heuristic. This allows the developer to combine the general-purpose heuristics here with other heuristics which make use of more model-specific assumptions
- Parameters:
model – the fit model
x – x-axis data
y – y-axis data
omega – FFT frequency axis
spectrum – complex FFT data
omega_cut_off – highest value of omega to use in offset estimation
test_pts – optional array of x-axis points to test
x_offset_param_name – name of the x-axis offset model parameter
y_offset_param_name – name of the y-axis offset model parameter
defaults – optional dictionary of fallback values to use for parameters with no initial value specified
- Returns:
an estimate of the x-axis offset
- ionics_fits.models.heuristics.get_pgram(x: ndarray, y: ndarray) Tuple[ndarray, ndarray]
Returns a Lombe-Scargle periodogram for a dataset, converted into amplitude units.
This function supports datasets with a single x- and y-axis dimension.
- Parameters:
x – x-axis data
y – y-axis data
- Returns:
tuple with the frequency axis (angular units) and the periodogram
- ionics_fits.models.heuristics.get_spectrum(x: float | List | ndarray, y: float | ndarray, trim_dc: bool = False) Tuple[ndarray, ndarray]
Returns the frequency spectrum (Fourier transform) of a dataset.
NB the returned spectrum will only match the continuos Fourier transform of the model function in the limit where the model function is zero outside of the sampling window.
NB for narrow-band signals the peak amplitude depends on where the signal frequency lies compared to the frequency bins.
This function supports datasets with a single x- and y-axis dimension.
- Parameters:
x – 1D ndarray of shape (num_samples,) containing x-axis data
y – 1D ndarray of shape (num_samples,) containing y-axis data
trim_dc – if True we do not return the DC component.
- Returns:
tuple of (angular freq, fft)
- ionics_fits.models.heuristics.get_sym_x(x: float | List | ndarray, y: float | ndarray) float
Returns
x_0
such thaty(x-x_0)
is maximally symmetric.This heuristic does not require any model parameters to have value estimates.
This heuristic supports arbitrary numbers y-axis dimensions, but only a single x-axis dimension.
Limitations of the current implementation:
it can struggle with datasets which have significant amounts of data “in the wings” of the model function. Because it doesn’t know anything about the model function (to avoid needing estimates for parameters) it can’t tell if the symmetry point it has found is really just an area of the distribution which is featureless. This could potentially be fixed by changing how we divide the data up into “windows”
it currently assumes the data is on a roughly regularly sampled grid
- Parameters:
x – x-axis data
y – y-axis data
- Returns:
the value of
x
about whichy
is maximally symmetric
- ionics_fits.models.heuristics.param_min_sqrs(model: Model, x: float | List | ndarray, y: float | ndarray, scanned_param: str, scanned_param_values: List | ndarray, defaults: Dict[str, float] | None = None) Tuple[float, float]
Scans one model parameter while holding the others fixed to find the value that gives the best fit to the data (in the minimum sum-squared residuals sense).
A limitation of this heuristic is that all other parameters must already have a known value (fixed value, user estimate, heuristic or via the
defaults
argument).This heuristic supports arbitrary numbers of x- and y-axis dimensions.
- Parameters:
x – x-axis data
y – y-axis data
scanned_param – name of parameter to optimize
scanned_param_values – array of scanned parameter values to test
defaults – optional dictionary of fallback values to use for non-scanned parameters, which don’t have an in initial value (fixed value, user estimate or heuristic) set
- Returns:
tuple with the value from
scanned_param_values
which gives the best fit and the root-sum-squared residuals for that value (“cost”).
Laser Rabi
- class ionics_fits.models.laser_rabi.LaserFlop(distribution_fun: Callable[[...], ndarray], start_excited: bool, sideband_index: int, n_max: int = 30)
Base class for damped Rabi flopping with finite Lamb-Dicke parameter.
This model calculates measurement outcomes for systems containing two internal states and moving in a 1D harmonic potential that undergo damped Rabi oscillations, defined by:
P = P_readout_g + (P_readout_e - P_readout_g) * P_e
where
P_e
is the (time-dependent) population in the excited state andP_readout_g
andP_readout_e
are the readout levels (measurement outcomes when the qubit is in one state).This class does not support fitting directly; use one of the subclasses instead. Subclasses must inherit from this class and a suitable
RabiFlop
subclass, such asRabiFlopFreq
orRabiFlopTime
.The model requires that the spin state of the system starts out entirely in one of the ground or excited states, specified using
__init__()
'sstart_excited
parameter. It further assumes that the motional part of the system starts out in a distribution over different Fock states, described by the specifieddistribution_fun
.Independent variables:
t_pulse
: duration of driving pulse including dead time. The duration of the interaction is given byt = max(0, t_pulse - t_dead)
.w
: frequency of driving pulse relative to the reference frequencyw_0
, given bydelta = w - w_0
The model additionally gains any parameters associated with the specified Fock state distribution function.
Derived parameters are inherited from
RabiFlop
'scalculate_derived_params()
method.All frequencies are in angular units.
See also
RabiFlop
.- __init__(distribution_fun: Callable[[...], ndarray], start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
- _func(x: ~typing.Tuple[float | ~typing.List | ~numpy.ndarray, float | ~typing.List | ~numpy.ndarray], P_readout_e: <ModelParameter(lower_bound=0.0,upper_bound=1.0,scale_func=scale_y)>, P_readout_g: <ModelParameter(lower_bound=0.0,upper_bound=1.0,scale_func=scale_y)>, eta: <ModelParameter(lower_bound=0.0,scale_func=scale_invariant)>, omega: <ModelParameter(lower_bound=0.0,scale_func=scale_undefined)>, tau: <ModelParameter(lower_bound=0.0,fixed_to=inf,scale_func=scale_undefined)>, t_dead: <ModelParameter(lower_bound=0.0,fixed_to=0.0,scale_func=scale_undefined)>, w_0: <ModelParameter(scale_func=scale_undefined)>, **kwargs: ~ionics_fits.common.ModelParameter) float | ndarray
Return measurement probability.
- Parameters:
x – tuple of
(t_pulse, w)
. Subclasses should override func to map this onto the appropriate input data.P_readout_e – excited state readout level
P_readout_g – ground state readout level
eta – Lamb-Dicke parameter
omega – carrier Rabi frequency
tau – decay time constant
t_dead – dead time
w_0 – resonance frequency offset
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
- Derived parameters:
t_pi
: Pi-time, calculated ast_pi = pi / omega
t_pi_2
: Pi/2-time, calculated ast_pi_2 = t_pi / 2
f_0
: Offset of resonance from zero of frequency variable in linear units
- class ionics_fits.models.laser_rabi.LaserFlopFreqCoherent(start_excited: bool, sideband_index: int, n_max: int = 30)
Fit model for Rabi flopping pulse detuning scans when the motional degree of freedom starts in a coherent state.
- __init__(start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
- class ionics_fits.models.laser_rabi.LaserFlopFreqDisplacedThermal(start_excited: bool, sideband_index: int, n_max: int = 30)
Fit model for Rabi flopping pulse detuning scans when the motional degree of freedom starts in a displaced thermal state.
- __init__(start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
- class ionics_fits.models.laser_rabi.LaserFlopFreqSqueezed(start_excited: bool, sideband_index: int, n_max: int = 30)
Fit model for Rabi flopping pulse detuning scans when the motional degree of freedom starts in a squeezed state.
- __init__(start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
- class ionics_fits.models.laser_rabi.LaserFlopFreqThermal(start_excited: bool, sideband_index: int, n_max: int = 30)
Fit model for Rabi flopping pulse detuning scans when the motional degree of freedom starts in a thermal state.
- __init__(start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
- class ionics_fits.models.laser_rabi.LaserFlopTimeCoherent(start_excited: bool, sideband_index: int, n_max: int = 30)
Fit model for Rabi flopping pulse duration scans when the motional degree of freedom starts in a coherent state.
- __init__(start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
- class ionics_fits.models.laser_rabi.LaserFlopTimeDisplacedThermal(start_excited: bool, sideband_index: int, n_max: int = 30)
Fit model for Rabi flopping pulse duration scans when the motional degree of freedom starts in a displaced thermal state.
- __init__(start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
- class ionics_fits.models.laser_rabi.LaserFlopTimeSqueezed(start_excited: bool, sideband_index: int, n_max: int = 30)
Fit model for Rabi flopping pulse duration scans when the motional degree of freedom starts in a squeezed state.
- __init__(start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
- class ionics_fits.models.laser_rabi.LaserFlopTimeThermal(start_excited: bool, sideband_index: int, n_max: int = 30)
Fit model for Rabi flopping pulse duration scans when the motional degree of freedom starts in a thermal state.
- __init__(start_excited: bool, sideband_index: int, n_max: int = 30)
- Parameters:
distribution_fun – function returning an array of Fock state occupation probabilities. The distribution function’s first argument should be the maximum Fock state to include in the simulation (the returned array has
n_max + 1
elements). Subsequent arguments should beModelParameter
s used to parametrise the distribution.start_excited – if
True
the qubit starts in the excited statesideband_index – change in motional state due to a pi-pulse starting from the spin ground-state.
n_max – maximum Fock state used in the simulation
Lorentzian
- class ionics_fits.models.lorentzian.Lorentzian(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Lorentzian model according to:
y = a * (0.5 * fwhmh)^2 / ((x - x0)^2 + (0. 5 * fwhmh)^2) + y0
See
_func()
for parameter details.- _func(x: float | ~typing.List | ~numpy.ndarray, x0: <ModelParameter(scale_func=scale_x)>, y0: <ModelParameter(scale_func=scale_y)>, a: <ModelParameter(scale_func=scale_y)>, fwhmh: <ModelParameter(lower_bound=0, scale_func=scale_x)>) float | ndarray
- Parameters:
x0 – x-axis offset
y0 – y-axis offset
a – peak value of the function above
y0
fwhmh – full width at half maximum height of the function
Mølmer–Sørensen
- class ionics_fits.models.molmer_sorensen.MolmerSorensen(num_qubits: int, start_excited: bool, walsh_idx: int)
Base class for Mølmer–Sørensen interactions.
This model calculates the time-dependent populations for one or two qubits coupled to a single motional mode undergoing a Mølmer–Sørensen type interaction.
It requires that the initial spin states of all qubits are the same and either
|g>
or|e>
- different initial states for each qubit or initial states which are superpositions of spin eigenstates are are not supported.For single-qubit interactions, the model has one y-axis dimension, giving the excited-state population at the end of the interaction.
For two-qubit interactions, the model has three y-axis dimensions -
P_gg
,P_1e
,P_ee
- giving the probabilities of 0, 1 or 2 ions being in the excited state at the end of the interaction duration.Modulation of the sign of the spin-dependent force according to a Walsh function is supported.
The motion’s initial state must be a thermal distribution.
This class does not support fitting directly; use one of its subclasses instead.
Independent variables:
t_pulse
: total interaction duration.w
: detuning of red/blue sideband tones relative to reference frequencyw_0
. The interaction detuning is given bydelta = w - w_0
.
All frequencies are in angular units unless stated otherwise.
- __init__(num_qubits: int, start_excited: bool, walsh_idx: int)
- Parameters:
num_qubits – number of qubits (must be 1 or 2)
walsh_idx – Index of Walsh function
start_excited – If True, all qubits start in
|e>
, otherwise they start in|g>
.
- _func(x: float | ~typing.List | ~numpy.ndarray, omega: <ModelParameter(lower_bound=0.0, scale_func=scale_undefined)>, w_0: <ModelParameter(scale_func=scale_undefined)>, n_bar: <ModelParameter(lower_bound=0.0, fixed_to=0.0, scale_func=scale_invariant)>) float | ndarray
Return measurement probabilities for the states
P_gg
,P_1
, andP_ee
.- Parameters:
omega – sideband Rabi frequency
w_0 – angular resonance frequency offset
n_bar – average initial occupancy of the motional mode
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
Derived parameters:
f_0
: resonance frequency offset (Hz)
- class ionics_fits.models.molmer_sorensen.MolmerSorensenFreq(num_qubits: int, start_excited: bool, walsh_idx: int)
Fit model for Mølmer–Sørensen detuning scans.
This model calculates the populations for Mølmer–Sørensen interactions when the gate duration is kept fixed and only the interaction detuning is varied. The pulse duration is specified using a new
t_pulse
model parameter.- __init__(num_qubits: int, start_excited: bool, walsh_idx: int)
- Parameters:
num_qubits – number of qubits (must be 1 or 2)
walsh_idx – Index of Walsh function
start_excited – If True, all qubits start in
|e>
, otherwise they start in|g>
.
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
Derived parameters:
f_0
: resonance frequency offset (Hz)f_loop_{n}_{i}
: frequency offset ofn
th loop closure (Hz) forn = [1, 5]
at “plus” (i = p
) or “minus” (i = m
) detuning
- class ionics_fits.models.molmer_sorensen.MolmerSorensenTime(num_qubits: int, start_excited: bool, walsh_idx: int)
Fit model for Mølmer–Sørensen pulse duration scans.
This model calculates the populations for Mølmer–Sørensen interactions when the interaction detuning is kept fixed and only the pulse duration is varied.
Since the detuning is not scanned as an independent variable, we replace
w_0
with a new model parameterdelta
, defined bydelta = |w - w_0|
.- __init__(num_qubits: int, start_excited: bool, walsh_idx: int)
- Parameters:
num_qubits – number of qubits (must be 1 or 2)
walsh_idx – Index of Walsh function
start_excited – If True, all qubits start in
|e>
, otherwise they start in|g>
.
Multi-X
Models with more than one x-axis degree of freedom, which have been created from 1D
models using Model2D
.
- class ionics_fits.models.multi_x.Cone2D
2D Cone Model.
Parameters are:
x0_x0
x0_x1
k_x0
k_x1
y0
Parameters with an
_x0
suffix inherit fromConeSlice
, parameters with an_x1
suffix inherit fromTriangle
.
- class ionics_fits.models.multi_x.Gaussian2D
2D Gaussian according to:
y = ( a / ((sigma_x0 * sqrt(2*pi)) * (sigma_x1 * sqrt(2*pi))) * exp(-0.5*((x0-x0_x0)/(sigma_x0))^2 -0.5*((x1-x0_x1)/(sigma_x1))^2) + y0
Parameters are:
a
x0_x0
x0_x1
sigma_x0
sigma_x1
y0
- Derived results are:
FWHMH_x0
FWHMH_x1
w0_x0
w0_x1
peak
See
Gaussian
for details.
Polynomial
- class ionics_fits.models.polynomial.Line
Straight line fit according to:
y = a * x + y0
Fit parameters (all floated by default unless stated otherwise):
y0
: y-axis intercepta
: slope
- class ionics_fits.models.polynomial.Parabola
Parabola fit according to: y = k * (x - x0)^2 + y0
- Fit parameters (all floated by default unless stated otherwise):
x0: x-axis offset
y0: y-axis intercept
k: curvature
- Derived parameters:
None
- estimate_parameters(x: float | List | ndarray, y: float | ndarray)
If
x0
is floated, we map the Polynomiala_1
coefficient onto a value forx0
according to:y = a_0 + a_2 * x^2 x -> x - x0: y = a_0 + a_2 * (x - x0)^2 y = a_0 + a_2*x^2 + a_2 * x0^2 + 2*a_2*x*x0 y = (a_0 + a_2 * x0^2) + 2*a_2*x*x0 + a_2*x^2 a_0 -> a_0 + a_2 * x0^2 a_1 -> 2*a_2*x0 => x0 = a_1/(2*a_2) a_2 -> a_2
- class ionics_fits.models.polynomial.Polynomial(poly_degree=10)
Polynomial fit model according to:
y = sum(a_n*(x-x0)^n) for n ={0...poly_degree}
Model parameters:
a_0
…a_{poly_degree}
: polynomial coefficientsx0
: x-axis offset (fixed to 0 by default). Floatingx0
as well as polynomial coefficients results in an under-defined problem.
- can_rescale() Tuple[List[bool], List[bool]]
Returns a tuple of lists of bools specifying whether the model can be rescaled along each x- and y-axes dimension.
- estimate_parameters(x: float | List | ndarray, y: float | ndarray)
Set heuristic values for model parameters.
Typically called by
Fitter
.Implementations of this method must ensure that all parameters have an initial value set (at least one of
fixed_to
,user_estimate
orheuristic
must not beNone
for each parameter).Implementations should aim to make use of all information supplied by the user (bounds, user estimates, fixed values) to provide the best initial guesses for all parameters.
The x and y data is sorted along the x-axis dimensions and is filtered to remove points with non-finite x or y values and are is rescaled if supported by the model.
- Parameters:
x – x-axis data
y – y-axis data
- func(x: float | List | ndarray, param_values: Dict[str, float]) float | ndarray
Evaluates the model at a given set of x-axis points and with a given set of parameter values and returns the result.
To use the model as a function outside of a fit,
__call__()
generally provides a more convenient interface.Overload this to provide a model function with a dynamic set of parameters, otherwise prefer to override
_func()
.- Parameters:
x – x-axis data
param_values – dictionary of parameter values
- Returns:
array of model values
- get_num_x_axes() int
Returns the number of x-axis dimensions the model has.
- get_num_y_axes() int
Returns the number of y-axis dimensions the model has.
- class ionics_fits.models.polynomial.Power(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Single-power fit according to:
y = a*(x-x0)^n + y0
x - x0
must always be strictly greater than0
. This is becausen
can take non-integral values (for integer coefficients usePolynomial
instead) and this function’s return is real-valued.The fit will often struggle when both
y0
andn
are floated if the dataset doesn’t contain some asymptotic values wherey ~ y0
. The more you can help it out by bounding parameters and providing initial guesses the better.The fit will generally struggle to converge if both
a
andy0
are floated unless it is given some guidance (e.g. initial values).- _func(x: float | ~typing.List | ~numpy.ndarray, a: <ModelParameter(fixed_to=1, scale_func=scale_undefined)>, x0: <ModelParameter(fixed_to=0, scale_func=scale_x)>, y0: <ModelParameter(scale_func=scale_y)>, n: <ModelParameter(scale_func=scale_invariant)>) float | ndarray
a
: y-axis scale factorx0
: x-axis offsety0
: y-axis offsetn
: power
Quantum Physics
- ionics_fits.models.quantum_phys.coherent_state_probs(n_max: int, alpha: <ModelParameter(lower_bound=0, scale_func=scale_invariant)>) ndarray
Coherent state probability distribution.
A coherent state is defined as:
|α> = exp(α a_dag - α* a) |0>
where
a_dag
anda
denote the harmonic-oscillator creation and annihilation operators. The mean Fock state occupancy is given byn_bar = |α|^2
- Parameters:
n_max – the distribution is truncated at a maximum Fock state of
|n_max>
alpha – Complex displacement parameter
- Returns:
array of Fock state occupation probabilities
- ionics_fits.models.quantum_phys.displaced_thermal_state_probs(n_max: int, n_bar: <ModelParameter(lower_bound=0, scale_func=scale_invariant)>, alpha: <ModelParameter(lower_bound=0, scale_func=scale_invariant)>) ndarray
Displaced thermal probability distribution.
For an ion initially in a thermal distribution characterised by an average phonon number n_bar, we calculate the new probability distribution after applying a displacement operator D(α).
Formula taken from equation (7) of Ramm, M., Pruttivarasin, T. and Häffner, H., 2014. Energy transport in trapped ion chains. New Journal of Physics, 16(6), p.063062. https://iopscience.iop.org/article/10.1088/1367-2630/16/6/063062/pdf
- Parameters:
n_max – the distribution is truncated at a maximum Fock state of
|n_max>
n_bar – the mean thermal Fock state occupation before displacement
alpha – Complex displacement parameter
- Returns:
array of Fock state occupation probabilities
- ionics_fits.models.quantum_phys.squeezed_state_probs(n_max: int, zeta: <ModelParameter(lower_bound=0, scale_func=scale_invariant)>) ndarray
Squeezed state probability distribution.
A pure squeezed state is defined as:
|ζ> = exp[1 / 2 * (ζ* a^2 - ζ a_dag^2)] |0>,
where
a_dag
anda
denote the harmonic-oscillator creation and annihilation operators.- Parameters:
n_max – the distribution is truncated at a maximum Fock state of
|n_max>
zeta – Complex squeezing parameter
- Returns:
array of Fock state occupation probabilities
- ionics_fits.models.quantum_phys.thermal_state_probs(n_max: int, n_bar: <ModelParameter(lower_bound=0, scale_func=scale_invariant)>) ndarray
Thermal state probability distribution.
- Parameters:
n_max – the distribution is truncated at a maximum Fock state of
|n_max>
n_bar – the mean Fock state occupation
- Returns:
array of Fock state occupation probabilities
Rabi
- class ionics_fits.models.rabi.RabiFlop(start_excited: bool)
Base class for damped Rabi flopping.
This model calculates measurement outcomes for two-state systems undergoing damped Rabi oscillations, defined by:
P = P_readout_g + (P_readout_e - P_readout_g) * P_e
where
P_e
is the (time-dependent) population in the excited state andP_readout_g
andP_readout_e
are the readout levels (measurement outcomes when the qubit is in one state).This class does not support fitting directly; use one of the subclasses instead.
The model requires that the system starts out entirely in one of the ground or excited states, specified using
__init__()
'sstart_excited
parameter.The probability of transition from one state to the other is calculated as:
P_trans = 1 / 2 * omega^2 / W^2 * [1 - exp(-t / tau) * cos(W * t)]
- where:
t
is the duration of interaction between qubit and driving fieldW = sqrt(omega^2 + delta^2)
delta
is the detuning of the driving field from resonanceomega
is the Rabi frequencytau
is the decay time constant.
- Independent variables:
t_pulse
: duration of driving pulse including dead time. The duration of the interaction is given byt = max(0, t_pulse - t_dead)
.w
: frequency of driving pulse relative to the reference frequencyw_0
, given bydelta = w - w_0
All frequencies are in angular units.
- __init__(start_excited: bool)
- Parameters:
start_excited – if
True
the system is assumed to start in the excited state, other wise it is assumed to start in the ground state.
- _func(x: ~typing.Tuple[float | ~typing.List | ~numpy.ndarray, float | ~typing.List | ~numpy.ndarray], P_readout_e: <ModelParameter(lower_bound=0.0,upper_bound=1.0,scale_func=scale_y)>, P_readout_g: <ModelParameter(lower_bound=0.0,upper_bound=1.0,scale_func=scale_y)>, omega: <ModelParameter(lower_bound=0.0,scale_func=scale_undefined)>, tau: <ModelParameter(lower_bound=0.0,fixed_to=inf,scale_func=scale_undefined)>, t_dead: <ModelParameter(lower_bound=0.0,fixed_to=0.0,scale_func=scale_undefined)>, w_0: <ModelParameter(scale_func=scale_undefined)>) float | ndarray
Return measurement probability.
- Parameters:
x – tuple of
(t_pulse, w)
. Subclasses should override func to map this onto the appropriate input data.P_readout_e – excited state readout level
P_readout_g – ground state readout level
omega – Rabi frequency
tau – decay time constant (fixed to infinity by default)
t_dead – dead time (fixed to 0 by default)
w_0 – resonance frequency offset
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
- Derived parameters:
t_pi
: Pi-time, calculated ast_pi = pi / omega
t_pi_2
: Pi/2-time, calculated ast_pi_2 = t_pi / 2
f_0
: Offset of resonance from zero of frequency variable in linear units
- class ionics_fits.models.rabi.RabiFlopFreq(start_excited: bool)
Fit model for Rabi flopping frequency scans.
This model calculates the measurement outcomes for damped Rabi flops when the pulse duration is kept fixed and only its frequency is varied. The pulse duration is specified using a new
t_pulse
model parameter.See also
RabiFlop
.- __init__(start_excited: bool)
- Parameters:
start_excited – if
True
the system is assumed to start in the excited state, other wise it is assumed to start in the ground state.
- class ionics_fits.models.rabi.RabiFlopTime(start_excited: bool)
Fit model for Rabi flopping pulse duration scans.
This model calculates the measurement outcome for damped Rabi flops when the frequency of the pulse is kept fixed and only its duration is varied.
Since the detuning is not scanned as an independent variable, we replace
w_0
with a new model parameterdelta
, defined by:delta = |w - w_0|
.See also
RabiFlop
.- __init__(start_excited: bool)
- Parameters:
start_excited – if
True
the system is assumed to start in the excited state, other wise it is assumed to start in the ground state.
Ramsey
- class ionics_fits.models.ramsey.Ramsey(start_excited: bool)
Fit model for detuning scans of Ramsey experiments (for time scans, use the Sinusoid model).
This model calculates the measurement outcomes for Ramsey experiments, defined by:
P = P_readout_g + (P_readout_e - P_readout_g) * P_e
where
P_e
is the (time-dependent) population in the excited state andP_readout_g
andP_readout_e
are the readout levels (measurement outcomes when the qubit is in one state).The model requires that the system starts out entirely in one of the ground or excited states, specified using :meth:
__init__
'sstart_excited
parameter.All frequencies are in angular units.
- _func(x: float | ~typing.List | ~numpy.ndarray, P_readout_e: <ModelParameter(lower_bound=0.0, upper_bound=1.0, scale_func=scale_y)>, P_readout_g: <ModelParameter(lower_bound=0.0, upper_bound=1.0, scale_func=scale_y)>, t: <ModelParameter(lower_bound=0.0, scale_func=scale_x_inv)>, t_pi_2: <ModelParameter(lower_bound=0.0, scale_func=scale_x_inv)>, w_0: <ModelParameter(scale_func=scale_x)>, phi: <PeriodicModelParameter(scale_func=scale_invariant, period=6.283, offset=-3.142)>, tau: <ModelParameter(lower_bound=0.0, fixed_to=inf, scale_func=scale_x_inv)>)
- Parameters:
P_readout_e – excited state readout level
P_readout_g – ground state readout level
t – Ramsey delay
t_pi_2 – duration of the pi/2 pulses. The pi/2 pulses are assumed to be ideal pi/2 pulses with a corresponding Rabi frequency of
Omega = np.pi / (2 * t_pi_2)
w_0 – resonance frequency offset, defined such that the Ramsey detuning is given by
delta = x - w_0
phi – phase of the second pi/2 pulse relative to the first pi/2 pulse
tau – decay time constant (fixed to infinity by default)
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
Derived parameters:
f_0
: resonance frequency offset in linear units, given byw_0 / (2 * np.pi)
Rectangle
- class ionics_fits.models.rectangle.Rectangle(thresh: float = 0.5)
Rectangle function according to:
x <= x_l: y = y0 x >= x_r: y = y0 x_r > x > x_l: y0 + a
For
x_l = y0 = 0
,x_r = inf
this is a Heaviside step function.- __init__(thresh: float = 0.5)
- Parameters:
thresh – threshold used to configure the parameter estimator, which attempts to find the edges of the rectangle by applying this threshold to the peak height above the baseline to determine which points are inside / outside the rectangle.
- _func(x: float | ~typing.List | ~numpy.ndarray, a: <ModelParameter(scale_func=scale_y)>, y0: <ModelParameter(scale_func=scale_y)>, x_l: <ModelParameter(scale_func=scale_x)>, x_r: <ModelParameter(scale_func=scale_x)>) float | ndarray
- Parameters:
a – rectangle height above the baseline
y0 – y-axis offset
x_l – left transition point
- Param- x_r:
right transition point
Sigmoid
- class ionics_fits.models.sigmoid.LogisticFunction(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Logistic function model according to:
y = a / (1 + exp(-k*(x - x0))) + y0
See
_func()
for parameters.- _func(x: float | ~typing.List | ~numpy.ndarray, a: <ModelParameter(scale_func=scale_y)>, y0: <ModelParameter(scale_func=scale_y)>, x0: <ModelParameter(scale_func=scale_x)>, k: <ModelParameter(lower_bound=0, scale_func=scale_x_inv)>) float | ndarray
- Parameters:
a – y-axis scale factor
y0 – y-axis offset
x0 – x-axis offset
k – logistic growth rate (steepness of the curve)
Sinc
- class ionics_fits.models.sinc.Sinc(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Sinc function according to:
y = a * sin(w * (x - x0)) / (w * (x - x0)) + y0
See
_func()
for parameters.- _func(x: float | ~typing.List | ~numpy.ndarray, x0: <ModelParameter(scale_func=scale_x)>, y0: <ModelParameter(scale_func=scale_y)>, a: <ModelParameter(scale_func=scale_y)>, w: <ModelParameter(lower_bound=0, scale_func=scale_x_inv)>) float | ndarray
- Parameters:
x0 – x-axis offset
y0 – y-axis offset
a – amplitude
w – x scale factor
- class ionics_fits.models.sinc.Sinc2(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Sinc-squared function according to:
y = a * (sin(w * (x - x0)) / (w * (x - x0)))^2 + y0
See
_func()
for parameters.- _func(x: float | ~typing.List | ~numpy.ndarray, x0: <ModelParameter(scale_func=scale_x)>, y0: <ModelParameter(scale_func=scale_y)>, a: <ModelParameter(scale_func=scale_y)>, w: <ModelParameter(lower_bound=0, scale_func=scale_x_inv)>) float | ndarray
- Parameters:
x0 – x-axis offset
y0 – y-axis offset
a – amplitude
w – x scale factor
Sinusoid
- class ionics_fits.models.sinusoid.Sine2(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Sine-squared fit according to:
y = Gamma * a * [sin(omega * (x - x0) + phi)]**2 + y0 Gamma = np.exp(-x / tau)
See also
Sinusoid
.- _func(x: float | ~typing.List | ~numpy.ndarray, a: <ModelParameter(lower_bound=0, scale_func=scale_y)>, omega: <ModelParameter(lower_bound=0, scale_func=scale_x_inv)>, phi: <PeriodicModelParameter(scale_func=scale_invariant, period=6.283, offset=-3.142)>, y0: <ModelParameter(scale_func=scale_y)>, x0: <ModelParameter(fixed_to=0, scale_func=scale_x)>, tau: <ModelParameter(lower_bound=0, fixed_to=inf, scale_func=scale_x)>) float | ndarray
- Parameters:
a – initial (
x = 0
) amplitude of the sinusoidomega – angular frequency
phi – phase offset
y0 – y-axis offset
x0 – x-axis offset
tau – decay/growth constant
- class ionics_fits.models.sinusoid.SineMinMax
Sinusoid parametrised by minimum / maximum values instead of offset / amplitude:
y = Gamma * a * sin[omega * (x - x0) + phi] + y0
This class is equivalent to
Sinusoid
except that thea
andy0
parameters are replaced with newmin
andmax
parameters defined by:min = y0 - a max = y0 + a
See
Sinusoid
for further details.
- class ionics_fits.models.sinusoid.Sinusoid(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Generalised sinusoid fit according to:
y = Gamma * a * sin[omega * (x - x0) + phi] + y0 Gamma = exp(-x / tau).
All phases are in radians, frequencies are in angular units.
x0
andphi0
are equivalent parametrisations for the phase offset, but in some cases it works out convenient to have access to both (e.g. one as a fixed offset, the other floated). At most one of them should be floated at once. By default,x0
is fixed at 0 andphi0
is floated.- _func(x: float | ~typing.List | ~numpy.ndarray, a: <ModelParameter(lower_bound=0, scale_func=scale_y)>, omega: <ModelParameter(lower_bound=0, scale_func=scale_x_inv)>, phi: <PeriodicModelParameter(scale_func=scale_invariant, period=6.283, offset=-3.142)>, y0: <ModelParameter(scale_func=scale_y)>, x0: <ModelParameter(fixed_to=0, scale_func=scale_x)>, tau: <ModelParameter(lower_bound=0, fixed_to=inf, scale_func=scale_x)>) float | ndarray
- Parameters:
a – initial (
x = 0
) amplitude of the sinusoidomega – angular frequency
phi – phase offset
y0 – y-axis offset
x0 – x-axis offset
tau – decay/growth constant
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
f
: frequencyphi_cosine
: cosine phase (phi + pi/2
)contrast
: peak-to-peak amplitude of the pure sinusoidmin
/max
: min / max values of the pure sinusoidperiod
: period of oscillation
TODO: peak values of the damped sinusoid as well as
x
value that the peak occurs at.
Triangle
- class ionics_fits.models.triangle.Triangle(parameters: Dict[str, ModelParameter] | None = None, internal_parameters: List[ModelParameter] | None = None)
Triangle function according to:
y(x>=x0) = k_p*|x-x0| + y0 y(x<x0) = k_m*|x-x0| + y0 y = max(y, y_min) y = min(y, m_max) k_p = (1 + sym) * k k_m = (1 - sym) * k
See
_func()
for parameter details.- _func(x: float | ~typing.List | ~numpy.ndarray, x0: <ModelParameter(scale_func=scale_x)>, y0: <ModelParameter(scale_func=scale_y)>, k: <ModelParameter(scale_func=scale_power)>, sym: <ModelParameter(lower_bound=-1, upper_bound=1, fixed_to=0, scale_func=scale_invariant)>, y_min: <ModelParameter(fixed_to=-inf, scale_func=scale_y)>, y_max: <ModelParameter(fixed_to=inf, scale_func=scale_y)>) float | ndarray
- Parameters:
x0 – x-axis offset
y0 – y-axis offset
k – average slope
sym – symmetry parameter (fixed to
0
by default)y_min – minimum value of y (bound to
-inf
by default)y_max – maximum value of y (bound to
+inf
by default)
- calculate_derived_params(x: float | List | ndarray, y: float | ndarray, fitted_params: Dict[str, float], fit_uncertainties: Dict[str, float]) Tuple[Dict[str, float], Dict[str, float]]
Derived parameters:
k_m: slope for
x < x0
k_p: slope for
x >= x0
Utils
- class ionics_fits.models.utils.PeriodicModelParameter(scale_func: Callable[[ndarray, ndarray], float], fixed_to: float | None = None, user_estimate: float | None = None, heuristic: float | None = None, period: float = 1, offset: float = 0)
Represents a model parameter whose value is periodic.
Parameter values are clipped to lie within:
((value - offset) % period) + offset
PeriodicModelParameter
s do not support bounds.- period
the period (default = 1)
- Type:
float
- offset
the offset (default = 0)
- Type:
float
- ionics_fits.models.utils.param_like(template_param: ModelParameter, overrides: Dict[str, Any] | None = None) ModelParameter
Returns a new parameter based on a template.
- Parameters:
template_param – the returned parameter is a (deep) copy of the template parameter.
overrides – optional dictionary of attributes of the template parameter to replace.
- Returns:
the new
ModelParameter
Transformations
Models that change the behaviour of other models.
Aggregate Model
- class ionics_fits.models.transformations.aggregate_model.AggregateModel(models: Dict[str, Model], common_params: Dict[str, Tuple[ModelParameter, List[Tuple[str, str]]]] | None = None)
Model formed by combining one or more models along the y-axis to produce a new model, whose number of y-axis dimensions is the sum of the y-dimensionalities of the aggregated models.
When aggregating a number of identical models, use a
RepeatedModel
instead.Aggregate models allow multiple data sets to be fit jointly, with some parameters treated as “common” - their values forced to be the same for all of the aggregated models.
Example usage:
from pprint import pprint from ionics_fits.models.laser_rabi import LaserFlopTimeThermal from ionics_fits.models.transformations.aggregate_model import AggregateModel rsb = LaserFlopTimeThermal(start_excited=False, sideband_index=-1) bsb = LaserFlopTimeThermal(start_excited=False, sideband_index=+1) model = AggregateModel( models={"rsb": rsb, "bsb": bsb}, common_params={ param: (rsb.parameters[param], [("rsb", param), ("bsb", param)]) for param in rsb.parameters.keys() }, ) pprint(list(model.parameters.keys()))
['P_readout_e', 'P_readout_g', 'eta', 'omega', 'tau', 't_dead', 'delta', 'n_bar']
This creates an
AggregateModel
, which models Rabi flopping on the blue and red sidebands of a pair of spins coupled to a motional mode starting in a thermal state. In this example, all parameters for the two sideband models are fit jointly.The first y-axis dimension (
y[0, :]
) from theAggregateModel
stores the red sideband with the second dimension storing the blue sideband.At present this class only supports models with a single y-axis dimension. This is just because no one got around to implementing it yet rather than any fundamental difficulty.
- __init__(models: Dict[str, Model], common_params: Dict[str, Tuple[ModelParameter, List[Tuple[str, str]]]] | None = None)
- Parameters:
models –
The models to be aggregated. This should be a dictionary mapping model names to model instances. The model names are used as suffixes for names of model parameters and derived results. For example, if one of the aggregated models named
model
has a parameterparam
, the aggregate model will have a parameterparam_model
. The same applies to the derived results.The order of the models in this dictionary defines the order of the y-axis dimensions for the
AggregateModel
.The passed-in models are considered “owned” by the AggregateModel and should not be used / modified elsewhere.
common_params –
Optional dictionary specifying “common” model parameters. This feature allows multiple parameters (which can be from the same or different models) to be fit jointly to a single value. The common parameters are replaced with a new parameter, which is introduced to expose the common value to the user.
The parameter metadata (limits,
fixed_to
,user_estimate
, etc.) from the new parameter replaces the metadata for all parameters bound to it. Metadata set on the bound parameters is disregarded.The
common_params
dictionary keys are the names of the new model parameters.The dictionary values should be tuples containing the new model template parameter and a list of parameters to bind to the new parameter. The bound parameter lists should be lists of tuples, comprised of a pair of strings specifying the name of the model which owns the common parameter, and the name of the model parameter to make common.
The new model parameters inherit their metadata (limits etc.) from the template parameters, which are (deep) copied and are not modified.
Mapped Model
- class ionics_fits.models.transformations.mapped_model.MappedModel(model: Model, param_mapping: Dict[str, str], fixed_params: Dict[str, float] | None = None, derived_result_mapping: Dict[str, str | None] | None = None)
Wraps a
Model
, allowing parameters to be renamed or replaced with fixed values.See also
ReparametrizedModel
- __init__(model: Model, param_mapping: Dict[str, str], fixed_params: Dict[str, float] | None = None, derived_result_mapping: Dict[str, str | None] | None = None)
- Parameters:
model – The model to be wrapped. This model is considered “owned” by the
MappedModel
and should not be modified / used elsewhere.param_mapping – dictionary mapping names of parameters in the
WrappedModel
to names of parameters in the model being wrapped.fixed_params – dictionary mapping names of
WrappedModel
parameters to values they sould be fixed to. Fixed parameters are not parameters of theWrappedModel
.derived_result_mapping – optional dictionary mapping names of derived result in the
WrappedModel
to names of derived results in the model being wrapped. Derived results may be renamed toNone
to exclude them from theWrappedModel
.
Model2D
- class ionics_fits.models.transformations.model_2d.Model2D(models: Tuple[Model, Model], result_params: Tuple[str], model_names: Tuple[str, str] | None = None)
Combines a pair of
Model
's, each of which is a function of a single x-dimension, to make a newModel
, which is a function of 2 x-axis dimensions.All y-axis data is generated from the output of the first model; the output from the second model provides the values of certain “result” parameters used by the first model. In other words:
model_0 = models[0] = f(x_axis_0) model_1 = models[1] = g(x_axis_1) y(x_0, x_1) = model_0(x_0 | result_params = model_1(x_1))
The 2D models generated using this class are separable into functions of the two x-axes. As a result, it can only generate axis-aligned models.
This model provides a quick and convenient means of extending the x-axis dimensionality of existing models. The design aim is to maximise flexibility while making minimal assumptions about the underlying
Model
s.Model parameters and results:
All parameters from the two models - apart from the first model’s result parameters - are parameters of the 2D model.
All derived results from the two models are included in the
Model2D
's derived results.A parameter/derived result named
param
from a model namedmodel
is exposed as a parameter / result of theModel2D
namedparam_model
.A
MappedModel
can be used to provide custom naming schemes
Example usage:
from ionics_fits.models.gaussian import Gaussian from ionics_Fits.models.transformations.model_2d import Model2D Gaussian2D = Model2D( models=(Gaussian(), Gaussian), result_params=("a_x0",), )
This fits a Gaussian model along the first x-axis dimension. It then takes the amplitudes of those Gaussians at each value of the second x-axis dimension and fits those to a Gaussian to produce a 2D Gaussian fit.
At present this class aggregates a pair of 1D models to make a 2D model. It would be easy to extend it to the more general case of aggregating an arbitrary number of models, each with arbitrary x-axis dimensionality. This has not been done yet because there has not been a use-case.
See also
multi_x
.- __init__(models: Tuple[Model, Model], result_params: Tuple[str], model_names: Tuple[str, str] | None = None)
- Parameters:
models – Tuple containing the two
Model
s to be combined to make the 2D model. The model instances are considered “owned” by the 2D model (they are not copied). They should not be referenced externally.result_params – tuple of names of “result parameters” of the first model, whose value is found by evaluating the second model. The order of parameter names in this tuple must match the order of y-axis dimensions for the second model.
model_names – optional tuple of names for the two models. These are used when generating names for fit results and derived parameters. Empty strings are allowed so long as the two models do not share any parameter names. If this argument is
None
the model names default tox0
andx1
respectively. If a model name is an empty string, the trailing underscore is omitted in parameter / result names.
- wrap_scale_funcs()
Called during
__init__()
to make sure that eachModelParameter
uses the scale factor from the appropriate x-axis when rescaling.This method wraps the
scale_func
s for each parameter of the 1D models to use the x-axis scale factor for their x-axis dimension when rescaling.This is appropriate where each model parameter scales with just that the x-axis associated with its model. If this is not the case, this method should be overridden. See
Gaussian2D
for an example of this.
Reparametrized Model
- class ionics_fits.models.transformations.reparametrized_model.ReparametrizedModel(model: Model, new_params: Dict[str, ModelParameter], bound_params: List[str])
Model formed by reparametrizing an existing
Model
.ionics_fits
aims to provide convenient and flexible model parametrisations, however sometimes the default parametrisation won’t be convenient for your application. For these casesReparametrizedModel
s provide a convenient way of changing and extending the parametrisation of an existing model.Reparametrizing a model involves replacing “bound” parameters with “new” parameters, whose values the bound parameter values are calculated from.
All non-bound parameters of the original model as well as all “new” parameters are parameters of the
ReparametrizedModel
(bound parameters are internal parameters of the new model, but are not directly exposed to the user). All derived results from the original model are derived results from theReparametrizedModel
(overridecalculate_derived_params
to change this behaviour).Values and uncertainties for the bound parameters are exposed as derived results.
Subclasses must override
bound_param_values()
,bound_param_uncertainties()
andbound_param_uncertainties()
to specify the mapping between “new” and “bound” parameters.Example usage converting a sinusoid parameterised by offset and amplitude into one parametrised by minimum and maximum values:
from typing import Dict from ionics_fits.models.sinusoid import Sinusoid from ionics_fits.models.transformations.reparametrized_model import ( ReparametrizedModel ) class SineMinMax(ReparametrizedModel): def __init__(self): super().__init__( model=Sinusoid(), new_params={ "min": ModelParameter(scale_func=scale_y()), "max": ModelParameter(scale_func=scale_y()), }, bound_params=["a", "y0"], ) @staticmethod def bound_param_values(param_values: Dict[str, float]) -> Dict[str, float]: return { "a": 0.5 * (param_values["max"] - param_values["min"]), "y0": 0.5 * (param_values["max"] + param_values["min"]), } @staticmethod def bound_param_uncertainties( param_values: Dict[str, float], param_uncertainties: Dict[str, float] ) -> Dict[str, float]: err = 0.5 * np.sqrt( param_uncertainties["max"] ** 2 + param_uncertainties["min"] ** 2 ) return {"a": err, "y0": err} @staticmethod def new_param_values(model_param_values: Dict[str, float] ) -> Dict[str, float]: return { "max": (model_param_values["y0"] + model_param_values["a"]), "min": (model_param_values["y0"] - model_param_values["a"]), }
See also
MappedModel
.- __init__(model: Model, new_params: Dict[str, ModelParameter], bound_params: List[str])
- Parameters:
model – The model to be reparametrized. This model is considered “owned” by the
ReparametrizedModel
and should not be used / modified elsewhere.new_params – dictionary of new parameters of the
ReparametrizedModel
bound_params – list of parameters of the
Model
to bound. These parameters are not exposed as parameters of theReparametrizedModel
.
- static bound_param_uncertainties(param_values: Dict[str, float], param_uncertainties: Dict[str, float]) Dict[str, float]
Returns a dictionary of uncertainties for the model’s bound parameters.
This method must be overridden to specify the mapping from parameter uncertainties for the
ReparameterizedModel
to bound parameter uncertainties.- Parameters:
param_values – dictionary of values for parameters of the
ReparameterizedModel
.param_uncertainties – dictionary of uncertainties for parameters of the
ReparameterizedModel
.
- Returns:
dictionary of values for the bound parameters of the original model.
- static bound_param_values(param_values: Dict[str, float]) Dict[str, float]
Returns a dictionary of values of the model’s bound parameters.
This method must be overridden to specify the mapping from parameters of the
ReparameterizedModel
to values of the bound parameters.- Parameters:
new_param_values – dictionary of parameter values for the
ReparameterizedModel
.- Returns:
dictionary of values for the bound parameters of the original model.
- static new_param_values(model_param_values: Dict[str, float]) Dict[str, float]
Returns a dictionary of values of the model’s “new” parameters.
This method must be overridden to specify the mapping from values of the original model to values of the
ReparametrizedModel
's “new” parameters.This is used to find estimates for the new parameters from the original model’s parameter estimates.
- Parameters:
model_param_values – dictionary of parameter values for the original model
- Returns:
dictionary of values for the “new” parameters of the
ReparametrizedModel
.
Repeated Model
- class ionics_fits.models.transformations.repeated_model.RepeatedModel(model: Model, common_params: List[str] | None = None, num_repetitions: int = 2, aggregate_results=False)
Model formed by repeating a
Model
one or more models along the y-axis to produce a new model, with greater y-axis dimensionality.The number of y-axis dimensions in the
RepeatedModel
is the product of the number of y-axis dimensions form the original model and the number of repetitions.Repeated models allow multiple datasets to be analysed simultaneously. This is useful, for example, when doing joint fits to datasets (using common parameters).
The
RepeatedModel
has the following parameters and fit results:all common parameters of the repeated model are parameters of the new model
for each independent (not common) parameter of the repeated model,
param
, theRepeatedModel
has parametersparam_{n}
forn
in[0, .., num_repitions-1]
for each independent parameter,
param
, theRepeatedModel
model has additional fit results representing statistics between the repetitions. For each independent parameterparam
, the results dictionary will have additional quantitiesparam_mean
andparam_peak_peak
.
If
aggregate_results
is enabled the rules for fit results are modified as follows:no statistical quantities are calculated for fit parameters or derived results
all derived results whose values and uncertainties are the same for all repetitions are aggregated together to give a single result. These results have the same name as the original model’s results, with no suffix.
all derived results whose value is not the same for al repetitions are omitted.
- __init__(model: Model, common_params: List[str] | None = None, num_repetitions: int = 2, aggregate_results=False)
- Parameters:
model – The
Model
to be repeated. The implementation ofmodel
will be used to generate data for all y axes. This model is considered owned by theRepeatedModel
and should not be used / modified elsewhere.common_params – optional list of names of model parameters, whose value is common to all y axes. All other model parameters are allowed to vary independently between the y axes
num_repetitions – the number of times the model is to be repeated
aggregate_results – determines whether derived results are aggregated or not. The default behaviour is to not aggregate results. This is generally suitable when one wants access to the values of non-common parameters from the various repetitions. Aggregating results can be useful, for example, when all parameters are common across the repetitions and one wants a single set of values reported.
Scaled Model
- class ionics_fits.models.transformations.scaled_model.ScaledModel(model: Model, x_scale: float, x_offset: float = 0.0)
Model with rescaled x-axis.
A common use-case for
ScaledModel
s is converting models between linear and angular units.- __init__(model: Model, x_scale: float, x_offset: float = 0.0)
- Parameters:
model – model to rescale. This model is considered “owned” by the
ScaledModel
and should not be used/modified elsewhere.x_scale – multiplicative x-axis scale factor. To convert a model that takes x in angular units and convert to one that takes x in linear units use
x_scale = 2 * np.pi
x_offset – additive x-axis offset