4.3. OptimizationSelector¶
Added in version 0.6.4.
The OptimizationSelector handles parameter detection, bounds processing,
initial guess generation, and method selection.
4.3.1. Basic Usage¶
from nlsq.core.orchestration import OptimizationSelector
import jax.numpy as jnp
def model(x, a, b, c):
return a * jnp.exp(-b * x) + c
selector = OptimizationSelector()
config = selector.select(
f=model, xdata=x, ydata=y, p0=[1.0, 0.5, 0.0], bounds=None, method="trf"
)
# Access configuration
n_params = config.n_params
initial_guess = config.p0
method = config.method
4.3.2. OptimizationConfig¶
The select() method returns an OptimizationConfig object:
@dataclass
class OptimizationConfig:
n_params: int # Number of parameters
p0: np.ndarray # Initial parameter guess
bounds: tuple | None # (lower, upper) bounds
method: str # Optimization method
jac: str | Callable # Jacobian computation
tr_solver: str # Trust region solver
ftol: float # Function tolerance
xtol: float # Parameter tolerance
gtol: float # Gradient tolerance
4.3.3. Parameter Detection¶
Automatically detect number of parameters from model signature:
def model(x, a, b, c): # 3 parameters: a, b, c
return a * jnp.exp(-b * x) + c
n_params = selector.detect_parameter_count(model, xdata)
print(f"Detected {n_params} parameters") # 3
This uses Python’s inspect module to analyze the function signature.
4.3.4. Initial Guess Generation¶
If p0 is not provided:
config = selector.select(
f=model, xdata=x, ydata=y, p0=None # Auto-generate initial guess
)
print(f"Generated p0: {config.p0}")
The selector uses heuristics based on data range and model type.
4.3.5. Bounds Processing¶
Process user-provided bounds:
# Tuple format
bounds = ([0, 0, -1], [10, 5, 1])
config = selector.select(f=model, xdata=x, ydata=y, p0=[1, 0.5, 0], bounds=bounds)
# Unbounded parameters
bounds = ([0, -np.inf, -np.inf], [np.inf, np.inf, np.inf])
4.3.6. Method Selection¶
Select optimization method:
# Trust Region Reflective (default)
config = selector.select(..., method="trf")
# Dogbox (for small problems)
config = selector.select(..., method="dogbox")
# Levenberg-Marquardt (unbounded only)
config = selector.select(..., method="lm")
4.3.7. Solver Selection¶
Select trust region solver:
# Exact (SVD-based, for small problems)
config = selector.select(..., tr_solver="exact")
# LSMR (iterative, for large problems)
config = selector.select(..., tr_solver="lsmr")
4.3.8. Complete Example¶
import numpy as np
import jax.numpy as jnp
from nlsq.core.orchestration import OptimizationSelector
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 = 3.0 * np.exp(-0.5 * ((x - 5) / 1.2) ** 2) + 0.5
# Configure optimization
selector = OptimizationSelector()
config = selector.select(
f=gaussian,
xdata=x,
ydata=y,
p0=[2.5, 5.0, 1.0, 0.5],
bounds=([0, 0, 0.1, 0], [10, 10, 5, 2]),
method="trf",
ftol=1e-10,
xtol=1e-10,
gtol=1e-10,
)
print(f"Parameters: {config.n_params}")
print(f"Initial guess: {config.p0}")
print(f"Method: {config.method}")
print(f"TR solver: {config.tr_solver}")
print(f"Tolerances: ftol={config.ftol}, xtol={config.xtol}")
4.3.9. Next Steps¶
CovarianceComputer - Covariance estimation
StreamingCoordinator - Memory strategy