nlsq.stability.guard module¶
Numerical stability management for NLSQ optimization.
This module provides comprehensive numerical stability monitoring and correction capabilities for the NLSQ package, ensuring robust optimization even with ill-conditioned problems or extreme parameter values.
Stability Modes¶
The stability parameter in curve_fit() controls numerical stability behavior:
stability=False(default): No stability checks. Maximum performance.stability='check': Check for issues and warn, but don’t modify data.stability='auto': Automatically detect and fix numerical issues.
Key Design Decisions (v0.3.0)¶
Initialization-only Jacobian checks: Stability checks on the Jacobian are performed only at optimization initialization, not per-iteration. Per-iteration Jacobian modification was found to cause optimization divergence due to accumulated numerical perturbations. (commit 8028a03)
SVD skip for large Jacobians: For Jacobians exceeding max_jacobian_elements_for_svd (default 10M elements), SVD computation is skipped to avoid O(min(m,n)^2 * max(m,n)) overhead. Only O(n) NaN/Inf checking is performed.
rescale_data parameter: Applications requiring unit preservation can set rescale_data=False to preserve physical units. The default (True) rescales data to [0, 1] when ill-conditioning is detected.
Module Constants¶
- MAX_JACOBIAN_ELEMENTS_FOR_SVDint = 10_000_000
Default maximum Jacobian elements for SVD computation. For Jacobians exceeding this threshold, SVD is skipped to avoid excessive computation.
Performance characteristics: - 10M element Jacobian: ~1.5GB RAM, ~5 seconds (GPU) - SVD per iteration can exceed total optimization time - For large datasets (1M points x 3 params = 3M), SVD is computed - For large datasets (10M points x 3 params = 30M), SVD is skipped
- CONDITION_THRESHOLDfloat = 1e12
Threshold for detecting ill-conditioned matrices. Condition numbers above this trigger regularization warnings or automatic fixes.
- REGULARIZATION_FACTORfloat = 1e-10
Default Tikhonov regularization factor for ill-conditioned problems.
See also
NumericalStabilityGuardMain class for stability operations
apply_automatic_fixesFunction to apply stability fixes to data
check_problem_stabilityPre-flight stability check for optimization
- class nlsq.stability.guard.NumericalStabilityGuard(max_jacobian_elements_for_svd=10000000)[source]¶
Bases:
objectComprehensive numerical stability monitoring and correction.
This class provides methods to detect and correct numerical issues that can arise during optimization, including: - NaN/Inf detection and correction - Ill-conditioning detection and regularization - Overflow/underflow protection - Safe mathematical operations
- __init__(max_jacobian_elements_for_svd=10000000)[source]¶
Initialize stability guard with numerical constants.
- Parameters:
max_jacobian_elements_for_svd (int, default 10_000_000) – Maximum number of elements in Jacobian (m × n) for which SVD will be computed. For larger Jacobians, SVD is skipped to avoid excessive computation time. Default is 10M elements, which corresponds to e.g., a (1M × 10) or (100K × 100) matrix.
- eps¶
- max_float¶
- min_float¶
- condition_threshold¶
- regularization_factor¶
- max_exp_arg¶
- min_exp_arg¶
- max_jacobian_elements_for_svd¶
- check_and_fix_jacobian(J)[source]¶
Check Jacobian for numerical issues and fix them.
This method performs several checks and corrections: 1. Detects and replaces NaN/Inf values 2. Computes condition number (skipped for large Jacobians) 3. Applies regularization if ill-conditioned 4. Checks for near-zero singular values
For large Jacobians (> max_jacobian_elements_for_svd elements), SVD computation is skipped to avoid excessive computation time. Only NaN/Inf checking is performed.
- check_parameters(params)[source]¶
Check and fix parameter values.
- Parameters:
params (jnp.ndarray) – Parameter vector to check
- Returns:
params_fixed – Fixed parameter vector
- Return type:
jnp.ndarray
- safe_exp(x)[source]¶
Exponential with overflow/underflow protection.
- Parameters:
x (jnp.ndarray) – Input array
- Returns:
result – exp(x) with values clipped to prevent overflow
- Return type:
jnp.ndarray
- safe_log(x)[source]¶
Logarithm with domain protection.
- Parameters:
x (jnp.ndarray) – Input array (must be positive)
- Returns:
result – log(x) with values clipped to ensure positive domain
- Return type:
jnp.ndarray
- safe_divide(numerator, denominator)[source]¶
Division with zero-protection.
- Parameters:
numerator (jnp.ndarray) – Numerator array
denominator (jnp.ndarray) – Denominator array
- Returns:
result – numerator/denominator with small values in denominator replaced
- Return type:
jnp.ndarray
- safe_sqrt(x)[source]¶
Square root with domain protection.
- Parameters:
x (jnp.ndarray) – Input array
- Returns:
result – sqrt(x) with negative values set to 0
- Return type:
jnp.ndarray
- safe_power(base, exponent)[source]¶
Safe power operation.
- Parameters:
base (jnp.ndarray) – Base array
exponent (float) – Power exponent
- Returns:
result – base^exponent with numerical safety
- Return type:
jnp.ndarray
- check_gradient(gradient)[source]¶
Check and fix gradient values.
- Parameters:
gradient (jnp.ndarray) – Gradient vector
- Returns:
gradient_fixed – Fixed gradient with clipping applied if needed
- Return type:
jnp.ndarray
- regularize_hessian(H, min_eigenvalue=1e-08)[source]¶
Regularize Hessian to ensure positive definiteness.
- Parameters:
H (jnp.ndarray) – Hessian or Hessian approximation matrix
min_eigenvalue (float) – Minimum eigenvalue to ensure
- Returns:
H_reg – Regularized Hessian
- Return type:
jnp.ndarray
- nlsq.stability.guard.apply_automatic_fixes(xdata, ydata, p0=None, stability_report=None, rescale_data=True)[source]¶
Automatically apply fixes for detected stability issues.
This function applies common fixes such as rescaling data and parameters based on the stability report.
- Parameters:
xdata (array_like) – Independent variable data
ydata (array_like) – Dependent variable data
p0 (array_like, optional) – Initial parameter guess
stability_report (dict, optional) – Report from check_problem_stability(). If None, will be computed.
rescale_data (bool, optional) – If True (default), rescale xdata/ydata to [0, 1] when ill-conditioned or large range is detected. Set to False for applications requiring unit preservation where data must maintain physical units (e.g., time in seconds, frequency in Hz). NaN/Inf handling and parameter normalization are still applied when stability=’auto’. Default: True.
- Returns:
xdata_fixed (ndarray) – Fixed xdata
ydata_fixed (ndarray) – Fixed ydata
p0_fixed (ndarray or None) – Fixed p0
fix_info (dict) – Information about applied fixes with keys: - ‘applied_fixes’: list of str - ‘x_scale’: float - ‘y_scale’: float - ‘x_offset’: float - ‘y_offset’: float
- Return type:
Examples
>>> x = np.linspace(0, 1e6, 100) >>> y = 2.0 * x + 1.0 >>> x_fixed, y_fixed, p0_fixed, info = apply_automatic_fixes(x, y, [2.0, 1.0]) >>> print(f"Applied fixes: {info['applied_fixes']}")
>>> # For applications requiring unit preservation, disable rescaling >>> x_fixed, y_fixed, p0_fixed, info = apply_automatic_fixes( ... x, y, [2.0, 1.0], rescale_data=False ... )
- nlsq.stability.guard.check_problem_stability(xdata, ydata, p0=None, f=None)[source]¶
Comprehensive pre-flight stability check for optimization problem.
Identifies potential numerical issues before optimization begins, providing warnings and recommendations for fixes.
- Parameters:
xdata (array_like) – Independent variable data
ydata (array_like) – Dependent variable data
p0 (array_like, optional) – Initial parameter guess
f (callable, optional) – Model function (currently unused, reserved for future checks)
- Returns:
report – Stability report with keys: - ‘issues’: list of (issue_type, message, severity) tuples - ‘condition_number’: float - ‘parameter_scale_ratio’: float or None - ‘has_collinearity’: bool - ‘recommendations’: list of str - ‘severity’: str (‘ok’, ‘warning’, ‘critical’)
- Return type:
Examples
>>> x = np.linspace(0, 1e6, 100) >>> y = 2.0 * x + 1.0 >>> p0 = [2.0, 1.0] >>> report = check_problem_stability(x, y, p0) >>> print(f"Severity: {report['severity']}") >>> for issue_type, message, severity in report['issues']: ... print(f"{severity}: {message}")
- nlsq.stability.guard.detect_collinearity(xdata, threshold=0.95)[source]¶
Detect collinearity in multidimensional input data.
Collinearity occurs when predictors are highly correlated, leading to unstable parameter estimates.
- Parameters:
xdata (array_like) – Independent variable data (multidimensional)
threshold (float, optional) – Correlation threshold for detecting collinearity. Default: 0.95
- Returns:
has_collinearity (bool) – True if any pair of variables is highly correlated
collinear_pairs (list of tuple) – List of (i, j, correlation) for collinear variable pairs
- Return type:
Examples
>>> x1 = np.linspace(0, 10, 100) >>> x2 = 2 * x1 + 0.01 * np.random.randn(100) # Nearly collinear >>> xdata = np.column_stack([x1, x2]) >>> has_coll, pairs = detect_collinearity(xdata) >>> print(f"Collinear: {has_coll}") Collinear: True
- nlsq.stability.guard.detect_parameter_scale_mismatch(p0, threshold=1000000.0)[source]¶
Detect if parameter scales differ by too many orders of magnitude.
Large scale differences can cause numerical issues and slow convergence.
- Parameters:
p0 (array_like) – Initial parameter guess
threshold (float, optional) – Ratio threshold for detecting mismatch. Default: 1e6
- Returns:
has_mismatch (bool) – True if parameter scales differ by more than threshold
scale_ratio (float) – Ratio of largest to smallest parameter magnitude
- Return type:
Examples
>>> p0 = np.array([1e-3, 1e3, 1.0]) >>> has_mismatch, ratio = detect_parameter_scale_mismatch(p0) >>> print(f"Mismatch: {has_mismatch}, Ratio: {ratio:.2e}") Mismatch: True, Ratio: 1.00e+06
- nlsq.stability.guard.estimate_condition_number(xdata)[source]¶
Estimate the condition number of the data matrix.
This checks if the independent variable data is well-conditioned for least squares fitting. High condition numbers indicate numerical instability.
- Parameters:
xdata (array_like) – Independent variable data (can be 1D or 2D)
- Returns:
condition_number – Estimated condition number. Values > 1e10 indicate potential problems.
- Return type:
Notes
For 1D data, constructs a Vandermonde-like matrix with [1, x, x^2]. For multidimensional data, computes the condition number directly.
- nlsq.stability.guard.solve_with_cholesky_fallback(A, b)[source]¶
Solve linear system Ax = b using Cholesky with eigenvalue fallback.
This function attempts Cholesky decomposition first (O(n³/3)) and falls back to eigenvalue decomposition (O(n³)) only if Cholesky fails. The fallback is detected via NaN check, making this function JAX JIT-compatible.
- Parameters:
A (jnp.ndarray) – Symmetric matrix (n × n). Should be positive definite for Cholesky to succeed.
b (jnp.ndarray) – Right-hand side vector (n,).
- Returns:
x (jnp.ndarray) – Solution vector (n,).
used_cholesky (jnp.ndarray) – Boolean mask indicating whether Cholesky was used (True) or the eigenvalue fallback was needed (False).
- Return type:
Notes
For positive definite matrices (e.g., J^T J + λI with λ > 0), Cholesky is approximately 3x faster than eigenvalue decomposition. The JAX-compatible fallback pattern uses NaN detection instead of Python try/except.
Examples
>>> import jax.numpy as jnp >>> A = jnp.array([[4.0, 2.0], [2.0, 3.0]]) # Positive definite >>> b = jnp.array([1.0, 2.0]) >>> x, used_cholesky = solve_with_cholesky_fallback(A, b) >>> used_cholesky True
Overview¶
The guard module provides comprehensive numerical stability monitoring and
correction capabilities for the NLSQ package, ensuring robust optimization even
with ill-conditioned problems or extreme parameter values.
Stability Modes¶
The stability parameter in curve_fit() controls
numerical stability behavior:
stability=False(default): No stability checks. Maximum performance.stability='check': Check for issues and warn, but don’t modify data.stability='auto': Automatically detect and fix numerical issues.
Key Features¶
Condition number estimation via singular values (avoids full SVD overhead)
Automatic data rescaling for ill-conditioned problems
Collinearity detection among model parameters
Parameter scale mismatch detection
Cholesky-with-fallback linear solver for JIT-compatible robustness
Classes¶
- class nlsq.stability.guard.NumericalStabilityGuard(max_jacobian_elements_for_svd=10000000)[source]¶
Bases:
objectComprehensive numerical stability monitoring and correction.
This class provides methods to detect and correct numerical issues that can arise during optimization, including: - NaN/Inf detection and correction - Ill-conditioning detection and regularization - Overflow/underflow protection - Safe mathematical operations
- __init__(max_jacobian_elements_for_svd=10000000)[source]¶
Initialize stability guard with numerical constants.
- Parameters:
max_jacobian_elements_for_svd (int, default 10_000_000) – Maximum number of elements in Jacobian (m × n) for which SVD will be computed. For larger Jacobians, SVD is skipped to avoid excessive computation time. Default is 10M elements, which corresponds to e.g., a (1M × 10) or (100K × 100) matrix.
- eps¶
- max_float¶
- min_float¶
- condition_threshold¶
- regularization_factor¶
- max_exp_arg¶
- min_exp_arg¶
- max_jacobian_elements_for_svd¶
- check_and_fix_jacobian(J)[source]¶
Check Jacobian for numerical issues and fix them.
This method performs several checks and corrections: 1. Detects and replaces NaN/Inf values 2. Computes condition number (skipped for large Jacobians) 3. Applies regularization if ill-conditioned 4. Checks for near-zero singular values
For large Jacobians (> max_jacobian_elements_for_svd elements), SVD computation is skipped to avoid excessive computation time. Only NaN/Inf checking is performed.
- check_parameters(params)[source]¶
Check and fix parameter values.
- Parameters:
params (jnp.ndarray) – Parameter vector to check
- Returns:
params_fixed – Fixed parameter vector
- Return type:
jnp.ndarray
- safe_exp(x)[source]¶
Exponential with overflow/underflow protection.
- Parameters:
x (jnp.ndarray) – Input array
- Returns:
result – exp(x) with values clipped to prevent overflow
- Return type:
jnp.ndarray
- safe_log(x)[source]¶
Logarithm with domain protection.
- Parameters:
x (jnp.ndarray) – Input array (must be positive)
- Returns:
result – log(x) with values clipped to ensure positive domain
- Return type:
jnp.ndarray
- safe_divide(numerator, denominator)[source]¶
Division with zero-protection.
- Parameters:
numerator (jnp.ndarray) – Numerator array
denominator (jnp.ndarray) – Denominator array
- Returns:
result – numerator/denominator with small values in denominator replaced
- Return type:
jnp.ndarray
- safe_sqrt(x)[source]¶
Square root with domain protection.
- Parameters:
x (jnp.ndarray) – Input array
- Returns:
result – sqrt(x) with negative values set to 0
- Return type:
jnp.ndarray
- safe_power(base, exponent)[source]¶
Safe power operation.
- Parameters:
base (jnp.ndarray) – Base array
exponent (float) – Power exponent
- Returns:
result – base^exponent with numerical safety
- Return type:
jnp.ndarray
- check_gradient(gradient)[source]¶
Check and fix gradient values.
- Parameters:
gradient (jnp.ndarray) – Gradient vector
- Returns:
gradient_fixed – Fixed gradient with clipping applied if needed
- Return type:
jnp.ndarray
- regularize_hessian(H, min_eigenvalue=1e-08)[source]¶
Regularize Hessian to ensure positive definiteness.
- Parameters:
H (jnp.ndarray) – Hessian or Hessian approximation matrix
min_eigenvalue (float) – Minimum eigenvalue to ensure
- Returns:
H_reg – Regularized Hessian
- Return type:
jnp.ndarray
Functions¶
- nlsq.stability.guard.apply_automatic_fixes(xdata, ydata, p0=None, stability_report=None, rescale_data=True)[source]¶
Automatically apply fixes for detected stability issues.
This function applies common fixes such as rescaling data and parameters based on the stability report.
- Parameters:
xdata (array_like) – Independent variable data
ydata (array_like) – Dependent variable data
p0 (array_like, optional) – Initial parameter guess
stability_report (dict, optional) – Report from check_problem_stability(). If None, will be computed.
rescale_data (bool, optional) – If True (default), rescale xdata/ydata to [0, 1] when ill-conditioned or large range is detected. Set to False for applications requiring unit preservation where data must maintain physical units (e.g., time in seconds, frequency in Hz). NaN/Inf handling and parameter normalization are still applied when stability=’auto’. Default: True.
- Returns:
xdata_fixed (ndarray) – Fixed xdata
ydata_fixed (ndarray) – Fixed ydata
p0_fixed (ndarray or None) – Fixed p0
fix_info (dict) – Information about applied fixes with keys: - ‘applied_fixes’: list of str - ‘x_scale’: float - ‘y_scale’: float - ‘x_offset’: float - ‘y_offset’: float
- Return type:
Examples
>>> x = np.linspace(0, 1e6, 100) >>> y = 2.0 * x + 1.0 >>> x_fixed, y_fixed, p0_fixed, info = apply_automatic_fixes(x, y, [2.0, 1.0]) >>> print(f"Applied fixes: {info['applied_fixes']}")
>>> # For applications requiring unit preservation, disable rescaling >>> x_fixed, y_fixed, p0_fixed, info = apply_automatic_fixes( ... x, y, [2.0, 1.0], rescale_data=False ... )
- nlsq.stability.guard.check_problem_stability(xdata, ydata, p0=None, f=None)[source]¶
Comprehensive pre-flight stability check for optimization problem.
Identifies potential numerical issues before optimization begins, providing warnings and recommendations for fixes.
- Parameters:
xdata (array_like) – Independent variable data
ydata (array_like) – Dependent variable data
p0 (array_like, optional) – Initial parameter guess
f (callable, optional) – Model function (currently unused, reserved for future checks)
- Returns:
report – Stability report with keys: - ‘issues’: list of (issue_type, message, severity) tuples - ‘condition_number’: float - ‘parameter_scale_ratio’: float or None - ‘has_collinearity’: bool - ‘recommendations’: list of str - ‘severity’: str (‘ok’, ‘warning’, ‘critical’)
- Return type:
Examples
>>> x = np.linspace(0, 1e6, 100) >>> y = 2.0 * x + 1.0 >>> p0 = [2.0, 1.0] >>> report = check_problem_stability(x, y, p0) >>> print(f"Severity: {report['severity']}") >>> for issue_type, message, severity in report['issues']: ... print(f"{severity}: {message}")
- nlsq.stability.guard.detect_collinearity(xdata, threshold=0.95)[source]¶
Detect collinearity in multidimensional input data.
Collinearity occurs when predictors are highly correlated, leading to unstable parameter estimates.
- Parameters:
xdata (array_like) – Independent variable data (multidimensional)
threshold (float, optional) – Correlation threshold for detecting collinearity. Default: 0.95
- Returns:
has_collinearity (bool) – True if any pair of variables is highly correlated
collinear_pairs (list of tuple) – List of (i, j, correlation) for collinear variable pairs
- Return type:
Examples
>>> x1 = np.linspace(0, 10, 100) >>> x2 = 2 * x1 + 0.01 * np.random.randn(100) # Nearly collinear >>> xdata = np.column_stack([x1, x2]) >>> has_coll, pairs = detect_collinearity(xdata) >>> print(f"Collinear: {has_coll}") Collinear: True
- nlsq.stability.guard.detect_parameter_scale_mismatch(p0, threshold=1000000.0)[source]¶
Detect if parameter scales differ by too many orders of magnitude.
Large scale differences can cause numerical issues and slow convergence.
- Parameters:
p0 (array_like) – Initial parameter guess
threshold (float, optional) – Ratio threshold for detecting mismatch. Default: 1e6
- Returns:
has_mismatch (bool) – True if parameter scales differ by more than threshold
scale_ratio (float) – Ratio of largest to smallest parameter magnitude
- Return type:
Examples
>>> p0 = np.array([1e-3, 1e3, 1.0]) >>> has_mismatch, ratio = detect_parameter_scale_mismatch(p0) >>> print(f"Mismatch: {has_mismatch}, Ratio: {ratio:.2e}") Mismatch: True, Ratio: 1.00e+06
- nlsq.stability.guard.estimate_condition_number(xdata)[source]¶
Estimate the condition number of the data matrix.
This checks if the independent variable data is well-conditioned for least squares fitting. High condition numbers indicate numerical instability.
- Parameters:
xdata (array_like) – Independent variable data (can be 1D or 2D)
- Returns:
condition_number – Estimated condition number. Values > 1e10 indicate potential problems.
- Return type:
Notes
For 1D data, constructs a Vandermonde-like matrix with [1, x, x^2]. For multidimensional data, computes the condition number directly.
- nlsq.stability.guard.solve_with_cholesky_fallback(A, b)[source]¶
Solve linear system Ax = b using Cholesky with eigenvalue fallback.
This function attempts Cholesky decomposition first (O(n³/3)) and falls back to eigenvalue decomposition (O(n³)) only if Cholesky fails. The fallback is detected via NaN check, making this function JAX JIT-compatible.
- Parameters:
A (jnp.ndarray) – Symmetric matrix (n × n). Should be positive definite for Cholesky to succeed.
b (jnp.ndarray) – Right-hand side vector (n,).
- Returns:
x (jnp.ndarray) – Solution vector (n,).
used_cholesky (jnp.ndarray) – Boolean mask indicating whether Cholesky was used (True) or the eigenvalue fallback was needed (False).
- Return type:
Notes
For positive definite matrices (e.g., J^T J + λI with λ > 0), Cholesky is approximately 3x faster than eigenvalue decomposition. The JAX-compatible fallback pattern uses NaN detection instead of Python try/except.
Examples
>>> import jax.numpy as jnp >>> A = jnp.array([[4.0, 2.0], [2.0, 3.0]]) # Positive definite >>> b = jnp.array([1.0, 2.0]) >>> x, used_cholesky = solve_with_cholesky_fallback(A, b) >>> used_cholesky True
- nlsq.stability.guard.stability_guard = NumericalStabilityGuard()¶
Comprehensive numerical stability monitoring and correction.
This class provides methods to detect and correct numerical issues that can arise during optimization, including: - NaN/Inf detection and correction - Ill-conditioning detection and regularization - Overflow/underflow protection - Safe mathematical operations
- nlsq.stability.guard.condition_threshold¶
Threshold for detecting ill-conditioned matrices
- Type:
See Also¶
nlsq.stability.svd_fallback- GPU/CPU fallback SVDnlsq.utils.diagnostics module - Optimization diagnostics and monitoring