nlsq.result module

Result types for NLSQ optimization operations.

This module provides consolidated result types that were previously scattered across the codebase. As of v0.4.3, OptimizeResult and OptimizeWarning have been moved here from nlsq.core._optimize.

Result types for NLSQ optimization.

This package provides the canonical location for optimization result types:

  • OptimizeResult: Base container for optimization results with attribute access

  • OptimizeResultV2: Memory-efficient frozen dataclass (v0.4.3+)

  • OptimizeWarning: Warning class for non-critical optimization issues

  • CurveFitResult: Enhanced result with statistical properties and visualization

Example

>>> from nlsq.result import OptimizeResult, OptimizeResultV2
>>> result = OptimizeResult(x=[1.0, 2.0], success=True)
>>> result.x
[1.0, 2.0]
class nlsq.result.CurveFitResult(*args, **kwargs)[source]

Bases: OptimizeResult

Enhanced curve fitting result with statistical properties and visualization.

This class extends OptimizeResult with convenience methods for statistical analysis and visualization. It maintains backward compatibility by supporting tuple unpacking: popt, pcov = curve_fit(...).

The result can be used in two ways:

  1. Tuple unpacking (backward compatible):

    popt, pcov = curve_fit(model, x, y)
    
  2. Enhanced result object:

    result = curve_fit(model, x, y)
    print(f"R² = {result.r_squared:.4f}")
    print(f"RMSE = {result.rmse:.4f}")
    result.plot()
    result.summary()
    

Additional Attributes

modelcallable

The model function f(x, *params) used for fitting.

xdataarray_like

The independent variable data.

ydataarray_like

The dependent variable data (observations).

poptarray_like

Fitted parameters (alias for self.x).

pcovarray_like

Parameter covariance matrix.

diagnosticsDiagnosticsReport | None

Model health diagnostics report (if compute_diagnostics=True).

Statistical Properties

r_squaredfloat

Coefficient of determination (R²). Measures goodness of fit. Range: (-∞, 1], where 1 is perfect fit.

adj_r_squaredfloat

Adjusted R² accounting for number of parameters. Preferred over R² when comparing models with different parameters.

rmsefloat

Root mean squared error. Lower is better.

maefloat

Mean absolute error. Robust to outliers.

aicfloat

Akaike Information Criterion. Lower is better. Used for model selection.

bicfloat

Bayesian Information Criterion. Lower is better. Penalizes model complexity more than AIC.

residualsarray_like

Residuals (observed - predicted). Should be random for good fit.

predictionsarray_like

Model predictions at xdata points.

confidence_intervals(alpha=0.95)[source]

Compute parameter confidence intervals.

prediction_interval(x, alpha=0.95)[source]

Compute prediction interval at new x values.

plot(ax=None, show_residuals=True)[source]

Plot data, fit, and residuals.

summary()[source]

Print statistical summary table.

Examples

Basic usage with enhanced features:

import numpy as np
import jax.numpy as jnp
from nlsq import curve_fit

# Define model
def exponential(x, a, b, c):
    return a * jnp.exp(-b * x) + c

# Generate data
x = np.linspace(0, 10, 100)
y_true = 10 * np.exp(-0.5 * x) + 2
y = y_true + np.random.normal(0, 0.5, 100)

# Fit and analyze
result = curve_fit(exponential, x, y, p0=[10, 0.5, 2])

print(f"R² = {result.r_squared:.4f}")
print(f"RMSE = {result.rmse:.4f}")
print(f"AIC = {result.aic:.2f}")

# Get confidence intervals
ci = result.confidence_intervals(alpha=0.95)
print(f"95% CI for parameters: {ci}")

# Visualize fit
result.plot()

# Statistical summary
result.summary()

With diagnostics:

result = curve_fit(exponential, x, y, compute_diagnostics=True)
print(result.diagnostics.summary())
print(result.diagnostics.identifiability.health_status)

Backward compatibility:

# Tuple unpacking still works
popt, pcov = curve_fit(exponential, x, y)

# But enhanced features available if not unpacked
result = curve_fit(exponential, x, y)
result.plot()
__init__(*args, **kwargs)[source]

Initialize enhanced curve fit result.

__iter__()[source]

Support tuple unpacking: popt, pcov = curve_fit(…)

property popt

Fitted parameters (alias for self.x).

Returns:

popt – Fitted parameters as NumPy array for SciPy compatibility.

Return type:

ndarray

property pcov

Parameter covariance matrix.

Returns:

pcov – Covariance matrix as NumPy array for SciPy compatibility.

Return type:

ndarray

property diagnostics

Model health diagnostics report.

Returns the health report if compute_diagnostics=True was specified when calling curve_fit(), otherwise returns None.

Returns:

diagnostics – Aggregated model health report containing identifiability analysis, gradient health monitoring, and other health metrics. None if diagnostics were not computed.

Return type:

ModelHealthReport | None

Examples

>>> result = curve_fit(model, x, y, compute_diagnostics=True)
>>> if result.diagnostics is not None:
...     print(result.diagnostics.summary())
...     print(result.diagnostics.status)
...     print(result.diagnostics.health_score)
...     if result.diagnostics.identifiability is not None:
...         print(result.diagnostics.identifiability.health_status)

See also

nlsq.diagnostics.ModelHealthReport

Aggregated health report

nlsq.diagnostics.IdentifiabilityReport

Identifiability analysis

nlsq.diagnostics.GradientHealthReport

Gradient health monitoring

property predictions

Model predictions at xdata points.

Returns:

predictions – Model predictions: f(xdata, *popt)

Return type:

ndarray

property residuals

Residuals (observed - predicted).

Returns:

residuals – Residuals: ydata - predictions

Return type:

ndarray

Notes

For a good fit, residuals should be randomly distributed around zero with no systematic patterns.

property r_squared

Coefficient of determination (R²).

Returns:

r2 – R² value in range (-∞, 1]. Values closer to 1 indicate better fit.

Return type:

float

Notes

R² = 1 - SS_res / SS_tot

where SS_res = sum((y - y_pred)²) and SS_tot = sum((y - y_mean)²)

Interpretation: - R² = 1: Perfect fit - R² = 0: Model no better than mean - R² < 0: Model worse than mean (overfitting or poor model)

property adj_r_squared

Adjusted R² accounting for number of parameters.

Returns:

adj_r2 – Adjusted R² value.

Return type:

float

Notes

Adj R² = 1 - (1 - R²) * (n - 1) / (n - p - 1)

where n is number of data points and p is number of parameters.

Adjusted R² penalizes adding parameters and is better for comparing models with different numbers of parameters.

property rmse

Root mean squared error.

Returns:

rmse – Root mean squared error: sqrt(mean(residuals²))

Return type:

float

Notes

RMSE has the same units as ydata and provides intuitive error measure. Lower values indicate better fit.

property mae

Mean absolute error.

Returns:

mae – Mean absolute error: mean(|residuals|)

Return type:

float

Notes

MAE is more robust to outliers than RMSE.

property aic

Akaike Information Criterion.

Returns:

aic – AIC value. Lower is better.

Return type:

float

Notes

AIC = 2k + n*ln(RSS/n)

where k is number of parameters, n is number of data points, and RSS is residual sum of squares.

Used for model selection. Penalizes model complexity.

property bic

Bayesian Information Criterion.

Returns:

bic – BIC value. Lower is better.

Return type:

float

Notes

BIC = k*ln(n) + n*ln(RSS/n)

where k is number of parameters, n is number of data points, and RSS is residual sum of squares.

BIC penalizes model complexity more heavily than AIC. Preferred for larger datasets.

confidence_intervals(alpha=0.95)[source]

Compute parameter confidence intervals.

Parameters:

alpha (float, optional) – Confidence level (default: 0.95 for 95% CI).

Returns:

intervals – Array of shape (n_params, 2) with [lower, upper] bounds for each parameter.

Return type:

ndarray

Examples

>>> result = curve_fit(model, x, y)
>>> ci = result.confidence_intervals(alpha=0.95)
>>> for i, (lower, upper) in enumerate(ci):
...     print(f"Parameter {i}: [{lower:.3f}, {upper:.3f}]")

Notes

Confidence intervals are computed using the parameter covariance matrix and Student’s t-distribution. Assumes residuals are normally distributed.

prediction_interval(x=None, alpha=0.95)[source]

Compute prediction interval at x values.

Parameters:
  • x (array_like, optional) – x values for prediction. If None, uses self.xdata (default: None).

  • alpha (float, optional) – Confidence level (default: 0.95).

Returns:

intervals – Array of shape (n_points, 2) with [lower, upper] bounds for each point.

Return type:

ndarray

Examples

>>> result = curve_fit(model, x, y)
>>> pi = result.prediction_interval()  # Use fitted x values
>>> pi_new = result.prediction_interval(x_new)  # Use new x values

Notes

Prediction intervals account for both parameter uncertainty (from pcov) and inherent data variability (residual variance).

confidence_band(x=None, alpha=0.95)[source]

Compute mean prediction confidence band.

Parameters:
  • x (array_like, optional) – x values for prediction. If None, uses self.xdata (default: None).

  • alpha (float, optional) – Confidence level (default: 0.95 for 95% CI).

Returns:

  • lower (ndarray) – Lower confidence bound.

  • upper (ndarray) – Upper confidence bound.

Notes

This calculates the confidence interval for the mean response (f(x)), representing the uncertainty in the model curve itself due to parameter uncertainties.

plot(ax=None, show_residuals=True, show_confidence=True, **kwargs)[source]

Plot data, fitted curve, and residuals.

Parameters:
  • ax (matplotlib.axes.Axes, optional) – Axes to plot on. If None, creates new figure.

  • show_residuals (bool, optional) – Whether to show residual plot (default: True).

  • show_confidence (bool, optional) – Whether to show 95% confidence band (default: True).

  • **kwargs – Additional keyword arguments passed to plotting functions.

Returns:

  • fig (matplotlib.figure.Figure) – Figure object.

  • axes (matplotlib.axes.Axes or array of Axes) – Axes object(s).

Examples

>>> result = curve_fit(model, x, y)
>>> result.plot()
>>> plt.show()
summary()[source]

Print statistical summary of fit.

Displays: - Fitted parameters with standard errors - Goodness of fit metrics (R², RMSE, MAE) - Model selection criteria (AIC, BIC) - Convergence information - Diagnostics summary (if available)

Examples

>>> result = curve_fit(model, x, y)
>>> result.summary()
class nlsq.result.OptimizeResult[source]

Bases: dict

Optimization result container for NLSQ curve fitting operations.

This class stores the complete results from nonlinear least squares optimization performed using JAX-accelerated algorithms. It extends dict to provide both dictionary-style and attribute-style access to optimization results.

Core Attributes

xjax.numpy.ndarray or numpy.ndarray

Optimized parameter vector containing the final fitted parameters. These represent the solution to the nonlinear least squares problem.

successbool

Indicates whether the optimization terminated successfully. True means convergence criteria were satisfied within tolerance limits.

statusint

Numerical termination status code indicating why optimization stopped:

  • 1: Gradient convergence (||g||_inf < gtol)

  • 2: Step size convergence (||dx||/||x|| < xtol)

  • 3: Function value convergence (delta_f/f < ftol)

  • 0: Maximum iterations reached

  • -1: Evaluation limit exceeded

  • -3: Inner loop iteration limit (algorithm-specific)

messagestr

Human-readable description of termination cause. Provides detailed information about convergence status or failure reasons.

Objective Function Results

funjax.numpy.ndarray

Final residual vector f(x) at the solution. For curve fitting, these are the differences between model predictions and data points.

costfloat

Final cost function value: 0.5 * ||f(x)||² for standard least squares, or 0.5 * sum(ρ(f_i²/σ²)) for robust loss functions.

jacjax.numpy.ndarray

Final Jacobian matrix J(x) with shape (m, n) where m is number of data points and n is number of parameters. Computed using JAX autodiff.

gradjax.numpy.ndarray

Final gradient vector g = J^T * f with shape (n,). Used for convergence checking and parameter uncertainty estimation.

Convergence Metrics

optimalityfloat

Final gradient norm ||g||_inf used for convergence assessment. Should be less than gtol for successful convergence.

active_masknumpy.ndarray

Boolean mask indicating which parameters hit bounds (for bounded optimization). Shape (n,) with True for parameters at constraints.

Iteration Statistics

nfevint

Total number of objective function evaluations during optimization. Each evaluation computes residuals f(x) for given parameters.

njevint

Total number of Jacobian evaluations. With JAX autodiff, this equals the number of combined function+gradient evaluations.

nitint

Number of optimization iterations completed. Not always available for all algorithms.

Algorithm-Specific Results

pcovjax.numpy.ndarray, optional

Parameter covariance matrix with shape (n, n). Provides parameter uncertainty estimates. Available when uncertainty estimation is requested. Computed as: pcov = inv(J^T * J) * residual_variance

active_masknumpy.ndarray

For bounded optimization, indicates which parameters are at bounds.

all_timesdict, optional

Detailed timing information for algorithm profiling. Contains timing data for different optimization phases (function evaluation, Jacobian computation, linear algebra operations, etc.).

Usage Examples

Basic result access:

import nlsq

# Perform curve fitting
result = nlsq.curve_fit(model_func, x_data, y_data, p0=initial_guess)

# Access optimized parameters
fitted_params = result.x

# Check convergence
if result.success:
    print(f"Optimization converged: {result.message}")
    print(f"Final cost: {result.cost}")
    print(f"Function evaluations: {result.nfev}")
else:
    print(f"Optimization failed: {result.message}")

# Parameter uncertainties (if covariance computed)
if hasattr(result, 'pcov'):
    param_errors = jnp.sqrt(jnp.diag(result.pcov))
    print(f"Parameter uncertainties: {param_errors}")

Advanced result inspection:

# Examine residuals and fit quality
final_residuals = result.fun
rms_error = jnp.sqrt(jnp.mean(final_residuals**2))

# Check gradient convergence
gradient_norm = result.optimality
print(f"Final gradient norm: {gradient_norm}")

# Analyze Jacobian condition
jacobian = result.jac
condition_number = jnp.linalg.cond(jacobian)
print(f"Jacobian condition number: {condition_number}")

# For bounded problems, check active constraints
if hasattr(result, 'active_mask'):
    constrained_params = jnp.where(result.active_mask)[0]
    print(f"Parameters at bounds: {constrained_params}")

Integration with SciPy

This class maintains compatibility with scipy.optimize.OptimizeResult while adding JAX-specific features and NLSQ-specific results. It can be used interchangeably with SciPy optimization results in most contexts.

Technical Notes

  • All JAX arrays are automatically converted to NumPy arrays for compatibility

  • Covariance matrices use double precision for numerical stability

  • Large dataset results may include memory management statistics

  • GPU timing results require explicit timing mode activation

  • Progress monitoring data is stored in algorithm-specific attributes

nlsq.result.OptimizeResultLegacy

alias of OptimizeResult

class nlsq.result.OptimizeResultV2(x, success, cost, fun, jac=None, grad=None, optimality=0.0, active_mask=None, nfev=0, njev=0, nit=0, status=0, message='', pcov=None, all_times=None)[source]

Bases: object

Memory-efficient optimization result container (v2).

This class provides a memory-efficient alternative to OptimizeResult using Python’s frozen dataclass with slots. It offers:

  • ~40% memory reduction per instance (no __dict__)

  • ~2x faster attribute access (direct slot access vs dict lookup)

  • Immutability for thread-safety and caching

Core Attributes

xjnp.ndarray

Optimized parameter vector containing the final fitted parameters.

successbool

Indicates whether the optimization terminated successfully.

costfloat

Final cost function value: 0.5 * ||f(x)||².

funjnp.ndarray

Final residual vector f(x) at the solution.

Optional Attributes

jacjnp.ndarray | None

Final Jacobian matrix J(x). None if not requested (saves ~400KB for 10k×50).

gradjnp.ndarray | None

Final gradient vector g = J^T * f.

optimalityfloat

Final gradient norm ||g||_inf.

active_maskjnp.ndarray | None

Boolean mask indicating which parameters hit bounds.

nfevint

Total number of objective function evaluations.

njevint

Total number of Jacobian evaluations.

nitint

Number of optimization iterations completed.

statusint

Numerical termination status code.

messagestr

Human-readable description of termination cause.

pcovjnp.ndarray | None

Parameter covariance matrix.

all_timesdict | None

Detailed timing information for profiling.

Examples

>>> result.x        # Access optimized parameters
>>> result.success  # Check convergence
>>> result.cost     # Get final cost value
>>> result.to_dict()  # Convert to dictionary
x: Array
success: bool
cost: float
fun: Array
jac: Array | None
grad: Array | None
optimality: float
active_mask: Array | None
nfev: int
njev: int
nit: int
status: int
message: str
pcov: Array | None
all_times: dict[str, Any] | None
to_dict()[source]

Convert to dictionary.

Returns:

Dictionary containing all non-None fields.

Return type:

dict

__repr__()[source]

Compact representation showing key fields.

__init__(x, success, cost, fun, jac=None, grad=None, optimality=0.0, active_mask=None, nfev=0, njev=0, nit=0, status=0, message='', pcov=None, all_times=None)
exception nlsq.result.OptimizeWarning[source]

Bases: UserWarning

Warning class for optimization-related issues.

This warning is raised when non-critical issues are encountered during optimization, such as unknown solver options, convergence concerns, or numerical stability warnings that don’t prevent the optimization from completing but should be brought to the user’s attention.

Common scenarios:
  • Unknown or deprecated solver options passed to optimizer

  • Convergence achieved but with warnings about numerical conditioning

  • Parameter bounds adjusted automatically

  • Automatic algorithm selection overrides

Example

>>> import warnings
>>> warnings.filterwarnings('error', category=OptimizeWarning)
>>> # Now OptimizeWarning will raise an exception instead of warning

See also

nlsq.error_messages.OptimizationError : Exception for critical failures

Classes

OptimizeResult

class nlsq.result.OptimizeResult[source]

Bases: dict

Optimization result container for NLSQ curve fitting operations.

This class stores the complete results from nonlinear least squares optimization performed using JAX-accelerated algorithms. It extends dict to provide both dictionary-style and attribute-style access to optimization results.

Core Attributes

xjax.numpy.ndarray or numpy.ndarray

Optimized parameter vector containing the final fitted parameters. These represent the solution to the nonlinear least squares problem.

successbool

Indicates whether the optimization terminated successfully. True means convergence criteria were satisfied within tolerance limits.

statusint

Numerical termination status code indicating why optimization stopped:

  • 1: Gradient convergence (||g||_inf < gtol)

  • 2: Step size convergence (||dx||/||x|| < xtol)

  • 3: Function value convergence (delta_f/f < ftol)

  • 0: Maximum iterations reached

  • -1: Evaluation limit exceeded

  • -3: Inner loop iteration limit (algorithm-specific)

messagestr

Human-readable description of termination cause. Provides detailed information about convergence status or failure reasons.

Objective Function Results

funjax.numpy.ndarray

Final residual vector f(x) at the solution. For curve fitting, these are the differences between model predictions and data points.

costfloat

Final cost function value: 0.5 * ||f(x)||² for standard least squares, or 0.5 * sum(ρ(f_i²/σ²)) for robust loss functions.

jacjax.numpy.ndarray

Final Jacobian matrix J(x) with shape (m, n) where m is number of data points and n is number of parameters. Computed using JAX autodiff.

gradjax.numpy.ndarray

Final gradient vector g = J^T * f with shape (n,). Used for convergence checking and parameter uncertainty estimation.

Convergence Metrics

optimalityfloat

Final gradient norm ||g||_inf used for convergence assessment. Should be less than gtol for successful convergence.

active_masknumpy.ndarray

Boolean mask indicating which parameters hit bounds (for bounded optimization). Shape (n,) with True for parameters at constraints.

Iteration Statistics

nfevint

Total number of objective function evaluations during optimization. Each evaluation computes residuals f(x) for given parameters.

njevint

Total number of Jacobian evaluations. With JAX autodiff, this equals the number of combined function+gradient evaluations.

nitint

Number of optimization iterations completed. Not always available for all algorithms.

Algorithm-Specific Results

pcovjax.numpy.ndarray, optional

Parameter covariance matrix with shape (n, n). Provides parameter uncertainty estimates. Available when uncertainty estimation is requested. Computed as: pcov = inv(J^T * J) * residual_variance

active_masknumpy.ndarray

For bounded optimization, indicates which parameters are at bounds.

all_timesdict, optional

Detailed timing information for algorithm profiling. Contains timing data for different optimization phases (function evaluation, Jacobian computation, linear algebra operations, etc.).

Usage Examples

Basic result access:

import nlsq

# Perform curve fitting
result = nlsq.curve_fit(model_func, x_data, y_data, p0=initial_guess)

# Access optimized parameters
fitted_params = result.x

# Check convergence
if result.success:
    print(f"Optimization converged: {result.message}")
    print(f"Final cost: {result.cost}")
    print(f"Function evaluations: {result.nfev}")
else:
    print(f"Optimization failed: {result.message}")

# Parameter uncertainties (if covariance computed)
if hasattr(result, 'pcov'):
    param_errors = jnp.sqrt(jnp.diag(result.pcov))
    print(f"Parameter uncertainties: {param_errors}")

Advanced result inspection:

# Examine residuals and fit quality
final_residuals = result.fun
rms_error = jnp.sqrt(jnp.mean(final_residuals**2))

# Check gradient convergence
gradient_norm = result.optimality
print(f"Final gradient norm: {gradient_norm}")

# Analyze Jacobian condition
jacobian = result.jac
condition_number = jnp.linalg.cond(jacobian)
print(f"Jacobian condition number: {condition_number}")

# For bounded problems, check active constraints
if hasattr(result, 'active_mask'):
    constrained_params = jnp.where(result.active_mask)[0]
    print(f"Parameters at bounds: {constrained_params}")

Integration with SciPy

This class maintains compatibility with scipy.optimize.OptimizeResult while adding JAX-specific features and NLSQ-specific results. It can be used interchangeably with SciPy optimization results in most contexts.

Technical Notes

  • All JAX arrays are automatically converted to NumPy arrays for compatibility

  • Covariance matrices use double precision for numerical stability

  • Large dataset results may include memory management statistics

  • GPU timing results require explicit timing mode activation

  • Progress monitoring data is stored in algorithm-specific attributes

OptimizeWarning

class nlsq.result.OptimizeWarning[source]

Bases: UserWarning

Warning class for optimization-related issues.

This warning is raised when non-critical issues are encountered during optimization, such as unknown solver options, convergence concerns, or numerical stability warnings that don’t prevent the optimization from completing but should be brought to the user’s attention.

Common scenarios:
  • Unknown or deprecated solver options passed to optimizer

  • Convergence achieved but with warnings about numerical conditioning

  • Parameter bounds adjusted automatically

  • Automatic algorithm selection overrides

Example

>>> import warnings
>>> warnings.filterwarnings('error', category=OptimizeWarning)
>>> # Now OptimizeWarning will raise an exception instead of warning

See also

nlsq.error_messages.OptimizationError : Exception for critical failures

Example Usage

from nlsq import curve_fit

# Perform fit
popt, pcov = curve_fit(model, x, y, p0=[1.0, 0.1], full_output=True)

# Access additional result information
# (when using LeastSquares directly)
from nlsq import LeastSquares

ls = LeastSquares()
result = ls.least_squares(residuals, p0)

print(f"Success: {result.success}")
print(f"Message: {result.message}")
print(f"Number of iterations: {result.nit}")
print(f"Final cost: {result.cost}")

Result Attributes

The OptimizeResult object contains:

  • x: Solution parameters

  • success: Whether optimization succeeded

  • status: Termination status code

  • message: Description of termination

  • fun: Final residuals

  • jac: Final Jacobian matrix

  • cost: Final cost value

  • nfev: Number of function evaluations

  • njev: Number of Jacobian evaluations

  • nit: Number of iterations

Migration Notes

As of v0.4.3, OptimizeResult and OptimizeWarning have been moved from nlsq.core._optimize to nlsq.result. The old import paths continue to work with deprecation warnings during the 12-month transition period:

# Old (deprecated)
from nlsq.core._optimize import OptimizeResult

# New (recommended)
from nlsq.result import OptimizeResult

# or
from nlsq import OptimizeResult

See Also