4.3. Parameter Bounds¶
Bounds constrain parameters to physically meaningful or valid ranges.
4.3.1. Basic Syntax¶
Bounds are specified as two arrays: lower bounds and upper bounds.
from nlsq import fit
import numpy as np
# bounds = ([lower1, lower2, ...], [upper1, upper2, ...])
bounds = ([0, 0, -1], [10, 5, 1])
popt, pcov = fit(model, x, y, p0=[1, 0.5, 0], bounds=bounds)
For a 3-parameter model:
Parameter 1: 0 <= p1 <= 10
Parameter 2: 0 <= p2 <= 5
Parameter 3: -1 <= p3 <= 1
4.3.2. Unbounded Parameters¶
Use np.inf for unbounded parameters:
import numpy as np
# Only constrain first parameter (must be positive)
bounds = ([0, -np.inf, -np.inf], [np.inf, np.inf, np.inf])
# Only upper bound on second parameter
bounds = ([-np.inf, -np.inf, -np.inf], [np.inf, 100, np.inf])
4.3.3. Common Bound Patterns¶
Positive parameters:
# Amplitude, rate, offset must be positive
bounds = ([0, 0, 0], [np.inf, np.inf, np.inf])
Constrained angles:
# Phase angle in [-pi, pi]
bounds = ([-np.pi], [np.pi])
Relative constraints:
# center1 < center2 (enforce ordering)
# Use separate fits or reparameterize the model
Physical limits:
# Concentration (0-100%), temperature > 0
bounds = ([0, 0], [100, np.inf])
4.3.4. When to Use Bounds¶
Use bounds when:
Parameters have physical constraints (e.g., positive amplitude)
Using
workflow='auto_global'(required for global search)Preventing the optimizer from exploring invalid regions
Model is undefined outside certain ranges
Don’t use bounds when:
Parameters can take any value
Bounds are unnecessarily tight (may prevent convergence)
Using bounds to “fix” a bad fit (fix the model instead)
4.3.5. Bounds and Global Optimization¶
For workflow='auto_global', bounds define the search space:
# Global search requires bounds
popt, pcov = fit(
model, x, y, p0=[1, 0.5], workflow="auto_global", bounds=([0, 0], [10, 5])
)
Wider bounds = larger search space = slower but more thorough.
4.3.6. Effect on Results¶
Bounds affect optimization behavior:
Parameters at bounds: May indicate bounds too tight
Large covariance: May indicate constraint effects
Slow convergence: May indicate bounds issues
# Check if parameters are at bounds
lower, upper = bounds
at_lower = np.isclose(popt, lower, rtol=1e-3)
at_upper = np.isclose(popt, upper, rtol=1e-3)
if any(at_lower) or any(at_upper):
print("Warning: Some parameters at bounds")
4.3.7. Complete Example¶
import numpy as np
import jax.numpy as jnp
from nlsq import fit
# Model: Gaussian peak
def gaussian(x, amplitude, center, width, offset):
return amplitude * jnp.exp(-0.5 * ((x - center) / width) ** 2) + offset
# Generate data
np.random.seed(42)
x = np.linspace(0, 10, 100)
y_true = 3.0 * np.exp(-0.5 * ((x - 5) / 1.2) ** 2) + 0.5
y = y_true + 0.2 * np.random.randn(len(x))
# Physical constraints:
# - amplitude > 0
# - center within data range
# - width > 0
# - offset >= 0
bounds = ([0, 0, 0.1, 0], [10, 10, 5, 5]) # Lower bounds # Upper bounds
# Initial guess
p0 = [2.5, 5, 1, 0.5]
# Fit with bounds
popt, pcov = fit(gaussian, x, y, p0=p0, bounds=bounds)
# Results
names = ["amplitude", "center", "width", "offset"]
for name, val, lo, hi in zip(names, popt, bounds[0], bounds[1]):
at_bound = (
"AT LOWER" if np.isclose(val, lo) else "AT UPPER" if np.isclose(val, hi) else ""
)
print(f"{name:10s}: {val:6.3f} [{lo}, {hi}] {at_bound}")
4.3.8. Troubleshooting¶
Parameter stuck at bound:
Bounds may be too tight
Initial guess may be at bound
Model may not fit the data
Slow convergence with bounds:
Start with wider bounds
Use better initial guess
Check if model is appropriate
Fit fails with bounds:
Ensure bounds are valid (lower < upper)
Ensure p0 is within bounds
Try without bounds first to verify model
4.3.9. Next Steps¶
Large Datasets - Handle millions of points
workflow=”auto_global” - Global Optimization - Global optimization