Skip to content

curvefit_stats.inference

Local and resampling inference for nonlinear least-squares models.

Only NumPy and SciPy are required. Model calls use func(x, *parameters). Observations and model outputs are scalar or one-dimensional; coordinates may be multidimensional with observations on the last axis. All computations use float64. None of these methods establishes structural identifiability or exact coverage for arbitrary nonlinear models.

generate_error_bands

generate_error_bands(func: _Model, x: ArrayLike, popt: ArrayLike, pcov: ArrayLike, y: ArrayLike | None = None, alpha: float = 0.05, interval_type: Literal['confidence', 'prediction'] = 'confidence', *, x_fit: ArrayLike | None = None, dof: int | None = None, jac: _Jacobian | None = None, prediction_variance: float | ArrayLike | None = None, reference: Literal['student-t', 'normal'] = 'student-t') -> tuple[_Array, _Array]

Return pointwise Delta Method confidence or prediction bands.

Parameters:

Name Type Description Default
func callable

Model evaluated as func(x, *popt).

required
x array_like

Evaluation coordinates; observations occupy the last axis.

required
popt array_like

Fitted parameter vector of length p.

required
pcov array_like

Finite positive-semidefinite parameter covariance, shape (p, p).

required
y array_like

Original fitting observations. Used to infer degrees of freedom and, for prediction intervals, unknown common independent noise variance.

None
alpha float

Two-sided significance level.

0.05
interval_type ('confidence', 'prediction')

Confidence for fitted means or prediction for future scalar observations.

'confidence'
x_fit array_like

Fitting coordinates aligned with y; defaults to x when y is supplied.

None
dof int

Positive fitting residual degrees of freedom for Student-t intervals. Required if y is absent. Never inferred from an evaluation-only grid.

None
jac callable

Model parameter Jacobian of shape (n_evaluation, p). Without jac, numerical differentiation scales parameters by max(abs(popt), parameter standard deviation), using one for zero scales. Central differences are refined until successive columns agree within 1e-6 relative norm, with at most 40 refinements. Supply jac for nonsmooth models or derivatives that do not stabilize.

None
prediction_variance float or array_like

Nonnegative future-observation variances in evaluation order. Required for known, heteroscedastic, or correlated noise; includes only marginal noise and assumes future noise is independent of parameter estimation.

None
reference ('student-t', 'normal')

Use 'normal' explicitly for known absolute Gaussian error scales. That option does not require dof and never estimates noise from residuals.

'student-t'

Returns:

Type Description
lower_band, upper_band : ndarray

One-dimensional arrays, including for a single evaluation point.

Raises:

Type Description
ValueError

Invalid data/covariance, interval options, prediction variance, or dof.

FloatingPointError

Non-finite numerical calculations or unrepresentable band endpoints.

Notes

At each point, mean variance is j @ pcov @ j.T. Endpoints are f(x, popt) +/- q * sqrt(mean_variance + future_variance), where the future term is zero for confidence intervals. q is the specified normal or Student-t upper-tail quantile at alpha/2.

These are pointwise, not simultaneous, intervals. Residual-based prediction assumes independent homoscedastic Gaussian errors with unknown variance. The API cannot infer a noise model from pcov; choosing it is the caller's responsibility. Linearization may be unreliable near bounds or in strongly nonlinear or non-identifiable models. Unrepresentable band endpoints raise FloatingPointError instead of returning infinite or collapsed bands. Singular covariance permits zero mean uncertainty in its nullspace. A nonzero projection onto its positive-variance support that underflows to zero instead raises FloatingPointError.

Examples:

>>> import numpy as np
>>> lo, hi = generate_error_bands(lambda x, a: a*x, [1., 2.],
...     [2.], [[0.01]], dof=8)
>>> bool(np.all(lo < hi))
True

audit_fit

audit_fit(func: _Model, x: ArrayLike, y: ArrayLike, popt: ArrayLike, sigma: ArrayLike | None = None, absolute_sigma: bool = False) -> dict[str, float]

Calculate Gaussian goodness-of-fit and likelihood-consistent AIC/BIC.

Parameters:

Name Type Description Default
func callable

Model func(x, *popt).

required
x array_like

Fitting coordinates and aligned scalar observations.

required
y array_like

Fitting coordinates and aligned scalar observations.

required
popt array_like

Estimated parameters.

required
sigma array_like

Positive standard deviations (scalar/vector) or full positive-definite observation covariance, with the same interpretation as curve_fit.

None
absolute_sigma bool

True treats sigma as known; absent sigma then means known unit variance. False estimates one common scale multiplying the supplied covariance.

False

Returns:

Type Description
dict

Counts, dof, rss, rmse, residual_variance, chi_square, reduced_chi_square, aic, and bic. Counts are returned as floats.

Raises:

Type Description
ValueError

Invalid observations, parameters, sigma, absolute_sigma, or a numerically rank-deficient parameter Jacobian incompatible with regular criteria.

FloatingPointError

Residual sums or likelihood quantities are outside float64 range.

Notes

For observation covariance C, Q = r.T @ inv(C) @ r. Known-scale deviance is Nlog(2pi) + logdet(C) + Q, with K=p. Unknown-scale deviance is N(log(2pi)+1+log(Q/N)) + logdet(C), with K=p+1. AIC = deviance+2K; BIC = deviance+Klog(N).

AIC/BIC assume maximum-likelihood input estimates and count every supplied parameter. Fixed/tied parameters require a reduced model/vector; singular and boundary models need separate likelihood/parameter-count calibration. Arbitrary estimates merely evaluate a likelihood, not valid model criteria. A column-normalized, whitened numerical Jacobian must have full rank at relative cutoff max(1e-7, max(N,p)*eps); otherwise the audit rejects the input rather than reporting regular criteria with an invalid parameter count. Rank checks do not verify maximum likelihood or boundary regularity.

AIC/BIC retain the Gaussian normalization constant for consistent likelihood comparisons. Unknown scale counts as an additional fitted parameter. The unweighted residual variance is RSS/(N-p), even for weighted fits; it is not an estimate of a heteroscedastic covariance. Without known or supplied weights, chi-square is undefined (NaN). Nonpositive dof makes residual variance and reduced chi-square NaN. Zero weighted RSS with unknown scale has an unbounded likelihood and returns NaN AIC/BIC with a warning. Nonzero squared residuals outside float64 range raise FloatingPointError rather than falsely reporting an exact fit or infinite likelihood metrics.

Examples:

>>> result = audit_fit(lambda x, a: a*x, [1., 2., 3.],
...                    [2.1, 3.9, 6.0], [2.])
>>> round(result['rss'], 2)
0.02

fit_summary

fit_summary(func: _Model, x: ArrayLike, y: ArrayLike, popt: ArrayLike, pcov: ArrayLike, param_names: list[str] | None = None, *, alpha: float = 0.05, precision: int = 5) -> str

Produce an aligned ASCII table of approximate nonlinear Wald inference.

Parameters:

Name Type Description Default
func callable

Model func(x, *popt); positional parameter names are inferred.

required
x array_like

Original aligned fitting data, used to determine N-p.

required
y array_like

Original aligned fitting data, used to determine N-p.

required
popt array_like

Parameter estimates and their covariance.

required
pcov array_like

Parameter estimates and their covariance.

required
param_names list of str

Explicit parameter names overriding signature inspection.

None
alpha float

Two-sided significance level.

0.05
precision int

Positive number of significant digits in numeric columns.

5

Returns:

Type Description
str

Table with estimates, standard errors, t-statistics, two-tailed p-values, confidence endpoints, sample size, dof, and confidence level.

Raises:

Type Description
ValueError

Invalid data, covariance shape/finite PSD values, names, alpha, or precision.

FloatingPointError

A roundoff covariance repair produces unrepresentable numerical results.

Notes

SE_i = sqrt(pcov[i,i]); t_i = popt[i]/SE_i. Two-sided p-values are 2*StudentT.sf(abs(t_i), N-p), and intervals use the Student-t upper-tail quantile at alpha/2. This function always uses Student-t, including when a caller supplies known-scale or ODR covariance; it does not automatically calibrate those alternative estimator conventions.

These are approximate Wald tests against zero, not exact nonlinear tests. Singular covariance or a rank-deficient/unverifiable parameter Jacobian produces NaN standard errors, tests, and intervals instead of spurious marginal significance. Numerical rank uses column normalization and a relative singular-value cutoff of at least 1e-7. Reduce fixed/tied parameterizations before requesting unrestricted Student-t inference. Invalid finite covariance raises ValueError. Non-finite covariance or nonpositive dof warns and displays NaN for undefined inference. Zero standard errors do not imply infinite significance: tests are shown as NaN. Active bounds, small samples, and non-identifiability invalidate ordinary Wald interpretations; supply an appropriate unrestricted covariance. Overflowing statistics or unrepresentable confidence endpoints warn and display NaN.

Examples:

>>> text = fit_summary(lambda x, slope: slope*x, [1., 2., 3.],
...                    [2.1, 3.9, 6.0], [2.], [[0.01]])
>>> 'slope' in text and 'Pr(>|t|)' in text
True

profile_likelihood_errors

profile_likelihood_errors(func: _Model, x: ArrayLike, y: ArrayLike, popt: ArrayLike, sigma: ArrayLike | None = None, sigma_level: float = 1.0, bounds: tuple[Any, Any] = (-np.inf, np.inf), *, pcov: ArrayLike | None = None, max_nfev: int = 10000, max_bracket_steps: int = 50, tolerance: float = 1e-06, absolute_sigma: bool = False) -> list[dict[str, float]]

Profile parameters with bounded nuisance fits and likelihood thresholds.

Parameters:

Name Type Description Default
func callable

Model func(x, *popt).

required
x array_like

Aligned fitting coordinates and observations.

required
y array_like

Aligned fitting coordinates and observations.

required
popt array_like

Locally optimal parameters; an appreciably suboptimal input is rejected.

required
sigma array_like

Standard deviations or positive-definite observation covariance.

None
sigma_level float

Normal-equivalent central coverage; 1 corresponds to about 68.27%.

1.0
bounds tuple

Scalar or vector lower and upper parameter bounds.

(-inf, inf)
pcov array_like

Covariance used only to select initial search step sizes.

None
max_nfev int

Limits for each optimizer run and each directional bracket search.

10000
max_bracket_steps int

Limits for each optimizer run and each directional bracket search.

10000
tolerance float

Positive relative root tolerance. An improvement exceeding tolerancesigma_level*2 on the likelihood-ratio scale rejects the baseline or invalidates the affected profile direction.

1e-06
absolute_sigma bool

True uses known Gaussian covariance (unit covariance when sigma is absent). False profiles one unknown scale, including for relative sigma.

False

Returns:

Type Description
list of dict

Each parameter has signed up and down deviations. An unresolved, optimizer-failed, or boundary-limited direction is NaN with a warning; a bound itself is never reported as a likelihood threshold crossing.

Raises:

Type Description
ValueError

Invalid data, options, bounds, covariance, suboptimal starting estimates, or zero best SSE with unknown scale.

RuntimeError

Baseline nuisance optimization fails. Directional failures instead warn and leave the affected endpoint NaN.

FloatingPointError

Baseline likelihood calculations are outside float64 range.

Notes

Signed endpoints solve 2(logL_best-logL_profile) = sigma_level*2 after reoptimizing nuisance parameters. The calibration assumes a regular one-parameter chi-square likelihood-ratio reference.

Known-scale likelihood ratios are weighted SSE differences. Unknown-scale ratios are Nlog(SSE_profile/SSE_best); a zero best SSE is undefined. Threshold is sigma_level2, equivalent to a chi-square(1) quantile at the specified normal central coverage. Nuisance fits are local, not global. Boundary and nonregular models need specialized coverage calibration. Root tolerances apply to signed parameter displacements, not absolute parameter values. Exactly fixed parameters are never numerically perturbed. Non-finite trial evaluations warn and leave that endpoint unresolved. Root searches allow at most 1000 iterations. A returned finite endpoint must resolve its likelihood threshold within 10tolerance relative error; an oversized covariance seed cannot turn zero displacement into a crossing. Optimality checks use the same likelihood-ratio scale and threshold, not a fraction of total SSE. Large parameter-independent residuals therefore cannot relax a known-scale optimum check. Near-equal unknown-scale SSEs use log1p to avoid subtracting nearly equal logarithms.

Examples:

>>> intervals = profile_likelihood_errors(lambda x, a: a*x,
...     [1., 2., 3.], [2.1, 3.9, 6.0], [1.992857142857],
...     sigma=[0.1, 0.1, 0.1], absolute_sigma=True)
>>> intervals[0]['down'] < 0 < intervals[0]['up']
True

bootstrap_curve_fit

bootstrap_curve_fit(func: _Model, x: ArrayLike, y: ArrayLike, p0: ArrayLike | None = None, n_resamples: int = 1000, random_state: int | None = None, **kwargs: Any) -> tuple[_Array, _Array, _Array]

Refit a fixed number of centered, exchangeable residual resamples.

Parameters:

Name Type Description Default
func callable

SciPy-style model.

required
x array_like

Original aligned data.

required
y array_like

Original aligned data.

required
p0 array_like

Initial guess for the base fit; SciPy signature inference otherwise.

None
n_resamples int

Number of attempted replicates, not a requested number of successes.

1000
random_state int

Seed for a local numpy.random.Generator.

None
**kwargs Any

Compatible curve_fit options, including bounds and analytic jac. sigma, full_output=True, nan_policy, and check_finite=False are rejected.

{}

Returns:

Type Description
mean, covariance, samples : ndarray

Shapes (p,), (p, p), and (n_successful, p). Covariance uses B-1.

Raises:

Type Description
ValueError

Invalid data, replicate count, seed, or unsupported bootstrap options.

RuntimeError

Base covariance is unusable or fewer than two replicates succeed; a base optimizer failure also propagates.

FloatingPointError

Empirical mean or covariance is outside float64 range.

TypeError

An unsupported keyword reaches scipy.optimize.curve_fit.

Notes

For B successful parameter samples theta_b, the empirical covariance is sum(outer(theta_b-mean, theta_b-mean))/(B-1). Returned samples are rows, and no residual degrees-of-freedom inflation is applied.

Fits internally subtract a common response origin and normalize response units, including supplied analytic Jacobians. This prevents absolute trust-region stopping tolerances from certifying an unchanged initial guess in tiny response units. Known-unit base/refit covariance is transformed back before reliability checks; returned bootstrap samples retain parameter units.

Assumes independent, identically distributed observation errors with zero mean. Fitted residuals are generally correlated and have leverage-dependent variances; their empirical distribution is an approximation to the error law. Residuals are centered before resampling; no leverage correction is applied. Population covariance consistency requires regular fitting and a suitable asymptotic design (for example, vanishing maximum leverage), not just finite successful refits. Finite-sample covariance is not generally unbiased. Population parameter covariance must exist; ordinary least squares requires finite error variance. Weighted/correlated noise is not supported by this bootstrap. An invalid/non-finite/singular base covariance or rank-deficient parameter Jacobian raises RuntimeError before resampling. Each refit is checked as well, including trust-region pseudoinverse covariance without OptimizeWarning. Numerical Jacobian rank uses a column-normalized relative cutoff of at least 1e-7; analytic jac uses max(N,p)*eps. Supply analytic jac for models whose derivatives cannot be evaluated in an open neighborhood of a bound. A full- rank exact fit may return zero empirical spread; this does not establish zero population parameter uncertainty. Failed fits are excluded with a count warning; fewer than two successes raises RuntimeError. Less than 90% success triggers an additional warning. This does not provide universal outlier robustness or fix model misspecification. Non-finite synthetic observations count as failures. Unrepresentable empirical mean/covariance raises FloatingPointError; rescale parameters in that case.

Examples:

>>> mean, covariance, samples = bootstrap_curve_fit(lambda x, a: a*x,
...     [1., 2., 3., 4.], [2.1, 3.9, 6.1, 7.9], [2.],
...     n_resamples=10, random_state=3)
>>> samples.shape, covariance.shape
((10, 1), (1, 1))

collinearity_diagnostics

collinearity_diagnostics(pcov: ArrayLike, *, correlation_threshold: float = 0.99) -> dict[str, Any]

Diagnose covariance-derived local parameter dependence.

Parameters:

Name Type Description Default
pcov array_like

Square parameter covariance matrix.

required
correlation_threshold float

Absolute off-diagonal correlation flag threshold, strictly between 0 and 1.

0.99

Returns:

Type Description
dict

correlation_matrix, vif, max_abs_correlation, condition_number, high_correlation_pairs, potential_identifiability_issue, and rank. Rank is None when non-finite covariance prevents its determination. Pair indices are zero-based. Condition and rank refer to correlation among parameters with positive variance; invalid zero-variance directions are excluded and flagged. Singular valid correlation has infinite VIF and condition number rather than a finite pseudoinverse substitute.

Raises:

Type Description
ValueError

Invalid covariance shape, finite PSD values, or correlation threshold.

FloatingPointError

A roundoff covariance repair produces unrepresentable numerical results.

Notes

For positive variances, R[i,j] = V[i,j]/sqrt(V[i,i]*V[j,j]), vif = diag(inv(R)), and condition_number = max(eig(R))/min(eig(R)). These use correlation units, not the raw covariance condition number.

VIF-like values are inverse correlation diagonals from a stable solver. They are not generally classical design-matrix VIFs for nonlinear models. Strong correlation indicates local dependence, not structural non-identifiability.

Examples:

>>> result = collinearity_diagnostics([[1., 0.5], [0.5, 1.]])
>>> result['high_correlation_pairs']
[]

robust_bounded_pcov

robust_bounded_pcov(func: _Model, x: ArrayLike, y: ArrayLike, popt: ArrayLike, bounds: tuple[Any, Any], *, sigma: ArrayLike | None = None, absolute_sigma: bool = False, jac: _Jacobian | None = None, bound_tolerance: float = 1e-08) -> _Array

Estimate conditional local covariance of parameters away from bounds.

Parameters:

Name Type Description Default
func callable

Model func(x, *popt).

required
x array_like

Aligned fitting data.

required
y array_like

Aligned fitting data.

required
popt array_like

Parameters within bounds.

required
bounds tuple

Scalar or vector lower/upper bounds; equality denotes an exactly fixed parameter but still produces NaN covariance rows/columns here.

required
sigma array_like

Standard deviations or positive-definite observation covariance.

None
absolute_sigma bool

If False, scale free covariance by weighted RSS/(N-p_free).

False
jac callable

Model Jacobian of shape (N, p); finite differences otherwise.

None
bound_tolerance float

Nonnegative tolerance multiplied by max(1, |parameter|, |finite bound|) separately for each bound. Numerical derivatives of free parameters require the model to be defined in a small open neighborhood.

1e-08

Returns:

Type Description
ndarray

Full (p, p) matrix. Active rows/columns are NaN. Free covariance is conditional on active parameters, not unrestricted marginal uncertainty. Rank-deficient free Jacobians or undefined scaling return NaN blocks.

Raises:

Type Description
ValueError

Invalid data, parameters, bounds, sigma, Jacobian, or activity tolerance.

FloatingPointError

Numerical differentiation or whitening produces non-finite results.

Notes

Holding active parameters fixed, the free block is approximately inv(J_free.T @ inv(C) @ J_free). Unknown scale multiplies it by Q/(N-p_free). Active rows/columns are NaN, not zero uncertainty.

This does not repair structural non-identifiability, nor infer one-sided uncertainty for active parameters. All-active inputs return all NaN. Conditional means holding active values fixed in the model; it does not calibrate the sampling distribution conditional on selecting that active set. Effective dof is N-p_free under the stated local conditional approximation. Rank is assessed after Jacobian column normalization, then covariance is transformed back to the original parameter units. A covariance outside float64 range warns and returns NaN, rather than finite zero uncertainty.

Examples:

>>> covariance = robust_bounded_pcov(lambda x, a, b: a*x+b,
...     [0., 1., 2.], [1., 3., 5.], [2., 1.],
...     ([0., 1.], [3., 1.]), absolute_sigma=True)
>>> bool(np.isnan(covariance[1]).all())
True

propagate_uncertainty

propagate_uncertainty(derived_func: Callable[..., Any], popt: ArrayLike, pcov: ArrayLike, *, n_samples: int = 10000, alpha: float = 0.05, random_state: int | None = None, min_valid_fraction: float = 0.9) -> dict[str, Any]

Propagate Gaussian parameter uncertainty by sample-wise Monte Carlo.

Parameters:

Name Type Description Default
derived_func callable

Scalar or fixed-shape vector output from derived_func(*parameters). No vectorization is assumed; arbitrary callable errors propagate.

required
popt array_like

Parameter mean and finite positive-semidefinite covariance.

required
pcov array_like

Parameter mean and finite positive-semidefinite covariance.

required
n_samples int

Number of attempted samples, at least two.

10000
alpha float

Two-sided empirical interval significance level.

0.05
random_state int

Seed for a local Generator, without modifying global RNG state.

None
min_valid_fraction float

Minimum fraction of finite outputs, in (0, 1]; default 0.9. At least two valid outputs are always required. Lower fractions raise ValueError.

0.9

Returns:

Type Description
dict

mean, median, std (ddof=1), lower, upper, n_samples, n_valid, n_invalid. Statistics are Python floats for scalar output and arrays for vectors. A vector sample is rejected in full if any component is non-finite.

Raises:

Type Description
ValueError

Invalid covariance, parameter/sample options, changing output shape, or too few valid derived samples.

FloatingPointError

Parameter uncertainty is below float64 resolution or summaries are unrepresentable. Exceptions raised by derived_func propagate unchanged.

Notes

Draw theta from Normal(popt, pcov), then evaluate g(theta) sample-wise. Standard deviation uses the valid sample count minus one; endpoints are empirical alpha/2 and 1-alpha/2 quantiles, not simultaneous vector regions.

Covariance PSD and rank checks use correlation units so parameter units cannot hide negative variance or create false singularity. Negative diagonal variances are rejected. Correlation eigenvalues in [-100epsp, 0) are repaired with a warning; zero variance requires an exactly zero row and column. Singular covariance warns. Non-finite outputs are counted and warned about; retained quantiles describe the valid subset and may be biased by rejection. A Gaussian approximation may violate physical bounds or miss asymmetric likelihoods. Finite ratio outputs do not guarantee that population moments exist. Unrepresentable numerical summaries or parameter uncertainty below floating point resolution at popt raise FloatingPointError; recenter/rescale instead. Seed reproducibility is guaranteed within a fixed NumPy/LAPACK environment, not bit-for-bit across backend versions or repeated eigenvalue conventions.

Examples:

>>> result = propagate_uncertainty(lambda a, b: a+b, [1., 2.],
...     [[0.01, 0.], [0., 0.01]], n_samples=100, random_state=7)
>>> result['n_valid']
100