Models#

Built-in models#

Model

Parameters

Use

constant

c

offset

linear

slope, intercept

lines, backgrounds

polynomial(n)

c0 … cn

smooth backgrounds

gaussian

amp, cen, sigma

peaks; amp is the height

lorentzian

amp, cen, gamma

resonances; gamma is the half width

voigt

amp, cen, sigma, gamma

spectral lines

exponential

amp, tau

decays, amp · exp(−x/τ)

sigmoid

amp, cen, width

steps, titrations

powerlaw

amp, exponent

scaling laws

damped_sine

amp, freq, phase, tau

oscillations

Shapes such as exponential and sigmoid have no offset of their own. Add one with + fw.models.constant, see below.

Every built-in model can be evaluated directly, which is handy for simulations:

fw.models.gaussian(x, amp=2.0, cen=0.0, sigma=1.0)

Models as text#

For quick fits, write the model as an expression:

result = fw.fit("a*exp(-x/tau) + c", x, y, yerr=dy)

Parameters are all names except x, the functions and pi, in order of first appearance. Available functions include exp, log/ln, log10, sqrt, sin, cos, tan and their inverses, the hyperbolic functions, abs, erf, erfc and heaviside; see fitwise.expression.FUNCTIONS. As in Origin, Excel and MATLAB, ^ means power. For another variable name, use fw.ExpressionModel("a*exp(-t/tau)", independent="t").

Expressions are checked against an allowlist before anything runs. Only numbers, names, + - * / ** ^ and these functions are accepted, so an expression cannot access files or attributes.

Your own functions#

Any function whose first argument is the independent variable works:

def arrhenius(T, A=1e10, Ea=50e3):
    return A * np.exp(-Ea / (8.314 * T))

result = fw.fit(arrhenius, T, k, yerr=dk)

Parameter names come from the signature. Numeric defaults are used as starting values; they only need the right order of magnitude, because fitwise normalizes all parameters internally. For more control, wrap the function in fitwise.Model and give it a guess function.

x may also be two-dimensional, shape (n, k), for surface fits; it is passed to your function unchanged.

Composition#

Models combine with + and *:

model = fw.models.gaussian + fw.models.linear
model = fw.models.exponential + fw.models.constant

If a name occurs twice, prefixes are added automatically:

(gaussian + gaussian).param_names
# ('g1_amp', 'g1_cen', 'g1_sigma', 'g2_amp', 'g2_cen', 'g2_sigma')

Choose your own prefixes with gaussian.with_prefix("peak_").

Linear models need no starting values#

If a model is linear in its parameters, such as a line, a polynomial, or a·sin(x) + b·x², fitwise recognizes this automatically and solves the problem directly, without iterations or starting values. The report then shows Linear least squares (direct) and not needed (linear model). This also works for your own functions, and it reaches the certified values of the NIST linear reference datasets, including the notoriously ill-conditioned Filip polynomial.

Starting values#

For each parameter, the first applicable rule wins, and the report shows which one:

  1. p0 given in fit()

  2. a default value in the function signature

  3. the heuristic of a built-in model (peak position and width, log-linear fit of a decay, periodogram of an oscillation, …)

  4. a multi-start for everything that is still missing (see below)

Sums of built-in models are estimated step by step: first the background from the flat edges of the data, then each peak or decay on what remains.

Your own functions without starting values#

You do not have to give starting values for your own functions. fitwise then finds them like this:

  • Parameters the model is linear in, such as amplitudes and offsets, are always computed exactly.

  • For the others, 400 candidates are drawn from ranges adapted to the data: positions and widths of features, frequencies, and many orders of magnitude of both signs. The best 5 are refined.

The report shows multi-start for these parameters.

On a benchmark of 32 realistic lab models (decays, peaks, sigmoids, Arrhenius, Shockley diode, Planck, Hill, Morse, …) with random parameters, this reaches the global minimum in 99.7 % of 3300 fits. It costs about 60 ms per fit instead of 10 ms.

Where it can fail: several strongly overlapping features in one function, with many nonlinear parameters, for example two overlapping peaks on a decay. Build such models from built-in components (fw.models.exponential + fw.models.gaussian + fw.models.gaussian), which are guessed one by one, or give p0 for the positions.