Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
LambdaDecay.py
Go to the documentation of this file.
1"""Python wrappers for lambda-backed custom decays and observables.
2
3The C++ binding exposes the low-level ``LambdaDecay`` callback context. This
4module keeps the public Python API wrapper-only: user callbacks receive
5``LambdaDecayContext`` and normal Python ids/enums instead of raw pybind objects.
6"""
7
8from __future__ import annotations
9
10from dataclasses import dataclass, field
11from typing import Callable, Sequence, Set
12
13from pyhyperiso.phyperiso.pyhyperiso import observable as _obs
14from pyhyperiso.core.Common.GeneralEnum import QCDOrder, ContributionType, DataType
15from pyhyperiso.core.Common.LhaID import LhaID
16from pyhyperiso.core.Common.ParamId import ParamId
17from pyhyperiso.core.Common.SymbolId import WGroupId, ObservableId
18from pyhyperiso.core.Common.Mapper import GroupMapper
19from pyhyperiso.core.PhysicalModel.WilsonInterface import CustomWilsonGroupConfig
20from pyhyperiso.core.Common.Configs import _cpp_group_id, _cpp_coef_id
21from pyhyperiso.core.Math.Scalar import Scalar
22
23
24def _enum_value(value):
25 """Return the bound C++ enum stored in the Python enum wrapper.
26
27 The public wrappers use Python ``Enum`` objects whose ``.value`` is the
28 pybind enum. If a caller already passes the bound enum, it is returned as-is.
29 """
30 return getattr(value, "value", value)
31
32
33def _to_observable_id(obs_id) -> ObservableId:
34 """Convert a C++/Python observable id to the Python wrapper id."""
35 if isinstance(obs_id, ObservableId):
36 return obs_id
37 return ObservableId(str(obs_id))
38
39
41 """Wrapper around the C++ ``LambdaDecay`` callback context.
42
43 User-defined observable callbacks receive this object. It hides the raw
44 pybind context and accepts the normal Python wrappers for Wilson ids,
45 parameters and enums.
46 """
47
48 def __init__(self, cpp_obj):
49 self._cpp_obj = cpp_obj
50
51 def get_M(self, group, coeff, order: QCDOrder, contribution: ContributionType) -> Scalar:
52 """Return a matching coefficient as ``Scalar``."""
53 return Scalar.from_cpp(
54 self._cpp_obj.get_M(
55 _cpp_group_id(group),
56 _cpp_coef_id(coeff),
57 _enum_value(order),
58 _enum_value(contribution),
59 )
60 )
61
62 def get_FM(self, group, coeff, order: QCDOrder, contribution: ContributionType) -> Scalar:
63 """Return a full matching coefficient as ``Scalar``."""
64 return Scalar.from_cpp(
65 self._cpp_obj.get_FM(
66 _cpp_group_id(group),
67 _cpp_coef_id(coeff),
68 _enum_value(order),
69 _enum_value(contribution),
70 )
71 )
72
73 def get_R(self, group, coeff, order: QCDOrder, contribution: ContributionType) -> Scalar:
74 """Return a running coefficient as ``Scalar``."""
75 return Scalar.from_cpp(
76 self._cpp_obj.get_R(
77 _cpp_group_id(group),
78 _cpp_coef_id(coeff),
79 _enum_value(order),
80 _enum_value(contribution),
81 )
82 )
83
84 def get_FR(self, group, coeff, order: QCDOrder, contribution: ContributionType) -> Scalar:
85 """Return a full running coefficient as ``Scalar``."""
86 return Scalar.from_cpp(
87 self._cpp_obj.get_FR(
88 _cpp_group_id(group),
89 _cpp_coef_id(coeff),
90 _enum_value(order),
91 _enum_value(contribution),
92 )
93 )
94
95 def get_sm_param(self, pid: ParamId, data_type: DataType = DataType.VALUE) -> Scalar:
96 """Return an SM parameter as ``Scalar``."""
97 return Scalar.from_cpp(self._cpp_obj.get_sm_param(pid.to_cpp(), _enum_value(data_type)))
98
99 def get_flavor_param(self, pid: ParamId, data_type: DataType = DataType.VALUE) -> Scalar:
100 """Return a FLAVOR parameter as ``Scalar``."""
101 return Scalar.from_cpp(self._cpp_obj.get_flavor_param(pid.to_cpp(), _enum_value(data_type)))
102
103 def current_bins(self):
104 """Return the currently requested bins."""
105 return self._cpp_obj.current_bins()
106
107
109 """Runtime observable computed by a Python callable.
110
111 Args:
112 canonical: Canonical observable name registered in the mapper layer.
113 compute: Callback receiving ``(ctx, observable_id)`` for scalar
114 observables, or ``(ctx, bin, observable_id)`` for binned observables.
115 aliases: Optional aliases accepted by ``ObservableMapper.id_of``.
116 flha: Optional FLHA id for experimental input/output conventions.
117 Required when the observable is passed to ``StatisticInterface``.
118 dependencies: Parameter dependencies visible to Statistic.
119 binned: Whether ``compute`` should be called with the current bin.
120 """
121
123 self,
124 canonical: str,
125 compute: Callable,
126 aliases: Sequence[str] | None = None,
127 flha: LhaID | None = None,
128 dependencies: Sequence[ParamId] | Set[ParamId] | None = None,
129 binned: bool = False,
130 ):
131 if binned:
132
133 def wrapped_compute(ctx, bin_range, obs_id):
134 return compute(LambdaDecayContext(ctx), bin_range, _to_observable_id(obs_id))
135
136 factory = _obs.LambdaObservableConfig.binned_scalar
137 else:
138
139 def wrapped_compute(ctx, obs_id):
140 return compute(LambdaDecayContext(ctx), _to_observable_id(obs_id))
141
142 factory = _obs.LambdaObservableConfig.scalar
143
144 self._cpp_obj = factory(canonical, wrapped_compute)
145 self._cpp_obj.aliases = list(aliases or [])
146 self._cpp_obj.flha = None if flha is None else flha.to_cpp()
147 self._cpp_obj.dependencies = {
148 p.to_cpp() if isinstance(p, ParamId) else p for p in (dependencies or [])
149 }
150
151 @classmethod
153 cls,
154 canonical: str,
155 compute: Callable,
156 aliases: Sequence[str] | None = None,
157 flha: LhaID | None = None,
158 dependencies: Sequence[ParamId] | Set[ParamId] | None = None,
159 ) -> "LambdaObservableConfig":
160 """Create an unbinned scalar observable.
161
162 ``compute`` receives ``(ctx, observable_id)`` and can return ``float`` or
163 any object implementing ``__float__`` such as ``Scalar``.
164 """
165 return cls(
166 canonical, compute, aliases=aliases, flha=flha, dependencies=dependencies, binned=False
167 )
168
169 @classmethod
171 cls,
172 canonical: str,
173 compute: Callable,
174 aliases: Sequence[str] | None = None,
175 flha: LhaID | None = None,
176 dependencies: Sequence[ParamId] | Set[ParamId] | None = None,
177 ) -> "LambdaObservableConfig":
178 """Create a binned scalar observable.
179
180 ``compute`` receives ``(ctx, (q2_min, q2_max), observable_id)`` and can
181 return ``float`` or any object implementing ``__float__``.
182 """
183 return cls(
184 canonical, compute, aliases=aliases, flha=flha, dependencies=dependencies, binned=True
185 )
186
187 def to_cpp(self):
188 """Return the bound C++ ``LambdaObservableConfig``."""
189 return self._cpp_obj
190
191
192@dataclass
194 """Runtime decay backed by Python observable lambdas.
195
196 This config is the Python counterpart of C++ ``LambdaDecayConfig``. It can
197 declare builtin Wilson groups, lambda-backed custom Wilson groups and one or
198 more custom observables. Declared dependencies are propagated to Statistic.
199 """
200
201 canonical: str
202 observables: Sequence[LambdaObservableConfig]
203 aliases: Sequence[str] = field(default_factory=list)
204 matching_scale: float = 81.0
205 hadronic_scale: float = 4.8
206 order: QCDOrder = QCDOrder.LO
207 max_order: QCDOrder = QCDOrder.NNLO
208 wilson_groups: Sequence[WGroupId | str] = field(default_factory=list)
209 custom_wilson_groups: Sequence[CustomWilsonGroupConfig] = field(default_factory=list)
210 propagate_custom_wilson_dependencies: bool = True
211
212 def to_cpp(self):
213 """Convert to the bound C++ ``LambdaDecayConfig``."""
214 cpp = _obs.LambdaDecayConfig()
215 cpp.canonical = self.canonical
216 cpp.aliases = list(self.aliases)
217 cpp.matching_scale = float(self.matching_scale)
218 cpp.hadronic_scale = float(self.hadronic_scale)
219 cpp.order = _enum_value(self.order)
220 cpp.max_order = _enum_value(self.max_order)
221 cpp.wilson_groups = {
222 g._to_cpp() if isinstance(g, WGroupId) else GroupMapper.id_of(g)._to_cpp()
223 for g in self.wilson_groups
224 }
225 cpp.custom_wilson_groups = [
226 g.to_cpp() if isinstance(g, CustomWilsonGroupConfig) else g
227 for g in self.custom_wilson_groups
228 ]
229 cpp.observables = [
230 o.to_cpp() if isinstance(o, LambdaObservableConfig) else o for o in self.observables
231 ]
232 cpp.propagate_custom_wilson_dependencies = bool(self.propagate_custom_wilson_dependencies)
233 return cpp
234
235
236__all__ = ["LambdaObservableConfig", "LambdaDecayConfig", "LambdaDecayContext"]
static IdOf< WGroupTag > id_of(std::string_view s)
Resolves a string into an IdOf<Tag> via the registry.
Scalar get_FM(self, group, coeff, QCDOrder order, ContributionType contribution)
Scalar get_R(self, group, coeff, QCDOrder order, ContributionType contribution)
Scalar get_M(self, group, coeff, QCDOrder order, ContributionType contribution)
Scalar get_flavor_param(self, ParamId pid, DataType data_type=DataType.VALUE)
Scalar get_FR(self, group, coeff, QCDOrder order, ContributionType contribution)
Scalar get_sm_param(self, ParamId pid, DataType data_type=DataType.VALUE)
"LambdaObservableConfig" binned_scalar(cls, str canonical, Callable compute, Sequence[str]|None aliases=None, LhaID|None flha=None, Sequence[ParamId]|Set[ParamId]|None dependencies=None)
"LambdaObservableConfig" scalar(cls, str canonical, Callable compute, Sequence[str]|None aliases=None, LhaID|None flha=None, Sequence[ParamId]|Set[ParamId]|None dependencies=None)
__init__(self, str canonical, Callable compute, Sequence[str]|None aliases=None, LhaID|None flha=None, Sequence[ParamId]|Set[ParamId]|None dependencies=None, bool binned=False)