Orchestration Components¶
Added in version 0.6.4.
The orchestration module provides decomposed components for curve fitting, enabling modular testing, customization, and gradual migration via feature flags.
Overview¶
The monolithic CurveFit class has been decomposed into four focused components:
Component |
Responsibility |
|---|---|
Input validation, array conversion, NaN handling |
|
Method selection, bounds preparation, initial guess |
|
SVD-based covariance, sigma transformation |
|
Memory analysis, streaming strategy selection |
DataPreprocessor¶
- class nlsq.core.orchestration.DataPreprocessor[source]¶
Bases:
objectPreprocessor for curve fitting input data.
Handles: 1. Input validation (type checking, finiteness) 2. Array conversion (numpy/list to JAX) 3. Length consistency checking 4. Data masking for invalid points 5. NaN/Inf handling via nan_policy
Example
>>> preprocessor = DataPreprocessor() >>> data = preprocessor.preprocess( ... f=my_model, ... xdata=x_values, ... ydata=y_values, ... sigma=uncertainties, ... check_finite=True, ... ) >>> print(f"Valid points: {data.n_points}")
- preprocess(f, xdata, ydata, *, sigma=None, absolute_sigma=False, check_finite=True, nan_policy='raise', stability_check=False)[source]¶
Validate and preprocess input data for curve fitting.
- Parameters:
f (Callable[..., ArrayLike]) – Model function to fit (used for parameter count detection)
xdata (ArrayLike) – Independent variable data
ydata (ArrayLike) – Dependent variable data (observations)
sigma (ArrayLike | None) – Uncertainty/weights for observations
absolute_sigma (bool) – If True, sigma is absolute; else relative
check_finite (bool) – If True, raise on NaN/Inf values
nan_policy (str) – How to handle NaN: ‘raise’, ‘omit’, or ‘propagate’
stability_check (bool) – If True, run additional stability checks
- Returns:
PreprocessedData with validated, converted arrays
- Raises:
ValueError – If inputs are invalid (wrong shape, non-finite, etc.)
TypeError – If inputs have wrong types
- Return type:
- validate_sigma(sigma, ydata_shape)[source]¶
Validate and convert sigma to appropriate format.
Public interface matching DataPreprocessorProtocol.
- Parameters:
- Returns:
Validated numpy array or None
- Raises:
ValueError – If sigma shape is incompatible with ydata
- Return type:
np.ndarray | None
Example:
from nlsq.core.orchestration import DataPreprocessor
preprocessor = DataPreprocessor()
data = preprocessor.preprocess(
f=my_model,
xdata=raw_x,
ydata=raw_y,
sigma=uncertainties,
check_finite=True,
nan_policy="omit",
)
print(f"Valid points: {data.n_points}")
print(f"NaNs removed: {data.has_nans_removed}")
OptimizationSelector¶
- class nlsq.core.orchestration.OptimizationSelector[source]¶
Bases:
objectSelector for optimization method and configuration.
Handles: 1. Parameter count detection from function signature 2. Method selection based on bounds and problem type 3. Bounds validation and preparation 4. Initial guess generation if not provided 5. Solver configuration validation
Example
>>> selector = OptimizationSelector() >>> config = selector.select( ... f=my_model, ... xdata=x_values, ... ydata=y_values, ... bounds=([0, 0], [10, 10]), ... ) >>> print(f"Method: {config.method}, Params: {config.n_params}")
- select(f, xdata, ydata, *, p0=None, bounds=None, method=None, jac=None, tr_solver=None, x_scale=1.0, ftol=1e-08, xtol=1e-08, gtol=1e-08, max_nfev=None)[source]¶
Select optimization method and prepare configuration.
- Parameters:
f (Callable[..., ArrayLike]) – Model function to fit
xdata (ArrayLike) – Independent variable data
ydata (ArrayLike) – Dependent variable data
p0 (ArrayLike | None) – Initial parameter guess (auto-detected if None)
bounds (tuple[ArrayLike, ArrayLike] | None) – Parameter bounds as (lower, upper)
method (str | None) – Optimization method (‘trf’, ‘lm’, ‘dogbox’, or None for auto)
jac (str | Callable | None) – Jacobian computation method
tr_solver (str | None) – Trust region solver (‘exact’, ‘lsmr’, or None for auto)
ftol (float) – Function tolerance
xtol (float) – Parameter tolerance
gtol (float) – Gradient tolerance
max_nfev (int | None) – Maximum function evaluations (auto if None)
- Returns:
OptimizationConfig with all settings resolved
- Raises:
ValueError – If configuration is invalid
- Return type:
- detect_parameter_count(f, xdata)[source]¶
Detect number of parameters from function signature.
Uses inspection of function signature to determine parameter count.
- Parameters:
f (Callable[..., ArrayLike]) – Model function to analyze
xdata (ArrayLike) – Sample data (not used currently, for future probing)
- Returns:
Number of parameters (excluding x)
- Raises:
ValueError – If parameter count cannot be determined
- Return type:
Example:
from nlsq.core.orchestration import OptimizationSelector
selector = OptimizationSelector()
config = selector.select(
f=my_model,
xdata=data.xdata,
ydata=data.ydata,
p0=None, # Auto-detect
bounds=([0, 0], [10, 10]),
method=None, # Auto-select
)
print(f"Method: {config.method}")
print(f"Parameters: {config.n_params}")
CovarianceComputer¶
- class nlsq.core.orchestration.CovarianceComputer[source]¶
Bases:
objectComputer for parameter covariance from optimization results.
Handles: 1. Jacobian-based covariance via SVD 2. Sigma transformation (1D and 2D) 3. Absolute vs relative sigma handling 4. Singularity detection and handling
Example
>>> computer = CovarianceComputer() >>> result = computer.compute( ... result=optimize_result, ... n_data=100, ... sigma=uncertainties, ... absolute_sigma=True, ... ) >>> print(f"Parameter errors: {result.perr}")
- compute(result, n_data, *, sigma=None, absolute_sigma=False, full_output=False)[source]¶
Compute parameter covariance from optimization result.
Uses the Jacobian at the solution to compute covariance via: pcov = (J^T @ J)^(-1) * s_sq
where s_sq is the residual variance.
- Parameters:
result (OptimizeResult) – OptimizeResult from LeastSquares
n_data (int) – Number of data points
sigma (jax.Array | None) – Observation uncertainties/weights
absolute_sigma (bool) – If True, sigma is absolute uncertainty
full_output (bool) – If True, include additional diagnostics
- Returns:
CovarianceResult with covariance matrix and metadata
- Raises:
ValueError – If Jacobian is unavailable or invalid
- Return type:
- create_sigma_transform(sigma, n_data)[source]¶
Create sigma transformation function.
Handles both 1D (diagonal) and 2D (full covariance) sigma.
- compute_condition_number(jacobian)[source]¶
Compute condition number of Jacobian.
Uses singular values: cond = max(s) / min(s)
- setup_sigma_transform(sigma, ydata, data_mask, len_diff, m)[source]¶
Setup sigma transformation for weighted least squares.
This is the legacy interface matching CurveFit._setup_sigma_transform.
- Parameters:
- Returns:
Transformation array for sigma or None
- Raises:
ValueError – If sigma has incorrect shape or is not positive definite
- Return type:
jax.Array | None
Example:
from nlsq.core.orchestration import CovarianceComputer
computer = CovarianceComputer()
cov_result = computer.compute(
result=optimize_result,
n_data=len(ydata),
sigma=uncertainties,
absolute_sigma=True,
)
print(f"Covariance:\n{cov_result.pcov}")
print(f"Errors: {cov_result.perr}")
StreamingCoordinator¶
- class nlsq.core.orchestration.StreamingCoordinator(safety_factor=0.75)[source]¶
Bases:
objectCoordinator for streaming strategy selection.
Handles: 1. Memory estimation for dataset + Jacobian 2. Available memory detection 3. Strategy selection based on memory pressure 4. Configuration of chunked/hybrid strategies
Example
>>> coordinator = StreamingCoordinator() >>> decision = coordinator.decide( ... xdata=x_array, ... ydata=y_array, ... n_params=5, ... ) >>> if decision.strategy == "hybrid": ... config = decision.hybrid_config ... # Use hybrid streaming optimizer
- __init__(safety_factor=0.75)[source]¶
Initialize StreamingCoordinator.
- Parameters:
safety_factor (float) – Memory safety factor (0.75 means use 75% of available)
- decide(xdata, ydata, n_params, *, workflow='auto', memory_limit_mb=None, force_streaming=False)[source]¶
Decide on streaming strategy for the dataset.
Analyzes memory requirements and available resources to select the optimal execution strategy.
- Parameters:
xdata (jax.Array) – Independent variable data
ydata (jax.Array) – Dependent variable data
n_params (int) – Number of parameters
workflow (str) – Workflow hint (‘auto’, ‘streaming’, ‘hybrid’, ‘normal’)
memory_limit_mb (float | None) – Override for memory limit detection
force_streaming (bool) – If True, always use streaming
- Returns:
StreamingDecision with strategy and configuration
- Raises:
MemoryError – If dataset too large even for streaming
- Return type:
- estimate_memory(n_data, n_params, dtype_bytes=8)[source]¶
Estimate memory requirement in MB.
Accounts for: - Data arrays (x, y, residuals) - Jacobian matrix (n_data x n_params) - Working arrays for optimization - JAX compilation overhead
- get_available_memory()[source]¶
Get available system memory in MB.
Cached once per coordinator lifetime (one streaming decision per fit).
- Returns:
Available memory in MB
- Return type:
Example:
from nlsq.core.orchestration import StreamingCoordinator
coordinator = StreamingCoordinator()
decision = coordinator.decide(
xdata=large_x,
ydata=large_y,
n_params=5,
workflow="auto",
)
print(f"Strategy: {decision.strategy}")
print(f"Reason: {decision.reason}")
print(f"Memory pressure: {decision.memory_pressure:.1%}")
Data Classes¶
PreprocessedData¶
- class nlsq.interfaces.orchestration_protocol.PreprocessedData(xdata, ydata, sigma, mask, n_points, is_padded, original_length, has_nans_removed, has_infs_removed)[source]¶
Bases:
objectResult of data preprocessing.
All arrays are validated and converted to JAX arrays. Padding may be applied for JAX compilation efficiency.
- __init__(xdata, ydata, sigma, mask, n_points, is_padded, original_length, has_nans_removed, has_infs_removed)¶
OptimizationConfig¶
- class nlsq.interfaces.orchestration_protocol.OptimizationConfig(method, tr_solver, n_params, p0, bounds, max_nfev, ftol, xtol, gtol, jac, x_scale)[source]¶
Bases:
objectConfiguration for optimization execution.
Contains all settings needed by LeastSquares optimizer.
- method¶
Optimization algorithm (‘trf’, ‘lm’, ‘dogbox’)
- Type:
Literal[‘trf’, ‘lm’, ‘dogbox’]
- tr_solver¶
Trust region subproblem solver (‘exact’, ‘lsmr’, None)
- Type:
Literal[‘exact’, ‘lsmr’] | None
- method: Literal['trf', 'lm', 'dogbox']¶
- __init__(method, tr_solver, n_params, p0, bounds, max_nfev, ftol, xtol, gtol, jac, x_scale)¶
CovarianceResult¶
- class nlsq.interfaces.orchestration_protocol.CovarianceResult(pcov, perr, method, condition_number, is_singular, sigma_used, absolute_sigma)[source]¶
Bases:
objectResult of covariance matrix computation.
- method¶
Computation method used (‘svd’, ‘cholesky’, ‘qr’)
- Type:
Literal[‘svd’, ‘cholesky’, ‘qr’]
- method: Literal['svd', 'cholesky', 'qr']¶
- __init__(pcov, perr, method, condition_number, is_singular, sigma_used, absolute_sigma)¶
StreamingDecision¶
- class nlsq.interfaces.orchestration_protocol.StreamingDecision(strategy, reason, estimated_memory_mb, available_memory_mb, memory_pressure, chunk_size, n_chunks, hybrid_config)[source]¶
Bases:
objectDecision about streaming execution strategy.
- strategy¶
Execution strategy to use - ‘direct’: Normal non-streaming fit - ‘chunked’: Simple chunked processing - ‘hybrid’: AdaptiveHybridStreamingOptimizer - ‘auto_memory’: Memory-aware automatic selection
- Type:
Literal[‘direct’, ‘chunked’, ‘hybrid’, ‘auto_memory’]
- hybrid_config¶
Configuration for hybrid strategy
- Type:
HybridStreamingConfig | None
- strategy: Literal['direct', 'chunked', 'hybrid', 'auto_memory']¶
- hybrid_config: HybridStreamingConfig | None¶
- __init__(strategy, reason, estimated_memory_mb, available_memory_mb, memory_pressure, chunk_size, n_chunks, hybrid_config)¶
Feature Flags¶
Control implementation selection via environment variables for gradual rollout:
# Use old implementation (for rollback)
export NLSQ_PREPROCESSOR_IMPL=old
# Use new implementation (default after rollout)
export NLSQ_PREPROCESSOR_IMPL=new
# Global rollout percentage (0-100)
export NLSQ_REFACTOR_ROLLOUT_PERCENT=50
- class nlsq.core.feature_flags.FeatureFlags(preprocessor_impl='auto', selector_impl='auto', covariance_impl='auto', streaming_impl='auto', rollout_percent=0, _session_id=<factory>)[source]¶
Bases:
objectFeature flags for CurveFit component extraction.
Controls which implementation (old or new) to use for each extracted component. Supports gradual rollout via percentage-based selection.
- preprocessor_impl¶
Implementation choice for DataPreprocessor
- Type:
Literal[‘old’, ‘new’, ‘auto’]
- selector_impl¶
Implementation choice for OptimizationSelector
- Type:
Literal[‘old’, ‘new’, ‘auto’]
- covariance_impl¶
Implementation choice for CovarianceComputer
- Type:
Literal[‘old’, ‘new’, ‘auto’]
- streaming_impl¶
Implementation choice for StreamingCoordinator
- Type:
Literal[‘old’, ‘new’, ‘auto’]
- rollout_percent¶
Percentage (0-100) of requests using new implementation when impl is ‘auto’
- Type:
- classmethod from_env(session_id=None)[source]¶
Create FeatureFlags from environment variables.
- Parameters:
session_id (str | None) – Optional session ID for deterministic rollout. If not provided, a random ID is generated.
- Returns:
FeatureFlags instance with values from environment.
- Raises:
ValueError – If environment variable has invalid value.
- Return type:
- get_impl(component)[source]¶
Get implementation choice for a component.
- Parameters:
component (str) – Component name (‘preprocessor’, ‘selector’, ‘covariance’, ‘streaming’)
- Returns:
Implementation choice for the component
- Raises:
ValueError – If component name is unknown
- Return type:
Literal[‘old’, ‘new’, ‘auto’]
- should_use_new(component)[source]¶
Determine if new implementation should be used.
For ‘old’ or ‘new’ choices, returns the explicit choice. For ‘auto’, uses rollout_percent with deterministic selection based on session_id and component name.
- Parameters:
component (str) – Component name (‘preprocessor’, ‘selector’, ‘covariance’, ‘streaming’)
- Returns:
True if new implementation should be used, False for old
- Raises:
ValueError – If component name is unknown
- Return type:
- with_override(*, preprocessor_impl=None, selector_impl=None, covariance_impl=None, streaming_impl=None, rollout_percent=None)[source]¶
Create new FeatureFlags with overridden values.
- Parameters:
preprocessor_impl (Literal['old', 'new', 'auto'] | None) – Override for preprocessor implementation
selector_impl (Literal['old', 'new', 'auto'] | None) – Override for selector implementation
covariance_impl (Literal['old', 'new', 'auto'] | None) – Override for covariance implementation
streaming_impl (Literal['old', 'new', 'auto'] | None) – Override for streaming implementation
rollout_percent (int | None) – Override for rollout percentage
- Returns:
New FeatureFlags instance with overridden values
- Return type:
- __init__(preprocessor_impl='auto', selector_impl='auto', covariance_impl='auto', streaming_impl='auto', rollout_percent=0, _session_id=<factory>)¶
See Also¶
Core API Reference - Main curve fitting API
Large Dataset API Reference - Large dataset handling
Architecture Overview - Architecture overview