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
xdataandydata, given the parameterized frequency response function relationshipfrfunand 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_conventionisTrue, the default. All elements ofpandargsand all values ofkwargsmust 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:NoiseModelfor the fit. Default isNone, which setsnoise_parmsto(1.0, 0.0, 0.0).- dtfloat or None, optional
Sampling time, normally in picoseconds. Default is
None, which setsdttothztools.options.sampling_time. If bothdtandthztools.options.sampling_timeareNone,dtis set to1.0. In this case, the angular frequencyomegamust be given in units of radians per sampling time, and any parameters inargsmust be expressed withdtas 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 frequenciesfsatisfyf_lo <= f <= f_hi.The default is
None, which setsf_boundsto include all frequencies except zero:f_bounds = (np.finfo(np.float64).smallest_normal, np.inf).Set the lower bound to
0.0or 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_squaresfor details.
- Returns:
- resFitResult
Fit result represented as a
FitResultobject. 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 isTruewhen the fit converges. See the Notes section below and theFitResultdocumentation for more details and for a description of other attributes.
- Raises:
- ValueError
If
noise_parmsdoes not have 3 elements.
- Warns:
- UserWarning
If
thztools.options.sampling_timeand thedtparameter are both notNoneand are set to differentfloatvalues, the function will set the sampling time todtand raise aUserWarning.
See also
NoiseModelNoise 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_squareswithpandmuas 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()