2.1. CurveFit Class¶
The CurveFit class provides a reusable, stateful interface for curve fitting.
2.1.1. Why Use CurveFit?¶
JIT Reuse: Compilations cached between calls
Batch Processing: Fit multiple datasets efficiently
State Access: Access diagnostics and internals
Customization: Configure once, use many times
2.1.2. Basic Usage¶
from nlsq import CurveFit
import jax.numpy as jnp
# Create instance
fitter = CurveFit()
# Define model
def model(x, a, b, c):
return a * jnp.exp(-b * x) + c
# Fit multiple datasets
for x, y in datasets:
popt, pcov = fitter.curve_fit(model, x, y, p0=[2, 0.5, 0])
process_results(popt, pcov)
2.1.3. Constructor Options¶
fitter = CurveFit(
# Diagnostics
enable_diagnostics=True, # Collect convergence metrics
enable_stability=True, # Enable stability checks
# Caching
enable_caching=True, # JIT compilation caching
# Recovery
enable_recovery=True, # Auto-recovery from issues
)
2.1.4. curve_fit Method¶
Full method signature:
popt, pcov = fitter.curve_fit(
f, # Model function
xdata, # Independent variable
ydata, # Dependent variable
p0=None, # Initial guess
sigma=None, # Uncertainties
absolute_sigma=False, # Absolute sigma interpretation
check_finite=True, # Check for inf/NaN
bounds=(-np.inf, np.inf), # Parameter bounds
method="trf", # Optimization method
jac="2-point", # Jacobian computation
full_output=False, # Return full result
nan_policy="raise", # NaN handling
**kwargs # Additional options
)
2.1.5. Advanced Options¶
Multi-start optimization:
popt, pcov = fitter.curve_fit(
model,
x,
y,
p0=[...],
multistart=True,
n_starts=20,
bounds=bounds, # Required for multi-start
)
Stability checks:
popt, pcov = fitter.curve_fit(
model,
x,
y,
p0=[...],
stability="auto", # 'auto', 'strict', 'none'
fallback=True, # Enable fallback strategies
rescale_data=True, # Automatic data rescaling
)
Full output:
popt, pcov, info = fitter.curve_fit(model, x, y, p0=[...], full_output=True)
print(f"Iterations: {info.nfev}")
print(f"Final cost: {info.cost}")
print(f"Status: {info.message}")
2.1.6. Batch Processing Pattern¶
from nlsq import CurveFit
import jax.numpy as jnp
def model(x, a, b):
return a * jnp.exp(-b * x)
# Create once
fitter = CurveFit(enable_diagnostics=True)
# Process many datasets
results = []
for data_file in data_files:
x, y = load_data(data_file)
popt, pcov = fitter.curve_fit(model, x, y, p0=[2, 0.5])
results.append(
{"file": data_file, "params": popt, "errors": np.sqrt(np.diag(pcov))}
)
# First fit is slow (JIT), rest are fast
2.1.7. Accessing Diagnostics¶
fitter = CurveFit(enable_diagnostics=True)
popt, pcov = fitter.curve_fit(model, x, y, p0=[...])
# Access internal state (implementation-dependent)
# Note: API may change between versions
2.1.8. Complete Example¶
import numpy as np
import jax.numpy as jnp
from nlsq import CurveFit
# Model
def gaussian(x, amplitude, center, width, offset):
return amplitude * jnp.exp(-0.5 * ((x - center) / width) ** 2) + offset
# Create fitter
fitter = CurveFit(enable_diagnostics=True)
# Generate test datasets
np.random.seed(42)
datasets = []
for i in range(10):
x = np.linspace(0, 10, 100)
# Varying parameters
amp = 2 + i * 0.5
ctr = 3 + i * 0.3
y = amp * np.exp(-0.5 * ((x - ctr) / 1.0) ** 2) + 0.5
y += 0.2 * np.random.randn(len(x))
datasets.append((x, y))
# Fit all datasets
print("Fitting datasets...")
for i, (x, y) in enumerate(datasets):
popt, pcov = fitter.curve_fit(
gaussian, x, y, p0=[2, 5, 1, 0.5], bounds=([0, 0, 0.1, 0], [10, 10, 5, 2])
)
perr = np.sqrt(np.diag(pcov))
print(
f"Dataset {i}: amp={popt[0]:.2f}+/-{perr[0]:.2f}, "
f"ctr={popt[1]:.2f}+/-{perr[1]:.2f}"
)
2.1.9. When to Use CurveFit vs fit()¶
Use fit() when:
Single fit
Using workflow system
Simple use case
Use CurveFit when:
Multiple fits with same model
Need JIT reuse for speed
Accessing diagnostics
Custom configuration
2.1.10. Next Steps¶
LeastSquares Class - Lower-level optimizer control
Factories and Dependency Injection - Factory patterns