Source code for homodyne.optimization.nlsq.results
"""NLSQ optimization result classes.
This module extracts result dataclasses from nlsq_wrapper.py
to reduce file size and improve maintainability.
Extracted from nlsq_wrapper.py as part of technical debt remediation (Dec 2025).
"""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
import numpy as np
if TYPE_CHECKING:
from homodyne.optimization.nlsq.strategies.chunking import StratificationDiagnostics
[docs]
@dataclass
class FunctionEvaluationCounter:
"""Wraps a callable and counts invocations.
Useful for tracking the number of function evaluations during optimization.
"""
fn: Callable[..., Any]
count: int = 0
[docs]
def __call__(self, *args, **kwargs):
"""Call the wrapped function and increment count."""
self.count += 1
return self.fn(*args, **kwargs)
[docs]
@dataclass
class OptimizationResult:
"""Complete optimization result with fit quality metrics and diagnostics.
Attributes
----------
parameters : np.ndarray
Converged parameter values.
uncertainties : np.ndarray
Standard deviations from covariance matrix diagonal.
covariance : np.ndarray
Full parameter covariance matrix.
chi_squared : float
Sum of squared residuals.
reduced_chi_squared : float
chi_squared / (n_data - n_params).
convergence_status : str
'converged', 'max_iter', or 'failed'.
iterations : int
Number of optimization iterations.
execution_time : float
Wall-clock execution time in seconds.
device_info : dict[str, Any]
Device used for computation (CPU details).
recovery_actions : list[str]
List of error recovery actions taken.
quality_flag : str
'good', 'marginal', or 'poor'.
streaming_diagnostics : dict[str, Any] | None
Enhanced diagnostics for streaming optimization.
stratification_diagnostics : StratificationDiagnostics | None
Diagnostics for angle-stratified chunking.
nlsq_diagnostics : dict[str, Any] | None
Additional NLSQ-specific diagnostics.
"""
parameters: np.ndarray
uncertainties: np.ndarray
covariance: np.ndarray
chi_squared: float
reduced_chi_squared: float
convergence_status: str
iterations: int
execution_time: float
device_info: dict[str, Any]
recovery_actions: list[str] = field(default_factory=list)
quality_flag: str = "good"
streaming_diagnostics: dict[str, Any] | None = None
stratification_diagnostics: StratificationDiagnostics | None = None
nlsq_diagnostics: dict[str, Any] | None = None
sigma_is_default: bool = False
@property
def success(self) -> bool:
"""Return True if optimization converged (backward compatibility)."""
return self.convergence_status == "converged"
@property
def message(self) -> str:
"""Return descriptive message about optimization outcome."""
if self.convergence_status == "converged":
return f"Optimization converged successfully. chi2={self.chi_squared:.6f}"
elif self.convergence_status == "max_iter":
return "Optimization stopped: maximum iterations reached"
else:
return f"Optimization failed: {self.convergence_status}"
# =============================================================================
# T010: FallbackInfo dataclass for adapter-to-wrapper fallback tracking
# =============================================================================
[docs]
@dataclass
class FallbackInfo:
"""Tracks fallback from NLSQAdapter to NLSQWrapper.
Included in OptimizationResult.device_info when fallback occurs.
Attributes:
fallback_occurred: True if fallback was triggered
adapter_used: "NLSQAdapter" or "NLSQWrapper"
adapter_error: Error message if adapter failed (None if succeeded)
wrapper_error: Error message if wrapper also failed (None otherwise)
States:
* NLSQAdapter + fallback_occurred=False + adapter_error=None: Adapter succeeded
* NLSQWrapper + fallback_occurred=True + adapter_error="...": Fallback succeeded
* NLSQWrapper + fallback_occurred=True + adapter_error="..." + wrapper_error="...": Both failed
"""
fallback_occurred: bool
adapter_used: str # "NLSQAdapter" or "NLSQWrapper"
adapter_error: str | None = None
wrapper_error: str | None = None
[docs]
def to_dict(self) -> dict[str, Any]:
"""Convert to dict for inclusion in device_info."""
return {
"fallback_occurred": self.fallback_occurred,
"adapter_used": self.adapter_used,
"adapter_error": self.adapter_error,
"wrapper_error": self.wrapper_error,
}
[docs]
@dataclass
class UseSequentialOptimization:
"""Marker indicating sequential per-angle optimization should be used.
This is returned by _apply_stratification_if_needed when conditions require
sequential per-angle optimization as a fallback strategy.
Attributes
----------
data : Any
Original XPCS data object.
reason : str
Why sequential optimization is needed.
"""
data: Any
reason: str