data-manipulation-utilities 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {data_manipulation_utilities-0.2.5.dist-info → data_manipulation_utilities-0.2.7.dist-info}/METADATA +179 -10
  2. {data_manipulation_utilities-0.2.5.dist-info → data_manipulation_utilities-0.2.7.dist-info}/RECORD +31 -19
  3. {data_manipulation_utilities-0.2.5.dist-info → data_manipulation_utilities-0.2.7.dist-info}/WHEEL +1 -1
  4. dmu/generic/hashing.py +44 -0
  5. dmu/generic/utilities.py +14 -1
  6. dmu/generic/version_management.py +3 -5
  7. dmu/ml/cv_diagnostics.py +221 -0
  8. dmu/ml/train_mva.py +143 -46
  9. dmu/pdataframe/utilities.py +36 -3
  10. dmu/plotting/fwhm.py +64 -0
  11. dmu/plotting/plotter.py +2 -0
  12. dmu/plotting/plotter_1d.py +87 -6
  13. dmu/stats/fitter.py +1 -1
  14. dmu/stats/minimizers.py +40 -11
  15. dmu/stats/model_factory.py +248 -44
  16. dmu/stats/zfit_models.py +68 -0
  17. dmu/stats/zfit_plotter.py +29 -21
  18. dmu/testing/utilities.py +31 -4
  19. dmu_data/ml/tests/diagnostics_from_file.yaml +13 -0
  20. dmu_data/ml/tests/diagnostics_from_model.yaml +10 -0
  21. dmu_data/ml/tests/diagnostics_multiple_methods.yaml +10 -0
  22. dmu_data/ml/tests/diagnostics_overlay.yaml +33 -0
  23. dmu_data/ml/tests/train_mva.yaml +19 -10
  24. dmu_data/ml/tests/train_mva_with_diagnostics.yaml +82 -0
  25. dmu_data/plotting/tests/plug_fwhm.yaml +24 -0
  26. dmu_data/plotting/tests/plug_stats.yaml +19 -0
  27. dmu_data/plotting/tests/simple.yaml +4 -3
  28. dmu_data/plotting/tests/styling.yaml +11 -0
  29. {data_manipulation_utilities-0.2.5.data → data_manipulation_utilities-0.2.7.data}/scripts/publish +0 -0
  30. {data_manipulation_utilities-0.2.5.dist-info → data_manipulation_utilities-0.2.7.dist-info}/entry_points.txt +0 -0
  31. {data_manipulation_utilities-0.2.5.dist-info → data_manipulation_utilities-0.2.7.dist-info}/top_level.txt +0 -0
dmu/stats/minimizers.py CHANGED
@@ -1,12 +1,16 @@
1
1
  '''
2
2
  Module containing derived classes from ZFit minimizer
3
3
  '''
4
+ from typing import Union
4
5
  import numpy
5
6
 
6
7
  import zfit
8
+ import matplotlib.pyplot as plt
9
+
7
10
  from zfit.result import FitResult
8
11
  from zfit.core.basepdf import BasePDF as zpdf
9
12
  from zfit.minimizers.baseminimizer import FailMinimizeNaN
13
+ from dmu.stats.utilities import print_pdf
10
14
  from dmu.stats.gof_calculator import GofCalculator
11
15
  from dmu.logging.log_store import LogStore
12
16
 
@@ -29,6 +33,7 @@ class AnealingMinimizer(zfit.minimize.Minuit):
29
33
  self._chi2ndof = chi2ndof
30
34
 
31
35
  self._check_thresholds()
36
+ self._l_bad_fit_res : list[FitResult] = []
32
37
 
33
38
  super().__init__()
34
39
  # ------------------------
@@ -66,19 +71,24 @@ class AnealingMinimizer(zfit.minimize.Minuit):
66
71
  return is_good
67
72
  # ------------------------
68
73
  def _is_good_fit(self, res : FitResult) -> bool:
74
+ good_fit = True
75
+
69
76
  if not res.valid:
70
- log.warning('Skipping invalid fit')
71
- return False
77
+ log.debug('Skipping invalid fit')
78
+ good_fit = False
72
79
 
73
80
  if res.status != 0:
74
- log.warning('Skipping fit with bad status')
75
- return False
81
+ log.debug('Skipping fit with bad status')
82
+ good_fit = False
76
83
 
77
84
  if not res.converged:
78
- log.warning('Skipping non-converging fit')
79
- return False
85
+ log.debug('Skipping non-converging fit')
86
+ good_fit = False
80
87
 
81
- return True
88
+ if not good_fit:
89
+ self._l_bad_fit_res.append(res)
90
+
91
+ return good_fit
82
92
  # ------------------------
83
93
  def _get_gof(self, nll) -> tuple[float, float]:
84
94
  log.debug('Checking GOF')
@@ -108,10 +118,11 @@ class AnealingMinimizer(zfit.minimize.Minuit):
108
118
  par.set_value(fval)
109
119
  log.debug(f'{par.name:<20}{ival:<15.3f}{"->":<10}{fval:<15.3f}{"in":<5}{par.lower:<15.3e}{par.upper:<15.3e}')
110
120
  # ------------------------
111
- def _pick_best_fit(self, d_chi2_res : dict) -> FitResult:
121
+ def _pick_best_fit(self, d_chi2_res : dict) -> Union[FitResult,None]:
112
122
  nres = len(d_chi2_res)
113
123
  if nres == 0:
114
- raise ValueError('No fits found')
124
+ log.error('No fits found')
125
+ return None
115
126
 
116
127
  l_chi2_res= list(d_chi2_res.items())
117
128
  l_chi2_res.sort()
@@ -149,6 +160,15 @@ class AnealingMinimizer(zfit.minimize.Minuit):
149
160
 
150
161
  return l_model[0]
151
162
  # ------------------------
163
+ def _print_failed_fit_diagnostics(self, nll) -> None:
164
+ for res in self._l_bad_fit_res:
165
+ print(res)
166
+
167
+ arr_mass = nll.data[0].numpy()
168
+
169
+ plt.hist(arr_mass, bins=60)
170
+ plt.show()
171
+ # ------------------------
152
172
  def minimize(self, nll, **kwargs) -> FitResult:
153
173
  '''
154
174
  Will run minimization and return FitResult object
@@ -156,18 +176,20 @@ class AnealingMinimizer(zfit.minimize.Minuit):
156
176
 
157
177
  d_chi2_res : dict[float,FitResult] = {}
158
178
  for i_try in range(self._ntries):
159
- log.info(f'try {i_try:02}/{self._ntries:02}')
160
179
  try:
161
180
  res = super().minimize(nll, **kwargs)
162
181
  except (FailMinimizeNaN, ValueError, RuntimeError) as exc:
163
- log.warning(exc)
182
+ log.error(f'{i_try:02}/{self._ntries:02}{"Failed":>20}')
183
+ log.debug(exc)
164
184
  self._randomize_parameters(nll)
165
185
  continue
166
186
 
167
187
  if not self._is_good_fit(res):
188
+ log.warning(f'{i_try:02}/{self._ntries:02}{"Bad fit":>20}')
168
189
  continue
169
190
 
170
191
  chi2, pvl = self._get_gof(nll)
192
+ log.info(f'{i_try:02}/{self._ntries:02}{chi2:>20.3f}')
171
193
  d_chi2_res[chi2] = res
172
194
 
173
195
  if self._is_good_gof(chi2, pvl):
@@ -176,6 +198,13 @@ class AnealingMinimizer(zfit.minimize.Minuit):
176
198
  self._randomize_parameters(nll)
177
199
 
178
200
  res = self._pick_best_fit(d_chi2_res)
201
+ if res is None:
202
+ self._print_failed_fit_diagnostics(nll)
203
+ pdf = nll.model[0]
204
+ print_pdf(pdf)
205
+
206
+ raise ValueError('Fit failed')
207
+
179
208
  pdf = self._pdf_from_nll(nll)
180
209
  self._set_pdf_pars(res, pdf)
181
210
 
@@ -6,9 +6,12 @@ Module storing ZModel class
6
6
  from typing import Callable, Union
7
7
 
8
8
  import zfit
9
+
9
10
  from zfit.core.interfaces import ZfitSpace as zobs
10
11
  from zfit.core.basepdf import BasePDF as zpdf
11
12
  from zfit.core.parameter import Parameter as zpar
13
+ from dmu.stats.zfit_models import HypExp
14
+ from dmu.stats.zfit_models import ModExp
12
15
  from dmu.logging.log_store import LogStore
13
16
 
14
17
  log=LogStore.add_logger('dmu:stats:model_factory')
@@ -37,7 +40,23 @@ class MethodRegistry:
37
40
  '''
38
41
  Will return method in charge of building PDF, for an input nickname
39
42
  '''
40
- return cls._d_method.get(nickname, None)
43
+ method = cls._d_method.get(nickname, None)
44
+
45
+ if method is not None:
46
+ return method
47
+
48
+ log.warning('Available PDFs:')
49
+ for value in cls._d_method:
50
+ log.info(f' {value}')
51
+
52
+ return method
53
+
54
+ @classmethod
55
+ def get_pdf_names(cls) -> list[str]:
56
+ '''
57
+ Returns list of PDFs that are registered/supported
58
+ '''
59
+ return list(cls._d_method)
41
60
  #-----------------------------------------
42
61
  class ModelFactory:
43
62
  '''
@@ -48,67 +67,172 @@ class ModelFactory:
48
67
 
49
68
  l_pdf = ['dscb', 'gauss']
50
69
  l_shr = ['mu']
51
- mod = ModelFactory(obs = obs, l_pdf = l_pdf, l_shared=l_shr)
70
+ l_flt = ['mu', 'sg']
71
+ d_rep = {'mu' : 'scale', 'sg' : 'reso'}
72
+ mod = ModelFactory(preffix = 'signal', obs = obs, l_pdf = l_pdf, l_shared = l_shr, d_rep = d_rep)
52
73
  pdf = mod.get_pdf()
53
74
  ```
54
75
 
55
- where one can specify which parameters can be shared among the PDFs
76
+ where one can specify which parameters
77
+
78
+ - Can be shared among the PDFs
79
+ - Are meant to float if this fit is done to MC, in order to fix parameters in data.
80
+ - Are scales or resolutions that need reparametrizations
56
81
  '''
57
82
  #-----------------------------------------
58
- def __init__(self, obs : zobs, l_pdf : list[str], l_shared : list[str]):
83
+ def __init__(self,
84
+ preffix : str,
85
+ obs : zobs,
86
+ l_pdf : list[str],
87
+ l_shared : list[str],
88
+ l_float : list[str],
89
+ d_fix : dict[str:float] = None,
90
+ d_rep : dict[str:str] = None):
59
91
  '''
92
+ preffix: used to identify PDF, will be used to name every parameter
60
93
  obs: zfit obserbable
61
94
  l_pdf: List of PDF nicknames which are registered below
62
95
  l_shared: List of parameter names that are shared
96
+ l_float: List of parameter names to allow to float
97
+ d_fix: Dictionary with keys as the beginning of the name of a parameter and value as the number
98
+ to which it has to be fixed. If not one and only one parameter is found, ValueError is raised
99
+ d_rep: Dictionary with keys as variables that will be reparametrized
63
100
  '''
64
101
 
102
+ self._preffix = preffix
65
103
  self._l_pdf = l_pdf
66
104
  self._l_shr = l_shared
67
- self._l_can_be_shared = ['mu', 'sg']
105
+ self._l_flt = l_float
106
+ self._d_fix = d_fix
107
+ self._d_rep = d_rep
68
108
  self._obs = obs
69
109
 
70
110
  self._d_par : dict[str,zpar] = {}
111
+
112
+ self._check_reparametrization()
113
+ #-----------------------------------------
114
+ def _check_reparametrization(self) -> None:
115
+ if self._d_rep is None:
116
+ return
117
+
118
+ s_par_1 = set(self._d_rep)
119
+ s_par_2 = set(self._l_flt)
120
+
121
+ if not s_par_1.isdisjoint(s_par_2):
122
+ raise ValueError('Non empty intersection between floating and reparametrization parameters')
123
+
124
+ s_kind = set(self._d_rep.values())
125
+ if not s_kind.issubset({'scale', 'reso'}):
126
+ raise ValueError(f'Only scales and resolution reparametrizations allowed, found: {s_kind}')
127
+ #-----------------------------------------
128
+ def _split_name(self, name : str) -> tuple[str,str]:
129
+ l_part = name.split('_')
130
+ pname = l_part[0]
131
+ xname = '_'.join(l_part[1:])
132
+
133
+ return pname, xname
71
134
  #-----------------------------------------
72
- def _fltname_from_name(self, name : str) -> str:
73
- if name in ['mu', 'sg']:
135
+ def _get_parameter_name(self, name : str, suffix : str) -> str:
136
+ pname, xname = self._split_name(name)
137
+
138
+ log.debug(f'Using physical name: {pname}')
139
+
140
+ if pname in self._l_shr:
141
+ name = f'{pname}_{self._preffix}'
142
+ else:
143
+ name = f'{pname}_{xname}_{self._preffix}{suffix}'
144
+
145
+ if pname in self._l_flt:
74
146
  return f'{name}_flt'
75
147
 
76
148
  return name
77
149
  #-----------------------------------------
78
- def _get_name(self, name : str, suffix : str) -> str:
79
- for can_be_shared in self._l_can_be_shared:
80
- if name.startswith(f'{can_be_shared}_') and can_be_shared in self._l_shr:
81
- return self._fltname_from_name(can_be_shared)
150
+ def _get_parameter(
151
+ self,
152
+ name : str,
153
+ suffix : str,
154
+ val : float,
155
+ low : float,
156
+ high : float) -> zpar:
157
+
158
+ par_name = self._get_parameter_name(name, suffix)
159
+ log.debug(f'Assigning name: {par_name}')
160
+
161
+ if par_name in self._d_par:
162
+ return self._d_par[par_name]
163
+
164
+ is_reparametrized = self._is_reparametrized(name)
165
+
166
+ if is_reparametrized:
167
+ init_name, _ = self._split_name(par_name)
168
+ par = self._get_reparametrization(par_name, init_name, val, low, high)
169
+ else:
170
+ par = zfit.param.Parameter(par_name, val, low, high)
82
171
 
83
- return self._fltname_from_name(f'{name}{suffix}')
172
+ self._d_par[par_name] = par
173
+
174
+ return par
84
175
  #-----------------------------------------
85
- def _get_parameter(self,
86
- name : str,
87
- suffix : str,
88
- val : float,
89
- low : float,
90
- high : float) -> zpar:
91
- name = self._get_name(name, suffix)
92
- if name in self._d_par:
93
- return self._d_par[name]
176
+ def _is_reparametrized(self, name : str) -> bool:
177
+ if self._d_rep is None:
178
+ return False
179
+
180
+ root_name, _ = self._split_name(name)
181
+
182
+ is_rep = root_name in self._d_rep
94
183
 
95
- par = zfit.param.Parameter(name, val, low, high)
184
+ log.debug(f'Reparametrizing {name}: {is_rep}')
96
185
 
97
- self._d_par[name] = par
186
+ return is_rep
187
+ #-----------------------------------------
188
+ def _get_reparametrization(self, par_name : str, init_name : str, value : float, low : float, high : float) -> zpar:
189
+ log.debug(f'Reparametrizing {par_name}')
190
+ par_const = zfit.Parameter(par_name, value, low, high)
191
+ par_const.floating = False
192
+
193
+ kind = self._d_rep[init_name]
194
+ if kind == 'reso':
195
+ par_reso = zfit.Parameter(f'{par_name}_reso_flt' , 1.0, 0.20, 5.0)
196
+ par = zfit.ComposedParameter(f'{par_name}_cmp', lambda d_par : d_par['par_const'] * d_par['reso' ], params={'par_const' : par_const, 'reso' : par_reso } )
197
+ elif kind == 'scale':
198
+ par_scale = zfit.Parameter(f'{par_name}_scale_flt', 0.0, -100, 100)
199
+ par = zfit.ComposedParameter(f'{par_name}_cmp', lambda d_par : d_par['par_const'] + d_par['scale'], params={'par_const' : par_const, 'scale' : par_scale} )
200
+ else:
201
+ raise ValueError(f'Invalid kind: {kind}')
98
202
 
99
203
  return par
100
204
  #-----------------------------------------
101
205
  @MethodRegistry.register('exp')
102
206
  def _get_exponential(self, suffix : str = '') -> zpdf:
103
- c = self._get_parameter('c_exp', suffix, -0.005, -0.05, 0.00)
104
- pdf = zfit.pdf.Exponential(c, self._obs)
207
+ c = self._get_parameter('c_exp', suffix, -0.010, -0.020, -0.0001)
208
+ pdf = zfit.pdf.Exponential(c, self._obs, name=f'exp{suffix}')
209
+
210
+ return pdf
211
+ # ---------------------------------------------
212
+ @MethodRegistry.register('hypexp')
213
+ def _get_hypexp(self, suffix : str = '') -> zpdf:
214
+ mu = zfit.Parameter('mu_hypexp', 5000, 4000, 6000)
215
+ ap = zfit.Parameter('ap_hypexp', 0.020, 0, 0.10)
216
+ bt = zfit.Parameter('bt_hypexp', 0.002, 0.0001, 0.003)
217
+
218
+ pdf= HypExp(obs=self._obs, mu=mu, alpha=ap, beta=bt, name=f'hypexp{suffix}')
219
+
220
+ return pdf
221
+ # ---------------------------------------------
222
+ @MethodRegistry.register('modexp')
223
+ def _get_modexp(self, suffix : str = '') -> zpdf:
224
+ mu = zfit.Parameter('mu_modexp', 4250, 4250, 4500)
225
+ ap = zfit.Parameter('ap_modexp', 0.002, 0.002, 0.026)
226
+ bt = zfit.Parameter('bt_modexp', 0.002, 0.002, 0.020)
227
+
228
+ pdf= ModExp(obs=self._obs, mu=mu, alpha=ap, beta=bt, name=f'modexp{suffix}')
105
229
 
106
230
  return pdf
107
231
  #-----------------------------------------
108
232
  @MethodRegistry.register('pol1')
109
233
  def _get_pol1(self, suffix : str = '') -> zpdf:
110
234
  a = self._get_parameter('a_pol1', suffix, -0.005, -0.95, 0.00)
111
- pdf = zfit.pdf.Chebyshev(obs=self._obs, coeffs=[a])
235
+ pdf = zfit.pdf.Chebyshev(obs=self._obs, coeffs=[a], name=f'pol1{suffix}')
112
236
 
113
237
  return pdf
114
238
  #-----------------------------------------
@@ -116,51 +240,100 @@ class ModelFactory:
116
240
  def _get_pol2(self, suffix : str = '') -> zpdf:
117
241
  a = self._get_parameter('a_pol2', suffix, -0.005, -0.95, 0.00)
118
242
  b = self._get_parameter('b_pol2', suffix, 0.000, -0.95, 0.95)
119
- pdf = zfit.pdf.Chebyshev(obs=self._obs, coeffs=[a, b])
243
+ pdf = zfit.pdf.Chebyshev(obs=self._obs, coeffs=[a, b ], name=f'pol2{suffix}')
244
+
245
+ return pdf
246
+ # ---------------------------------------------
247
+ @MethodRegistry.register('pol3')
248
+ def _get_pol3(self, suffix : str = '') -> zpdf:
249
+ a = zfit.Parameter('a_pol3', -0.005, -0.95, 0.00)
250
+ b = zfit.Parameter('b_pol3', 0.000, -0.95, 0.95)
251
+ c = zfit.Parameter('c_pol3', 0.000, -0.95, 0.95)
252
+ pdf = zfit.pdf.Chebyshev(obs=self._obs, coeffs=[a, b, c], name=f'pol3{suffix}')
120
253
 
121
254
  return pdf
122
255
  #-----------------------------------------
123
256
  @MethodRegistry.register('cbr')
124
257
  def _get_cbr(self, suffix : str = '') -> zpdf:
125
- mu = self._get_parameter('mu_cbr', suffix, 5300, 5250, 5350)
258
+ mu = self._get_parameter('mu_cbr', suffix, 5300, 5100, 5500)
126
259
  sg = self._get_parameter('sg_cbr', suffix, 10, 2, 300)
127
- ar = self._get_parameter('ac_cbr', suffix, -2, -4., -1.)
128
- nr = self._get_parameter('nc_cbr', suffix, 1, 0.5, 5.0)
260
+ ar = self._get_parameter('ac_cbr', suffix, -2, -14., -0.1)
261
+ nr = self._get_parameter('nc_cbr', suffix, 1, 0.5, 150)
262
+
263
+ pdf = zfit.pdf.CrystalBall(mu, sg, ar, nr, self._obs, name=f'cbr{suffix}')
264
+
265
+ return pdf
266
+ #-----------------------------------------
267
+ @MethodRegistry.register('suj')
268
+ def _get_suj(self, suffix : str = '') -> zpdf:
269
+ mu = self._get_parameter('mu_suj', suffix, 5300, 5000, 6000)
270
+ sg = self._get_parameter('sg_suj', suffix, 10, 2, 5000)
271
+ gm = self._get_parameter('gm_suj', suffix, 1, -10, 10)
272
+ dl = self._get_parameter('dl_suj', suffix, 1, 0.1, 40)
129
273
 
130
- pdf = zfit.pdf.CrystalBall(mu, sg, ar, nr, self._obs)
274
+ pdf = zfit.pdf.JohnsonSU(mu, sg, gm, dl, self._obs, name=f'suj{suffix}')
131
275
 
132
276
  return pdf
133
277
  #-----------------------------------------
134
278
  @MethodRegistry.register('cbl')
135
279
  def _get_cbl(self, suffix : str = '') -> zpdf:
136
- mu = self._get_parameter('mu_cbl', suffix, 5300, 5250, 5350)
280
+ mu = self._get_parameter('mu_cbl', suffix, 5300, 5100, 5500)
137
281
  sg = self._get_parameter('sg_cbl', suffix, 10, 2, 300)
138
- al = self._get_parameter('ac_cbl', suffix, 2, 1., 14.)
139
- nl = self._get_parameter('nc_cbl', suffix, 1, 0.5, 15.)
282
+ al = self._get_parameter('ac_cbl', suffix, 2, 0.0, 14.)
283
+ nl = self._get_parameter('nc_cbl', suffix, 1, 0.5, 150)
140
284
 
141
- pdf = zfit.pdf.CrystalBall(mu, sg, al, nl, self._obs)
285
+ pdf = zfit.pdf.CrystalBall(mu, sg, al, nl, self._obs, name=f'cbl{suffix}')
142
286
 
143
287
  return pdf
144
288
  #-----------------------------------------
145
289
  @MethodRegistry.register('gauss')
146
290
  def _get_gauss(self, suffix : str = '') -> zpdf:
147
- mu = self._get_parameter('mu_gauss', suffix, 5300, 5250, 5350)
291
+ mu = self._get_parameter('mu_gauss', suffix, 5300, 5100, 5500)
148
292
  sg = self._get_parameter('sg_gauss', suffix, 10, 2, 300)
149
293
 
150
- pdf = zfit.pdf.Gauss(mu, sg, self._obs)
294
+ pdf = zfit.pdf.Gauss(mu, sg, self._obs, name=f'gauss{suffix}')
151
295
 
152
296
  return pdf
153
297
  #-----------------------------------------
154
298
  @MethodRegistry.register('dscb')
155
299
  def _get_dscb(self, suffix : str = '') -> zpdf:
156
- mu = self._get_parameter('mu_dscb', suffix, 5300, 5250, 5400)
157
- sg = self._get_parameter('sg_dscb', suffix, 10, 2, 30)
300
+ mu = self._get_parameter('mu_dscb', suffix, 5300, 5000, 5400)
301
+ sg = self._get_parameter('sg_dscb', suffix, 10, 2, 500)
158
302
  ar = self._get_parameter('ar_dscb', suffix, 1, 0, 5)
159
303
  al = self._get_parameter('al_dscb', suffix, 1, 0, 5)
160
- nr = self._get_parameter('nr_dscb', suffix, 2, 1, 15)
161
- nl = self._get_parameter('nl_dscb', suffix, 2, 0, 15)
304
+ nr = self._get_parameter('nr_dscb', suffix, 2, 1, 150)
305
+ nl = self._get_parameter('nl_dscb', suffix, 2, 0, 150)
306
+
307
+ pdf = zfit.pdf.DoubleCB(mu, sg, al, nl, ar, nr, self._obs, name=f'dscb{suffix}')
308
+
309
+ return pdf
310
+ #-----------------------------------------
311
+ @MethodRegistry.register('voigt')
312
+ def _get_voigt(self, suffix : str = '') -> zpdf:
313
+ mu = zfit.Parameter('mu_voigt', 5280, 5040, 5500)
314
+ sg = zfit.Parameter('sg_voigt', 20, 10, 400)
315
+ gm = zfit.Parameter('gm_voigt', 4, 0.1, 100)
162
316
 
163
- pdf = zfit.pdf.DoubleCB(mu, sg, al, nl, ar, nr, self._obs)
317
+ pdf = zfit.pdf.Voigt(m=mu, sigma=sg, gamma=gm, obs=self._obs, name=f'voigt{suffix}')
318
+
319
+ return pdf
320
+ #-----------------------------------------
321
+ @MethodRegistry.register('qgauss')
322
+ def _get_qgauss(self, suffix : str = '') -> zpdf:
323
+ mu = zfit.Parameter('mu_qgauss', 5280, 5040, 5500)
324
+ sg = zfit.Parameter('sg_qgauss', 20, 10, 400)
325
+ q = zfit.Parameter( 'q_qgauss', 1, 1, 3)
326
+
327
+ pdf = zfit.pdf.QGauss(q=q, mu=mu, sigma=sg, obs=self._obs, name =f'qgauss{suffix}')
328
+
329
+ return pdf
330
+ #-----------------------------------------
331
+ @MethodRegistry.register('cauchy')
332
+ def _get_cauchy(self, suffix : str = '') -> zpdf:
333
+ mu = zfit.Parameter('mu', 5280, 5040, 5500)
334
+ gm = zfit.Parameter('gm', 150, 50, 500)
335
+
336
+ pdf = zfit.pdf.Cauchy(obs=self._obs, m=mu, gamma=gm, name=f'cauchy{suffix}')
164
337
 
165
338
  return pdf
166
339
  #-----------------------------------------
@@ -194,9 +367,39 @@ class ModelFactory:
194
367
  log.debug('Requested only one PDF, skipping sum')
195
368
  return l_pdf[0]
196
369
 
197
- l_frc= [ zfit.param.Parameter(f'frc_{ifrc + 1}', 0.5, 0, 1) for ifrc in range(nfrc - 1) ]
370
+ l_frc= [ zfit.param.Parameter(f'frc_{self._preffix}_{ifrc + 1}', 0.5, 0, 1) for ifrc in range(nfrc - 1) ]
371
+
372
+ pdf = zfit.pdf.SumPDF(l_pdf, name=self._preffix, fracs=l_frc)
373
+
374
+ return pdf
375
+ #-----------------------------------------
376
+ def _find_par(self, s_par : set[zpar], name_start : str) -> zpar:
377
+ l_par_match = [ par for par in s_par if par.name.startswith(name_start) ]
378
+
379
+ if len(l_par_match) != 1:
380
+ for par in s_par:
381
+ log.info(par.name)
382
+
383
+ raise ValueError(f'Not found one and only one parameter starting with: {name_start}')
384
+
385
+ return l_par_match[0]
386
+ #-----------------------------------------
387
+ def _fix_parameters(self, pdf : zpdf) -> zpdf:
388
+ if self._d_fix is None:
389
+ log.debug('Not fixing any parameter')
390
+ return pdf
391
+
392
+ s_par = pdf.get_params()
393
+
394
+ log.info('-' * 30)
395
+ log.info('Fixing parameters')
396
+ log.info('-' * 30)
397
+ for name_start, value in self._d_fix.items():
398
+ par = self._find_par(s_par, name_start)
399
+ par.set_value(value)
198
400
 
199
- pdf = zfit.pdf.SumPDF(l_pdf, fracs=l_frc)
401
+ log.info(f'{name_start:<20}{value:<20.3f}')
402
+ par.floating = False
200
403
 
201
404
  return pdf
202
405
  #-----------------------------------------
@@ -208,6 +411,7 @@ class ModelFactory:
208
411
  l_type= self._get_pdf_types()
209
412
  l_pdf = [ self._get_pdf(kind, preffix) for kind, preffix in l_type ]
210
413
  pdf = self._add_pdf(l_pdf)
414
+ pdf = self._fix_parameters(pdf)
211
415
 
212
416
  return pdf
213
417
  #-----------------------------------------
@@ -0,0 +1,68 @@
1
+ '''
2
+ Module meant to hold classes defining PDFs that can be used by ZFIT
3
+ '''
4
+
5
+ import zfit
6
+ from zfit import z
7
+
8
+ #-------------------------------------------------------------------
9
+ class HypExp(zfit.pdf.ZPDF):
10
+ _N_OBS = 1
11
+ _PARAMS = ['mu', 'alpha', 'beta']
12
+
13
+ def _unnormalized_pdf(self, x):
14
+ x = z.unstack_x(x)
15
+ mu = self.params['mu']
16
+ ap = self.params['alpha']
17
+ bt = self.params['beta']
18
+
19
+ u = (x - mu)
20
+ val = z.exp(-bt * x) / (1 + z.exp(-ap * u))
21
+
22
+ return val
23
+ #-------------------------------------------------------------------
24
+ class ModExp(zfit.pdf.ZPDF):
25
+ _N_OBS = 1
26
+ _PARAMS = ['mu', 'alpha', 'beta']
27
+
28
+ def _unnormalized_pdf(self, x):
29
+ x = z.unstack_x(x)
30
+ mu = self.params['mu']
31
+ ap = self.params['alpha']
32
+ bt = self.params['beta']
33
+
34
+ u = x - mu
35
+ val = (1 - z.exp(-ap * u)) * z.exp(-bt * u)
36
+
37
+ return val
38
+ #-------------------------------------------------------------------
39
+ class GenExp(zfit.pdf.ZPDF):
40
+ _N_OBS = 1
41
+ _PARAMS = ['mu', 'sg', 'alpha', 'beta']
42
+
43
+ def _unnormalized_pdf(self, x):
44
+ x = z.unstack_x(x)
45
+ mu = self.params['mu']
46
+ sg = self.params['sg']
47
+ ap = self.params['alpha']
48
+ bt = self.params['beta']
49
+
50
+ u = (x - mu) / sg
51
+ val = (1 - z.exp(-ap * u)) * z.exp(-bt * u)
52
+
53
+ return val
54
+ #-------------------------------------------------------------------
55
+ class FermiDirac(zfit.pdf.ZPDF):
56
+ _N_OBS = 1
57
+ _PARAMS = ['mu', 'ap']
58
+
59
+ def _unnormalized_pdf(self, x):
60
+ x = z.unstack_x(x)
61
+ mu = self.params['mu']
62
+ ap = self.params['ap']
63
+
64
+ exp = (x - mu) / ap
65
+ den = 1 + z.exp(exp)
66
+
67
+ return 1. / den
68
+ #-------------------------------------------------------------------