Model Functions Reference

NLSQ provides built-in model functions for common curve fitting scenarios.

Using Built-in Functions

Import from nlsq.core.functions:

from nlsq.core.functions import gaussian, exponential_decay, lorentzian

All functions are JAX-compatible and JIT-compilable. Each function includes:

  • Automatic p0 estimation via .estimate_p0(xdata, ydata)

  • Reasonable default bounds via .bounds()

Peak Functions

gaussian

nlsq.core.functions.gaussian(x, amp, mu, sigma)[source]

Gaussian (normal distribution) function: y = amp * exp(-(x-mu)² / (2*sigma²))

Common for spectral peaks, chromatography peaks, probability distributions.

Parameters:
  • x (array_like) – Independent variable

  • amp (float) – Amplitude (peak height)

  • mu (float) – Mean (center position, peak location)

  • sigma (float) – Standard deviation (width parameter, positive)

Returns:

y – Dependent variable

Return type:

array_like

Notes

  • Peak position: x = mu

  • Peak height: y = amp

  • FWHM (Full Width at Half Maximum): FWHM = 2.355 * sigma

  • Integral: ∫ gaussian dx = amp * sigma * sqrt(2π)

Examples

>>> from nlsq import curve_fit
>>> from nlsq.core.functions import gaussian
>>> import numpy as np
>>>
>>> # Spectral peak at x=5 with FWHM ≈ 2.355
>>> x = np.linspace(0, 10, 200)
>>> y = 10 * np.exp(-(x-5)**2 / (2*1**2)) + np.random.normal(0, 0.2, 200)
>>> popt, pcov = curve_fit(gaussian, x, y, p0='auto')
>>> print(f"Peak at {popt[1]:.2f}, FWHM = {2.355*popt[2]:.2f}")

lorentzian

nlsq.core.functions.lorentzian(x, amp, x0, gamma)[source]

Lorentzian (Cauchy) peak function: y = amp / (1 + ((x - x0) / gamma)²)

Common for spectral line shapes (NMR, IR, Raman), resonance curves, and natural line broadening.

Parameters:
  • x (array_like) – Independent variable

  • amp (float) – Amplitude (peak height)

  • x0 (float) – Center position (peak location)

  • gamma (float) – Half-width at half-maximum (HWHM, positive)

Returns:

y – Dependent variable

Return type:

array_like

Notes

  • Peak position: x = x0

  • Peak height: y = amp

  • FWHM (Full Width at Half Maximum): FWHM = 2 * gamma

  • Integral: ∫ lorentzian dx = amp * gamma * π

  • Heavier tails than Gaussian (decays as 1/x² vs exp(-x²))

Examples

>>> from nlsq import curve_fit
>>> from nlsq.core.functions import lorentzian
>>> import numpy as np
>>>
>>> # Spectral peak at x=5 with FWHM = 2
>>> x = np.linspace(0, 10, 200)
>>> y = 10 / (1 + ((x - 5) / 1)**2) + np.random.normal(0, 0.2, 200)
>>> popt, pcov = curve_fit(lorentzian, x, y, p0='auto')
>>> print(f"Peak at {popt[1]:.2f}, FWHM = {2*popt[2]:.2f}")

Exponential Functions

exponential_decay

nlsq.core.functions.exponential_decay(x, a, b, c)[source]

Exponential decay: y = a * exp(-b*x) + c

Common for radioactive decay, cooling curves, discharge curves.

Parameters:
  • x (array_like) – Independent variable (time, distance, etc.)

  • a (float) – Amplitude (initial value minus asymptote)

  • b (float) – Decay rate (positive, units: 1/x)

  • c (float) – Offset (asymptotic value as x → ∞)

Returns:

y – Dependent variable

Return type:

array_like

Notes

  • Half-life: t_half = ln(2) / b

  • Time constant: τ = 1 / b

  • At x=0: y = a + c

  • As x→∞: y → c

Examples

>>> from nlsq import curve_fit
>>> from nlsq.core.functions import exponential_decay
>>> import numpy as np
>>>
>>> # Radioactive decay with half-life = ln(2)/0.5 ≈ 1.4
>>> x = np.linspace(0, 10, 100)
>>> y = 100 * np.exp(-0.5 * x) + 10 + np.random.normal(0, 2, 100)
>>> popt, pcov = curve_fit(exponential_decay, x, y, p0='auto')
>>> print(f"Half-life: {np.log(2)/popt[1]:.2f}")

exponential_growth

nlsq.core.functions.exponential_growth(x, a, b, c)[source]

Exponential growth: y = a * exp(b*x) + c

Common for population growth, compound interest, bacterial growth.

Parameters:
  • x (array_like) – Independent variable (time, distance, etc.)

  • a (float) – Initial amplitude

  • b (float) – Growth rate (positive, units: 1/x)

  • c (float) – Offset (baseline)

Returns:

y – Dependent variable

Return type:

array_like

Notes

  • Doubling time: t_double = ln(2) / b

  • At x=0: y = a + c

  • As x→∞: y → ∞ (unbounded growth)

Examples

>>> from nlsq import curve_fit
>>> from nlsq.core.functions import exponential_growth
>>> import numpy as np
>>>
>>> # Bacterial growth with doubling time = ln(2)/0.3 ≈ 2.3
>>> x = np.linspace(0, 5, 50)
>>> y = 10 * np.exp(0.3 * x) + np.random.normal(0, 1, 50)
>>> popt, pcov = curve_fit(exponential_growth, x, y, p0='auto')
>>> print(f"Doubling time: {np.log(2)/popt[1]:.2f}")

Sigmoid Functions

sigmoid

nlsq.core.functions.sigmoid(x, L, x0, k, b)[source]

Sigmoid (logistic) function: y = L / (1 + exp(-k*(x-x0))) + b

Common for dose-response curves, growth saturation, S-curves.

Parameters:
  • x (array_like) – Independent variable

  • L (float) – Maximum value (saturation level)

  • x0 (float) – Midpoint (inflection point, x value at half-maximum)

  • k (float) – Steepness (growth rate, positive)

  • b (float) – Baseline offset (minimum asymptote)

Returns:

y – Dependent variable

Return type:

array_like

Notes

  • At x=x0: y = L/2 + b (midpoint)

  • As x→-∞: y → b (lower asymptote)

  • As x→+∞: y → L + b (upper asymptote)

  • Steeper curve: larger k

Examples

>>> from nlsq import curve_fit
>>> from nlsq.core.functions import sigmoid
>>> import numpy as np
>>>
>>> # Dose-response curve
>>> x = np.linspace(0, 10, 100)
>>> y = 5 / (1 + np.exp(-2*(x-5))) + 1 + np.random.normal(0, 0.1, 100)
>>> popt, pcov = curve_fit(sigmoid, x, y, p0='auto')
>>> print(f"EC50 (midpoint): {popt[1]:.2f}")

Power Functions

power_law

nlsq.core.functions.power_law(x, a, b)[source]

Power law function: y = a * x^b

Common for scaling relationships, fractals, allometry.

Parameters:
  • x (array_like) – Independent variable (must be positive)

  • a (float) – Prefactor (amplitude)

  • b (float) – Exponent (power)

Returns:

y – Dependent variable

Return type:

array_like

Notes

  • b > 0: increasing function (growth)

  • b < 0: decreasing function (decay)

  • b = 1: linear relationship

  • For x=1: y = a

Examples

>>> from nlsq import curve_fit
>>> from nlsq.core.functions import power_law
>>> import numpy as np
>>>
>>> # Allometric scaling: metabolic rate ∝ mass^0.75
>>> x = np.linspace(1, 100, 50)
>>> y = 3 * x**0.75 + np.random.normal(0, 0.5, 50)
>>> popt, pcov = curve_fit(power_law, x, y, p0='auto')
>>> print(f"Scaling exponent: {popt[1]:.2f}")

linear

nlsq.core.functions.linear(x, a, b)[source]

Linear function: y = a*x + b

Parameters:
  • x (array_like) – Independent variable

  • a (float) – Slope

  • b (float) – Intercept

Returns:

y – Dependent variable

Return type:

array_like

Examples

>>> from nlsq import curve_fit
>>> from nlsq.core.functions import linear
>>> import numpy as np
>>>
>>> x = np.array([1, 2, 3, 4, 5])
>>> y = 2 * x + 3 + np.random.normal(0, 0.1, 5)
>>> popt, pcov = curve_fit(linear, x, y, p0='auto')
>>> print(f"Slope: {popt[0]:.2f}, Intercept: {popt[1]:.2f}")

polynomial

nlsq.core.functions.polynomial(degree)[source]

Create polynomial function of given degree.

Returns a function that computes: y = c0*x^n + c1*x^(n-1) + … + cn where n is the degree.

Parameters:

degree (int) – Polynomial degree (0, 1, 2, 3, …)

Returns:

poly_func – Polynomial function with signature poly(x, *coeffs)

Return type:

callable

Examples

>>> from nlsq import curve_fit
>>> from nlsq.core.functions import polynomial
>>> import numpy as np
>>>
>>> # Fit quadratic: y = ax² + bx + c
>>> quadratic = polynomial(2)
>>> x = np.linspace(-5, 5, 50)
>>> y = 2*x**2 + 3*x + 1 + np.random.normal(0, 0.5, 50)
>>> popt, pcov = curve_fit(quadratic, x, y, p0='auto')
>>> print(f"Coefficients: {popt}")

Polynomial function factory for arbitrary degree.

Example:

import jax.numpy as jnp


# Define directly for curve fitting
def quadratic(x, a, b, c):
    return a + b * x + c * x**2

Creating Custom Functions

Custom model functions must use JAX operations:

import jax.numpy as jnp


def custom_model(x, a, b, c, d):
    """Custom model combining exponential and oscillation."""
    decay = a * jnp.exp(-b * x)
    oscillation = c * jnp.sin(d * x)
    return decay * oscillation


# Use like any built-in function
popt, pcov = fit(custom_model, x, y, p0=[1, 0.1, 1, 2 * jnp.pi])

Rules for custom functions:

  1. Use jax.numpy instead of numpy

  2. Avoid Python control flow on traced values

  3. Keep functions pure (no side effects)

  4. First parameter must be the independent variable

See Also