thztools.fit#
- thztools.fit(frfun, xdata, ydata, p0, noise_parms=None, *, dt=None, numpy_sign_convention=True, args=(), kwargs=None, f_bounds=None, p_bounds=None, jac=None, lsq_options=None)[source]#
Fit a parameterized frequency response function to time-domain data.
Determines the total least-squares fit to
xdata
andydata
, given the parameterized frequency response function relationshipfrfun
and noise model parametersnoise_parms
.- Parameters:
- frfuncallable
Frequency response function with signature
frfun(omega, *p, *args, **kwargs)
that returns anndarray
. Assumes the \(+i\omega t\) convention for harmonic time dependence whennumpy_sign_convention
isTrue
, the default. All elements ofp
andargs
and all values ofkwargs
must be real quantities.- xdataarray_like
Measured input signal.
- ydataarray_like
Measured output signal.
- p0array_like
Initial guess for the parameters
p
.- noise_parmsarray_like or None, optional
Noise parameters
(sigma_alpha, sigma_beta, sigma_tau)
used to define the :class:NoiseModel
for the fit. Default isNone
, which setsnoise_parms
to(1.0, 0.0, 0.0)
.- dtfloat or None, optional
Sampling time, normally in picoseconds. Default is
None
, which setsdt
tothztools.options.sampling_time
. If bothdt
andthztools.options.sampling_time
areNone
,dt
is set to1.0
. In this case, the angular frequencyomega
must be given in units of radians per sampling time, and any parameters inargs
must be expressed withdt
as the unit of time.- numpy_sign_conventionbool, optional
Adopt NumPy sign convention for harmonic time dependence, e.g, express a harmonic function with frequency \(\omega\) as \(x(t) = a e^{i\omega t}\). Default is
True
. When set toFalse
, uses the convention more common in physics, \(x(t) = a e^{-i\omega t}\).- argstuple
Additional arguments for
frfun
. All elements must be real.- kwargsdict or None, optional
Additional keyword arguments for
frfun
. Default isNone
, which passes no keyword arguments. All values must be real.- f_boundsarray_like, optional
Lower and upper bounds on the frequencies included in the fit. For
f_bounds = (f_lo, f_hi)
, the included frequenciesf
satisfyf_lo <= f <= f_hi
.The default is
None
, which setsf_bounds
to include all frequencies except zero:f_bounds = (np.finfo(np.float64).smallest_normal, np.inf)
.Set the lower bound to
0.0
or any negative number to include zero frequency.- p_boundsNone, 2-tuple of array_like, or
scipy.optimize.Bounds
, optional Lower and upper bounds on fit parameter(s). Default is
None
, which sets all parameter bounds to(-np.inf, np.inf)
. Each bound constraint(p_lo, p_hi)
represents a closed interval for the parameterp
,p_lo <= p <= p_hi
.- jaccallable or None, optional
Jacobian of the frequency response function with respect to the fit parameters, with signature
jac(w, *p, *args, **kwargs)
. Default isNone
, which usesscipy.optimize.approx_fprime
.- lsq_optionsdict or None, optional
Keyword options passed to
scipy.optimize.least_squares
. The following keywords are allowed:“ftol” : Tolerance for termination by the change of the cost function. Default is 1e-8.
“xtol” : Tolerance for termination by the change of the independent variables. Default is 1e-8.
“gtol” : Tolerance for termination by the norm of the gradient. Default is 1e-8.
“loss” : Determines the loss function. Default is “linear”, which gives a standard least-squares problem. Alternative settings have not been tested and should be considered experimental.
“f_scale” : Value of soft margin between inlier and outlier residuals. This parameter has no effect with
loss="linear"
. Default is 1.0.“max_nfev” : Maximum number of function evaluations before the termination. If None (default), the value is chosen automatically.
“verbose” : Level of algorithm’s verbosity. Default is 0, which works silently.
See the documentation for
scipy.optimize.least_squares
for details.
- Returns:
- resFitResult
Fit result represented as a
FitResult
object. Important attributes are:p_opt
, the optimal fit parameter values;p_cov
, the parameter covariance matrix estimated from fit;resnorm
, the value of the total least-squares cost function for the fit;dof
, the number of statistical degrees of freedom in the fit;r_tls
, andsuccess
, which isTrue
when the fit converges. See the Notes section below and theFitResult
documentation for more details and for a description of other attributes.
- Raises:
- ValueError
If
noise_parms
does not have 3 elements.
- Warns:
- UserWarning
If
thztools.options.sampling_time
and thedt
parameter are both notNone
and are set to differentfloat
values, the function will set the sampling time todt
and raise aUserWarning
.
See also
NoiseModel
Noise model class.
Notes
This function computes the maximum-likelihood estimate for the parameters \(\boldsymbol{\theta}\) in the frequency response function model \(H(\omega; \boldsymbol{\theta})\) by minimizing the total least-squares cost function
\[Q_\text{TLS}(\boldsymbol{\theta}) \ = \sum_{k=0}^{N-1}\left[ \ \frac{(x_k - \mu_k)^2}{\sigma_{\mathbf{x},k}^2} \ + \frac{(y_k - \psi_k)^2}{\sigma_{\mathbf{y},k}^2} \ \right],\]where \(\mathbf{x}\) is the input signal array, \(\mathbf{y}\) is the output signal array, and \(\boldsymbol{\sigma}_{\mathbf{y}}\), \(\boldsymbol{\sigma}_{\mathbf{y}}\) are their associated noise amplitudes. The arrays \(\boldsymbol{\mu}\) and \(\boldsymbol{\psi}\) are the ideal input and output signal arrays, respectively, which are related by the constraint
\[[\mathcal{F}\boldsymbol{\psi}]_k = \ [H(\omega_k; \boldsymbol{\theta})] \ [\mathcal{F}\boldsymbol{\mu}]_k\]at all discrete Fourier transform frequencies \(\omega_k\) within the frequency bounds.
The optimization step uses
scipy.optimize.least_squares
withp
andmu
as free parameters. It minimizes the Euclidean norm of the residual vectornp.concat((delta / sigma_x, epsilon / sigma_y)
,where
delta = xdata - mu
,epsilon = ydata - psi
,sigma_x = NoiseModel(*noise_parms, dt=dt).noise_amp(xdata)
,sigma_y = NoiseModel(*noise_parms, dt=dt).noise_amp(ydata)
, andpsi = thz.apply_frf(frfun(omega, *p, *args, **kwargs), mu, dt=dt)
.
References
[1]Laleh Mohtashemi, Paul Westlund, Derek G. Sahota, Graham B. Lea, Ian Bushfield, Payam Mousavi, and J. Steven Dodge, “Maximum- likelihood parameter estimation in terahertz time-domain spectroscopy,” Opt. Express 29, 4912-4926 (2021), https://doi.org/10.1364/OE.417724.
Examples
>>> import numpy as np >>> import thztools as thz >>> from matplotlib import pyplot as plt >>> n, dt = 256, 0.05 >>> t = thz.timebase(n, dt=dt) >>> mu = thz.wave(n, dt=dt) >>> alpha, beta, tau = 1e-4, 1e-2, 1e-3 >>> noise_model = thz.NoiseModel(sigma_alpha=alpha, sigma_beta=beta, ... sigma_tau=tau, dt=dt)
>>> def frfun(w, amplitude, delay): ... return amplitude * np.exp(1j * w * delay) >>> >>> p0 = (0.5, 1.0) >>> psi = thz.apply_frf(frfun, mu, dt=dt, args=p0) >>> xdata = mu + noise_model.noise_sim(mu, seed=0) >>> ydata = psi + noise_model.noise_sim(psi, seed=1) >>> result = thz.fit(frfun, xdata, ydata, p0, (alpha, beta, tau), dt=dt) >>> result.success True >>> result.p_opt array([0.49..., 1.00...]) >>> result.resnorm 198.266...
>>> _, ax = plt.subplots() >>> ax.plot(t, result.r_tls, ".") >>> ax.set_xlabel("t (ps)") >>> ax.set_ylabel(r"$r_\mathrm{TLS}$") >>> plt.show()