11"""
22Computation descriptor abstractions.
33
4- Provides ``ComputationOption``, ``FitterDescriptor``, and ``EvaluatorDescriptor``
5- for declaring metadata about fitters and evaluators.
4+ Provides ``CharacteristicOption``, ``ComputationOption``,
5+ ``FitterDescriptor``, and ``EvaluatorDescriptor`` for declaring metadata
6+ about fitters and evaluators.
7+
8+ Option taxonomy
9+ ---------------
10+ ``CharacteristicOption``
11+ Describes a parameter that is *intrinsic to the characteristic itself*
12+ (e.g. ``eps`` or ``x0`` for PPF). These options are shared between the
13+ fitter (pre-computation / caching path) and the evaluator (direct-query
14+ path). Because they affect the *meaning* of the result they must be
15+ encoded into the cache key.
16+
17+ ``ComputationOption``
18+ Describes a parameter that controls the *numerical algorithm* used to
19+ compute the characteristic (e.g. ``max_iter``, ``x_tol``). These are
20+ specific to a particular fitter implementation and do **not** affect the
21+ evaluator.
22+
23+ Passing options — ``TypedDict`` + ``Unpack`` pattern
24+ -----------------------------------------------------
25+ Concrete fitters and evaluators declare their accepted keyword arguments via
26+ ``TypedDict`` subclasses and annotate their signatures with
27+ ``**kwargs: Unpack[MyOptionsDict]``. This gives static type-checkers full
28+ visibility while keeping the runtime interface simple ``**kwargs``.
29+
30+ Example::
31+
32+ from typing import TypedDict
33+ from typing_extensions import Unpack
34+
35+ class PpfCharacteristicOptions(TypedDict, total=False):
36+ eps: float
37+ x0: float
38+
39+ class PpfComputationOptions(TypedDict, total=False):
40+ max_iter: int
41+ x_tol: float
42+
43+ def fit_cdf_to_ppf(
44+ distribution: Distribution,
45+ /,
46+ **kwargs: Unpack[PpfComputationOptions],
47+ ) -> FittedComputationMethod: ...
648"""
749
850from __future__ import annotations
2971
3072
3173@dataclass (frozen = True , slots = True )
32- class ComputationOption :
74+ class _BaseOption :
3375 """
34- Structured descriptor for a single computation option.
76+ Common base for option descriptors .
3577
3678 Attributes
3779 ----------
@@ -88,6 +130,56 @@ def resolve(self, kwargs: dict[str, Any]) -> Any:
88130 return value
89131
90132
133+ @dataclass (frozen = True , slots = True )
134+ class CharacteristicOption (_BaseOption ):
135+ """
136+ Option that is *intrinsic to the characteristic* being computed.
137+
138+ Characteristic options are shared between the fitter (pre-computation /
139+ caching path) and the evaluator (direct-query path). Because they affect
140+ the *meaning* of the result they must be encoded into the cache key.
141+
142+ Examples: ``eps`` (tail threshold for PPF), ``x0`` (starting point for
143+ bound search), ``excess`` (excess kurtosis flag).
144+
145+ These options are declared in ``FitterDescriptor.characteristic_options``
146+ and ``EvaluatorDescriptor.characteristic_options``.
147+ """
148+
149+
150+ @dataclass (frozen = True , slots = True )
151+ class ComputationOption (_BaseOption ):
152+ """
153+ Option that controls the *numerical algorithm* used to compute a
154+ characteristic.
155+
156+ Computation options are specific to a particular fitter implementation
157+ and do **not** affect the evaluator. They influence only the speed /
158+ accuracy trade-off of the fitting step, not the semantics of the result.
159+
160+ Examples: ``max_iter`` (bisection iterations), ``x_tol`` (bracket
161+ tolerance), ``limit`` (quad subdivisions), ``n_q_grid`` (PPF grid size).
162+
163+ These options are declared in ``FitterDescriptor.computation_options``.
164+ """
165+
166+
167+ def _resolve_options (
168+ options : tuple [_BaseOption , ...],
169+ kwargs : dict [str , Any ],
170+ ) -> dict [str , Any ]:
171+ """Resolve a tuple of option descriptors from *kwargs* (mutates *kwargs*)."""
172+ return {opt .name : opt .resolve (kwargs ) for opt in options }
173+
174+
175+ def _option_names (options : tuple [_BaseOption , ...]) -> tuple [str , ...]:
176+ return tuple (opt .name for opt in options )
177+
178+
179+ def _option_defaults (options : tuple [_BaseOption , ...]) -> dict [str , Any ]:
180+ return {opt .name : opt .default for opt in options }
181+
182+
91183@dataclass (frozen = True , slots = True )
92184class FitterDescriptor :
93185 """
@@ -106,25 +198,39 @@ class FitterDescriptor:
106198 Characteristics consumed by this fitter (typically length 1).
107199 fitter : FitterFunc
108200 The actual fitting callable.
109- options : tuple[ComputationOption, ...]
110- Structured option schema.
201+ characteristic_options : tuple[CharacteristicOption, ...]
202+ Options intrinsic to the characteristic (shared with evaluators,
203+ encoded into the cache key).
204+ computation_options : tuple[ComputationOption, ...]
205+ Options controlling the numerical algorithm (fitter-specific).
111206 constraint_tags : frozenset[str]
112207 Constraint tags used for matching (e.g. ``{"continuous", "univariate"}``).
113208 description : str
114209 Human-readable summary of what the fitter does.
210+
211+ Notes
212+ -----
213+ The combined ``options`` property returns all options (characteristic
214+ first, then computation) for backwards-compatible resolution.
115215 """
116216
117217 name : str
118218 target : GenericCharacteristicName
119219 sources : Sequence [GenericCharacteristicName ]
120220 fitter : FitterFunc
121- options : tuple [ComputationOption , ...] = ()
221+ characteristic_options : tuple [CharacteristicOption , ...] = ()
222+ computation_options : tuple [ComputationOption , ...] = ()
122223 constraint_tags : frozenset [str ] = field (default_factory = frozenset )
123224 description : str = ""
124225
226+ @property
227+ def options (self ) -> tuple [_BaseOption , ...]:
228+ """All options (characteristic first, then computation)."""
229+ return (* self .characteristic_options , * self .computation_options )
230+
125231 def to_computation_method (self ) -> FitterMethod :
126232 """
127- Build a :class: `FitterMethod` (computation method) from this descriptor.
233+ Build a `FitterMethod` (computation method) from this descriptor.
128234
129235 Returns
130236 -------
@@ -138,9 +244,43 @@ def to_computation_method(self) -> FitterMethod:
138244 fitter = self .fitter ,
139245 )
140246
247+ def resolve_characteristic_options (self , kwargs : dict [str , Any ]) -> dict [str , Any ]:
248+ """
249+ Resolve only the *characteristic* options from *kwargs*.
250+
251+ Consumes recognised keys from *kwargs* and returns a dict of
252+ ``{option_name: resolved_value}``. Unrecognised keys are left
253+ in *kwargs* untouched.
254+
255+ Parameters
256+ ----------
257+ kwargs : dict[str, Any]
258+ Mutable keyword-argument dict from the caller.
259+
260+ Returns
261+ -------
262+ dict[str, Any]
263+ """
264+ return _resolve_options (self .characteristic_options , kwargs )
265+
266+ def resolve_computation_options (self , kwargs : dict [str , Any ]) -> dict [str , Any ]:
267+ """
268+ Resolve only the *computation* options from *kwargs*.
269+
270+ Parameters
271+ ----------
272+ kwargs : dict[str, Any]
273+ Mutable keyword-argument dict from the caller.
274+
275+ Returns
276+ -------
277+ dict[str, Any]
278+ """
279+ return _resolve_options (self .computation_options , kwargs )
280+
141281 def resolve_options (self , kwargs : dict [str , Any ]) -> dict [str , Any ]:
142282 """
143- Resolve all declared options from *kwargs*.
283+ Resolve * all* declared options (characteristic + computation) from *kwargs*.
144284
145285 Consumes recognised keys from *kwargs* and returns a dict of
146286 ``{option_name: resolved_value}``. Unrecognised keys are left
@@ -156,15 +296,23 @@ def resolve_options(self, kwargs: dict[str, Any]) -> dict[str, Any]:
156296 dict[str, Any]
157297 Mapping from option name to resolved (validated, typed) value.
158298 """
159- return { opt . name : opt . resolve ( kwargs ) for opt in self .options }
299+ return _resolve_options ( self .options , kwargs )
160300
161301 def option_names (self ) -> tuple [str , ...]:
162- """Return the names of all declared options."""
163- return tuple ( opt . name for opt in self .options )
302+ """Return the names of all declared options (characteristic + computation) ."""
303+ return _option_names ( self .options )
164304
165305 def option_defaults (self ) -> dict [str , Any ]:
166306 """Return ``{name: default}`` for every declared option."""
167- return {opt .name : opt .default for opt in self .options }
307+ return _option_defaults (self .options )
308+
309+ def characteristic_option_names (self ) -> tuple [str , ...]:
310+ """Return the names of characteristic options only."""
311+ return _option_names (self .characteristic_options )
312+
313+ def computation_option_names (self ) -> tuple [str , ...]:
314+ """Return the names of computation options only."""
315+ return _option_names (self .computation_options )
168316
169317
170318@dataclass (frozen = True , slots = True )
@@ -186,8 +334,14 @@ class EvaluatorDescriptor:
186334 Characteristics consumed by this evaluator (typically length 1).
187335 evaluator : EvaluatorFunc
188336 The actual evaluator callable.
189- options : tuple[ComputationOption, ...]
190- Structured option schema.
337+ characteristic_options : tuple[CharacteristicOption, ...]
338+ Options intrinsic to the characteristic (shared with fitters).
339+ These affect the *meaning* of the result.
340+ computation_options : tuple[ComputationOption, ...]
341+ Options controlling the numerical algorithm used **on every call**.
342+ Unlike fitter computation options (used once at fit-time), evaluator
343+ computation options are applied on each invocation. Examples:
344+ integration tolerance, finite-difference step, iteration limit.
191345 constraint_tags : frozenset[str]
192346 Constraint tags used for matching.
193347 description : str
@@ -198,13 +352,19 @@ class EvaluatorDescriptor:
198352 target : GenericCharacteristicName
199353 sources : Sequence [GenericCharacteristicName ]
200354 evaluator : EvaluatorFunc
201- options : tuple [ComputationOption , ...] = ()
355+ characteristic_options : tuple [CharacteristicOption , ...] = ()
356+ computation_options : tuple [ComputationOption , ...] = ()
202357 constraint_tags : frozenset [str ] = field (default_factory = frozenset )
203358 description : str = ""
204359
360+ @property
361+ def options (self ) -> tuple [_BaseOption , ...]:
362+ """All options (characteristic first, then computation)."""
363+ return (* self .characteristic_options , * self .computation_options )
364+
205365 def to_computation_method (self ) -> EvaluatorMethod :
206366 """
207- Build an :class: `EvaluatorMethod` (computation method) from this descriptor.
367+ Build an `EvaluatorMethod` (computation method) from this descriptor.
208368
209369 Returns
210370 -------
@@ -218,13 +378,42 @@ def to_computation_method(self) -> EvaluatorMethod:
218378 evaluator = self .evaluator ,
219379 )
220380
221- def resolve_options (self , kwargs : dict [str , Any ]) -> dict [str , Any ]:
381+ def resolve_characteristic_options (self , kwargs : dict [str , Any ]) -> dict [str , Any ]:
222382 """
223- Resolve all declared options from *kwargs*.
383+ Resolve only the *characteristic* options from *kwargs*.
224384
225- Consumes recognised keys from *kwargs* and returns a dict of
226- ``{option_name: resolved_value}``. Unrecognised keys are left
227- in *kwargs* untouched.
385+ Parameters
386+ ----------
387+ kwargs : dict[str, Any]
388+ Mutable keyword-argument dict from the caller.
389+
390+ Returns
391+ -------
392+ dict[str, Any]
393+ """
394+ return _resolve_options (self .characteristic_options , kwargs )
395+
396+ def resolve_computation_options (self , kwargs : dict [str , Any ]) -> dict [str , Any ]:
397+ """
398+ Resolve only the *computation* options from *kwargs*.
399+
400+ For evaluators these are applied on **every call** (not just at
401+ fit-time as for fitters).
402+
403+ Parameters
404+ ----------
405+ kwargs : dict[str, Any]
406+ Mutable keyword-argument dict from the caller.
407+
408+ Returns
409+ -------
410+ dict[str, Any]
411+ """
412+ return _resolve_options (self .computation_options , kwargs )
413+
414+ def resolve_options (self , kwargs : dict [str , Any ]) -> dict [str , Any ]:
415+ """
416+ Resolve *all* declared options (characteristic + computation) from *kwargs*.
228417
229418 Parameters
230419 ----------
@@ -236,18 +425,27 @@ def resolve_options(self, kwargs: dict[str, Any]) -> dict[str, Any]:
236425 dict[str, Any]
237426 Mapping from option name to resolved (validated, typed) value.
238427 """
239- return { opt . name : opt . resolve ( kwargs ) for opt in self .options }
428+ return _resolve_options ( self .options , kwargs )
240429
241430 def option_names (self ) -> tuple [str , ...]:
242- """Return the names of all declared options."""
243- return tuple ( opt . name for opt in self .options )
431+ """Return the names of all declared options (characteristic + computation) ."""
432+ return _option_names ( self .options )
244433
245434 def option_defaults (self ) -> dict [str , Any ]:
246435 """Return ``{name: default}`` for every declared option."""
247- return {opt .name : opt .default for opt in self .options }
436+ return _option_defaults (self .options )
437+
438+ def characteristic_option_names (self ) -> tuple [str , ...]:
439+ """Return the names of characteristic options only."""
440+ return _option_names (self .characteristic_options )
441+
442+ def computation_option_names (self ) -> tuple [str , ...]:
443+ """Return the names of computation options only."""
444+ return _option_names (self .computation_options )
248445
249446
250447__all__ = [
448+ "CharacteristicOption" ,
251449 "ComputationOption" ,
252450 "FitterDescriptor" ,
253451 "EvaluatorDescriptor" ,
0 commit comments