1.3. Understanding Results¶
This tutorial explains how to interpret the output from fit() and calculate
parameter uncertainties.
1.3.1. The Two Return Values¶
fit() returns two values:
popt, pcov = fit(model, x, y, p0=[...])
popt: Optimal parameters (1D array)
pcov: Covariance matrix (2D array)
1.3.2. Optimal Parameters (popt)¶
popt contains the fitted parameter values in the same order as your model:
def model(x, a, b, c):
return a * jnp.exp(-b * x) + c
popt, pcov = fit(model, x, y, p0=[1, 0.5, 0])
# popt[0] = a (amplitude)
# popt[1] = b (decay rate)
# popt[2] = c (offset)
a_fit, b_fit, c_fit = popt
1.3.3. Covariance Matrix (pcov)¶
pcov is a square matrix where:
Diagonal elements: Variance of each parameter
Off-diagonal elements: Covariance between parameters
pcov = [[var(a), cov(a,b), cov(a,c)],
[cov(b,a), var(b), cov(b,c)],
[cov(c,a), cov(c,b), var(c) ]]
1.3.4. Calculating Parameter Uncertainties¶
The standard error (1-sigma uncertainty) of each parameter is the square root of the diagonal:
import numpy as np
# Standard errors (1-sigma)
perr = np.sqrt(np.diag(pcov))
a_fit, b_fit, c_fit = popt
a_err, b_err, c_err = perr
print(f"a = {a_fit:.4f} +/- {a_err:.4f}")
print(f"b = {b_fit:.4f} +/- {b_err:.4f}")
print(f"c = {c_fit:.4f} +/- {c_err:.4f}")
1.3.5. Confidence Intervals¶
For different confidence levels, multiply the standard error:
68.3% (1-sigma):
perr95.4% (2-sigma):
2 * perr99.7% (3-sigma):
3 * perr
# 95% confidence interval
ci_95 = 1.96 * perr
print(f"a = {a_fit:.4f} +/- {ci_95[0]:.4f} (95% CI)")
1.3.6. Parameter Correlations¶
The correlation matrix shows how parameters are related:
# Correlation matrix (normalized covariance)
d = np.sqrt(np.diag(pcov))
corr = pcov / np.outer(d, d)
print("Correlation matrix:")
print(corr)
+1: Parameters are perfectly correlated
-1: Parameters are perfectly anti-correlated
0: Parameters are independent
High correlations (absolute value > 0.9) indicate the parameters may be difficult to determine independently.
1.3.7. Goodness of Fit¶
Compute residuals and chi-squared:
# Compute residuals
y_fit = model(x, *popt)
residuals = y - y_fit
# Sum of squared residuals
ss_res = np.sum(residuals**2)
# Chi-squared (if sigma is provided)
if sigma is not None:
chi_sq = np.sum((residuals / sigma) ** 2)
dof = len(y) - len(popt) # degrees of freedom
reduced_chi_sq = chi_sq / dof
print(f"Reduced chi-squared: {reduced_chi_sq:.3f}")
# Good fit: reduced chi-sq ~ 1.0
# R-squared
ss_tot = np.sum((y - np.mean(y)) ** 2)
r_squared = 1 - (ss_res / ss_tot)
print(f"R-squared: {r_squared:.4f}")
1.3.8. Complete Example¶
from nlsq import fit
import jax.numpy as jnp
import numpy as np
# Model
def exponential(x, a, b, c):
return a * jnp.exp(-b * x) + c
# Generate data
np.random.seed(42)
x = np.linspace(0, 10, 100)
y_true = 2.5 * np.exp(-0.5 * x) + 0.3
sigma = 0.1
y = y_true + sigma * np.random.normal(size=len(x))
# Fit
popt, pcov = fit(exponential, x, y, p0=[2, 0.5, 0], sigma=sigma)
# Extract results
a, b, c = popt
perr = np.sqrt(np.diag(pcov))
print("Fitted parameters:")
print(f" a = {a:.4f} +/- {perr[0]:.4f}")
print(f" b = {b:.4f} +/- {perr[1]:.4f}")
print(f" c = {c:.4f} +/- {perr[2]:.4f}")
# Goodness of fit
y_fit = exponential(x, *popt)
residuals = y - y_fit
chi_sq = np.sum((residuals / sigma) ** 2)
dof = len(y) - len(popt)
print(f"\nReduced chi-squared: {chi_sq/dof:.3f}")
1.3.9. When pcov is inf¶
If pcov contains inf values, the covariance could not be estimated.
This typically means:
Poor fit: The model doesn’t describe the data well
Ill-conditioned: Parameters are highly correlated or unidentifiable
Insufficient data: Not enough points to constrain all parameters
Solutions:
Check if the model is appropriate
Provide better initial guesses
Add bounds to constrain parameters
Use
workflow='auto_global'for robust fitting
1.3.10. Absolute vs Relative Sigma¶
The absolute_sigma parameter affects uncertainty scaling:
# Sigma represents actual measurement uncertainties
popt, pcov = fit(model, x, y, p0=[...], sigma=sigma, absolute_sigma=True)
# Sigma represents relative weights (default)
popt, pcov = fit(model, x, y, p0=[...], sigma=sigma, absolute_sigma=False)
With absolute_sigma=False (default), uncertainties are scaled based on
the residual variance, which may be more appropriate when actual measurement
errors are unknown.
1.3.11. Next Steps¶
Now that you understand the basics, continue to The 3-Workflow System to learn about NLSQ’s 3-workflow system for handling different fitting scenarios.