nnpdf 4.1.0__py3-none-any.whl → 4.1.1__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 (116) hide show
  1. n3fit/backends/keras_backend/MetaModel.py +27 -26
  2. n3fit/backends/keras_backend/callbacks.py +16 -8
  3. n3fit/backends/keras_backend/internal_state.py +13 -2
  4. n3fit/backends/keras_backend/operations.py +26 -26
  5. n3fit/hyper_optimization/hyper_scan.py +3 -9
  6. n3fit/hyper_optimization/penalties.py +11 -8
  7. n3fit/hyper_optimization/rewards.py +65 -34
  8. n3fit/model_gen.py +344 -270
  9. n3fit/model_trainer.py +71 -105
  10. n3fit/performfit.py +2 -7
  11. n3fit/tests/regressions/quickcard_1.json +12 -28
  12. n3fit/tests/regressions/quickcard_3.json +12 -28
  13. n3fit/tests/regressions/quickcard_pol_1.json +10 -26
  14. n3fit/tests/regressions/quickcard_pol_3.json +9 -25
  15. n3fit/tests/regressions/quickcard_qed_1.json +11 -27
  16. n3fit/tests/regressions/quickcard_qed_3.json +11 -27
  17. n3fit/tests/test_hyperopt.py +6 -12
  18. n3fit/tests/test_layers.py +6 -6
  19. n3fit/tests/test_modelgen.py +73 -24
  20. n3fit/tests/test_multireplica.py +52 -16
  21. n3fit/tests/test_penalties.py +7 -8
  22. n3fit/tests/test_preprocessing.py +2 -2
  23. n3fit/tests/test_vpinterface.py +5 -10
  24. n3fit/vpinterface.py +88 -44
  25. {nnpdf-4.1.0.dist-info → nnpdf-4.1.1.dist-info}/METADATA +9 -3
  26. {nnpdf-4.1.0.dist-info → nnpdf-4.1.1.dist-info}/RECORD +105 -67
  27. {nnpdf-4.1.0.dist-info → nnpdf-4.1.1.dist-info}/WHEEL +1 -1
  28. nnpdf_data/_version.py +1 -1
  29. nnpdf_data/commondata/ATLAS_2JET_7TEV_R06/metadata.yaml +16 -5
  30. nnpdf_data/commondata/ATLAS_TTBAR_13P6TEV_TOT/data.yaml +2 -0
  31. nnpdf_data/commondata/ATLAS_TTBAR_13P6TEV_TOT/kinematics.yaml +13 -0
  32. nnpdf_data/commondata/ATLAS_TTBAR_13P6TEV_TOT/metadata.yaml +51 -0
  33. nnpdf_data/commondata/ATLAS_TTBAR_13P6TEV_TOT/uncertainties.yaml +17 -0
  34. nnpdf_data/commondata/ATLAS_TTBAR_5TEV_TOT/data.yaml +2 -0
  35. nnpdf_data/commondata/ATLAS_TTBAR_5TEV_TOT/kinematics.yaml +13 -0
  36. nnpdf_data/commondata/ATLAS_TTBAR_5TEV_TOT/metadata.yaml +52 -0
  37. nnpdf_data/commondata/ATLAS_TTBAR_5TEV_TOT/uncertainties.yaml +22 -0
  38. nnpdf_data/commondata/ATLAS_WPWM_13P6TEV_TOT/data.yaml +3 -0
  39. nnpdf_data/commondata/ATLAS_WPWM_13P6TEV_TOT/kinematics.yaml +17 -0
  40. nnpdf_data/commondata/ATLAS_WPWM_13P6TEV_TOT/metadata.yaml +57 -0
  41. nnpdf_data/commondata/ATLAS_WPWM_13P6TEV_TOT/uncertainties.yaml +8 -0
  42. nnpdf_data/commondata/ATLAS_Z0_13P6TEV_TOT/data.yaml +2 -0
  43. nnpdf_data/commondata/ATLAS_Z0_13P6TEV_TOT/kinematics.yaml +9 -0
  44. nnpdf_data/commondata/ATLAS_Z0_13P6TEV_TOT/metadata.yaml +54 -0
  45. nnpdf_data/commondata/ATLAS_Z0_13P6TEV_TOT/uncertainties.yaml +7 -0
  46. nnpdf_data/commondata/CMS_1JET_8TEV/metadata.yaml +7 -1
  47. nnpdf_data/commondata/CMS_2JET_7TEV/metadata.yaml +16 -19
  48. nnpdf_data/commondata/CMS_TTBAR_13P6TEV_TOT/data.yaml +2 -0
  49. nnpdf_data/commondata/CMS_TTBAR_13P6TEV_TOT/kinematics.yaml +13 -0
  50. nnpdf_data/commondata/CMS_TTBAR_13P6TEV_TOT/metadata.yaml +51 -0
  51. nnpdf_data/commondata/CMS_TTBAR_13P6TEV_TOT/uncertainties.yaml +12 -0
  52. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/data_d2Sig_dmttBar_dyttBar.yaml +17 -0
  53. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/data_dSig_dmttBar.yaml +8 -0
  54. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/data_dSig_dpTt.yaml +8 -0
  55. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/data_dSig_dyt.yaml +11 -0
  56. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/filter.py +260 -0
  57. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/kinematics_d2Sig_dmttBar_dyttBar.yaml +193 -0
  58. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/kinematics_dSig_dmttBar.yaml +57 -0
  59. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/kinematics_dSig_dpTt.yaml +57 -0
  60. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/kinematics_dSig_dyt.yaml +81 -0
  61. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/metadata.yaml +114 -0
  62. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/rawdata/mtt_abs_parton.yaml +828 -0
  63. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/rawdata/mttytt-abs_parton.yaml +1899 -0
  64. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/rawdata/ptt_abs_parton.yaml +828 -0
  65. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/rawdata/submission.yaml +47 -0
  66. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/rawdata/yt_abs_parton.yaml +1179 -0
  67. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/uncertainties_d2Sig_dmttBar_dyttBar.yaml +2282 -0
  68. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/uncertainties_dSig_dmttBar.yaml +1256 -0
  69. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/uncertainties_dSig_dpTt.yaml +1256 -0
  70. nnpdf_data/commondata/CMS_TTBAR_13TEV_2L_138FB-1_DIF/uncertainties_dSig_dyt.yaml +1598 -0
  71. nnpdf_data/commondata/CMS_TTBAR_13TEV_35P9FB-1_TOT/data.yaml +2 -0
  72. nnpdf_data/commondata/CMS_TTBAR_13TEV_35P9FB-1_TOT/kinematics.yaml +13 -0
  73. nnpdf_data/commondata/CMS_TTBAR_13TEV_35P9FB-1_TOT/metadata.yaml +51 -0
  74. nnpdf_data/commondata/CMS_TTBAR_13TEV_35P9FB-1_TOT/uncertainties.yaml +17 -0
  75. nnpdf_data/commondata/CMS_TTBAR_5TEV_TOT/metadata.yaml +1 -1
  76. nnpdf_data/commondata/NNPDF_POS_2P24GEV/metadata.yaml +60 -0
  77. nnpdf_data/commondata/dataset_names.yml +6 -1
  78. nnpdf_data/theory_cards/41000010.yaml +42 -0
  79. nnpdf_data/theory_cards/41000011.yaml +43 -0
  80. nnpdf_data/theory_cards/41000012.yaml +43 -0
  81. nnpdf_data/theory_cards/41000013.yaml +42 -0
  82. nnpdf_data/theory_cards/41000014.yaml +43 -0
  83. nnpdf_data/theory_cards/41000015.yaml +43 -0
  84. validphys/_version.py +1 -1
  85. validphys/config.py +30 -10
  86. validphys/convolution.py +37 -14
  87. validphys/coredata.py +15 -5
  88. validphys/covmats.py +9 -2
  89. validphys/dataplots.py +1 -1
  90. validphys/filters.py +17 -3
  91. validphys/fkparser.py +11 -1
  92. validphys/gridvalues.py +1 -0
  93. validphys/hessian2mc.py +5 -5
  94. validphys/lhaindex.py +5 -0
  95. validphys/loader.py +1 -1
  96. validphys/n3fit_data.py +107 -61
  97. validphys/nnprofile_default.yaml +2 -1
  98. validphys/pineparser.py +12 -2
  99. validphys/scripts/postfit.py +4 -4
  100. validphys/scripts/vp_pdfrename.py +8 -9
  101. validphys/tests/conftest.py +6 -2
  102. validphys/tests/test_hessian2mc.py +7 -5
  103. validphys/utils.py +1 -0
  104. n3fit/tests/regressions/quickcard_pol/filter.yml +0 -80
  105. n3fit/tests/regressions/quickcard_pol/nnfit/input/lockfile.yaml +0 -111
  106. n3fit/tests/regressions/quickcard_pol/nnfit/replica_1/quickcard_pol.exportgrid +0 -572
  107. n3fit/tests/regressions/quickcard_pol/nnfit/replica_1/quickcard_pol.json +0 -71
  108. n3fit/tests/regressions/quickcard_pol/nnfit/replica_3/quickcard_pol.exportgrid +0 -615
  109. n3fit/tests/regressions/quickcard_pol/nnfit/replica_3/quickcard_pol.json +0 -71
  110. n3fit/tests/regressions/weights.weights.h5 +0 -0
  111. n3fit/tests/regressions/weights_pol.weights.h5 +0 -0
  112. n3fit/tests/test +0 -1
  113. nnpdf_data/theory_cards/40000099.yaml +0 -41
  114. nnpdf_data/theory_cards/40000099.yml +0 -41
  115. {nnpdf-4.1.0.dist-info → nnpdf-4.1.1.dist-info}/entry_points.txt +0 -0
  116. {nnpdf-4.1.0.dist-info → nnpdf-4.1.1.dist-info/licenses}/LICENSE +0 -0
@@ -1,14 +1,14 @@
1
1
  """
2
- MetaModel class
2
+ MetaModel class
3
3
 
4
- Extension of the backend Model class containing some wrappers in order to absorb other
5
- backend-dependent calls.
4
+ Extension of the backend Model class containing some wrappers in order to absorb other
5
+ backend-dependent calls.
6
6
  """
7
7
 
8
8
  from pathlib import Path
9
9
  import re
10
10
 
11
- from keras import Variable
11
+ from keras import backend as K
12
12
  from keras import optimizers as Kopt
13
13
  from keras.models import Model
14
14
  import numpy as np
@@ -32,6 +32,16 @@ NN_PREFIX = "NN"
32
32
  NN_LAYER_ALL_REPLICAS = "all_NNs"
33
33
  PREPROCESSING_LAYER_ALL_REPLICAS = "preprocessing_factor"
34
34
 
35
+ # Running many steps in epoch eliminates some per-epoch overhead and has a big impact
36
+ # in GPU. In benchmarks, more than 100 steps doesn't seem to have any impact
37
+ # so this is the rationale for that number.
38
+ #
39
+ # For reasons that are not clear at the time of writing (13/08/2025) jax only accepts
40
+ # one step per epoch, showing the same penalty of other libraries.
41
+ STEPS_PER_EPOCH = 100
42
+ if K.backend() == "jax":
43
+ STEPS_PER_EPOCH = 1
44
+
35
45
  # Some keys need to work for everyone
36
46
  for k, v in optimizers.items():
37
47
  v[1]["clipnorm"] = 1.0
@@ -98,7 +108,6 @@ class MetaModel(Model):
98
108
  self.required_slots.add(k)
99
109
  super().__init__(input_tensors, output_tensors, **kwargs)
100
110
 
101
- self.x_in = x_in
102
111
  self.input_tensors = input_tensors
103
112
  self.single_replica_generator = None
104
113
 
@@ -106,6 +115,10 @@ class MetaModel(Model):
106
115
  self.compute_losses_function = None
107
116
  self._scaler = scaler
108
117
 
118
+ # Keras' __setattr__ would try to track the input dictionary as a TrackedDict
119
+ # which is incompatible with jax, to avoid this problem, set the attribute directly
120
+ object.__setattr__(self, "x_in", x_in)
121
+
109
122
  def _parse_input(self, extra_input=None):
110
123
  """Returns the input data the model was compiled with.
111
124
  Introduces the extra_input in the places asigned to the placeholders.
@@ -153,34 +166,22 @@ class MetaModel(Model):
153
166
  if y is None:
154
167
  y = self.target_tensors
155
168
 
156
- # Avoids Tensorflow overhead that happens at every epoch, by putting multiple steps in an epoch
157
- steps_per_epoch = self._determine_steps_per_epoch(epochs)
169
+ # Running more than 1 step for every epoch eliminates some overhead of the backend libraries.
170
+ # In the special case in which epochs < STEPS_PER_EPOCH, set it to 1
171
+ if epochs < STEPS_PER_EPOCH:
172
+ steps_per_epoch = 1
173
+ else:
174
+ steps_per_epoch = STEPS_PER_EPOCH
158
175
 
159
176
  for k, v in x_params.items():
160
177
  x_params[k] = ops.repeat(v, steps_per_epoch, axis=0)
161
178
  y = [ops.repeat(yi, steps_per_epoch, axis=0) for yi in y]
162
-
163
179
  history = super().fit(
164
180
  x=x_params, y=y, epochs=epochs // steps_per_epoch, batch_size=1, **kwargs
165
181
  )
166
182
  loss_dict = history.history
167
183
  return loss_dict
168
184
 
169
- def _determine_steps_per_epoch(self, epochs):
170
- """Determine how many step to run in every epoch.
171
- When running a single replica (CPU) or when the number of epochs is < 100 default to 1.
172
- Otherwise run 100 steps per epoch.
173
-
174
- If the number of epochs requested is not divisible by 100 there will be a number
175
- of extra training epochs being run equal to max_epochs % 100 in the worst case.
176
-
177
- """
178
- num_replicas = self.output_shape[0]
179
- if num_replicas == 1 or epochs < 100:
180
- return 1
181
-
182
- return 100
183
-
184
185
  def predict(self, x=None, **kwargs):
185
186
  """Call super().predict with the right input arguments"""
186
187
  x = self._parse_input(x)
@@ -408,7 +409,7 @@ class MetaModel(Model):
408
409
  raise ValueError("Trying to generate single replica models with no generator set.")
409
410
  replicas = []
410
411
  for i_replica in range(self.num_replicas):
411
- replica = self.single_replica_generator()
412
+ replica = self.single_replica_generator(i_replica)
412
413
  replica.set_replica_weights(self.get_replica_weights(i_replica))
413
414
  replicas.append(replica)
414
415
 
@@ -496,9 +497,9 @@ def get_layer_replica_weights(layer, i_replica: int):
496
497
  """
497
498
  if is_stacked_single_replicas(layer):
498
499
  weights_ref = layer.get_layer(f"{NN_PREFIX}_{i_replica}").weights
499
- weights = [Variable(w, name=w.name) for w in weights_ref]
500
+ weights = [ops.variable_to_numpy(w) for w in weights_ref]
500
501
  else:
501
- weights = [Variable(w[i_replica : i_replica + 1], name=w.name) for w in layer.weights]
502
+ weights = [ops.variable_to_numpy(w)[i_replica : i_replica + 1] for w in layer.weights]
502
503
 
503
504
  return weights
504
505
 
@@ -1,20 +1,21 @@
1
1
  """
2
- Callbacks to be used during training
2
+ Callbacks to be used during training
3
3
 
4
- The callbacks defined in this module can be passed to the ``callbacks`` argument
5
- of the ``perform_fit`` method as a list.
4
+ The callbacks defined in this module can be passed to the ``callbacks`` argument
5
+ of the ``perform_fit`` method as a list.
6
6
 
7
- For the most typical usage: ``on_batch_end``,
8
- they must take as input an epoch number and a log of the partial losses.
7
+ For the most typical usage: ``on_batch_end``,
8
+ they must take as input an epoch number and a log of the partial losses.
9
9
 
10
- Note: the terminology used everywhere refers to a single training step as a single epoch.
11
- It turns out that to avoid tensorflow overhead, it is beneficial to write a step as a
12
- single batch instead. So callbacks must use ``on_batch_end``.
10
+ Note: the terminology used everywhere refers to a single training step as a single epoch.
11
+ It turns out that to avoid tensorflow overhead, it is beneficial to write a step as a
12
+ single batch instead. So callbacks must use ``on_batch_end``.
13
13
  """
14
14
 
15
15
  import logging
16
16
  from time import time
17
17
 
18
+ from keras import backend as K
18
19
  from keras.callbacks import Callback, TensorBoard
19
20
  import numpy as np
20
21
 
@@ -130,6 +131,13 @@ class StoppingCallback(CallbackStep):
130
131
  print_stats = ((epoch + 1) % self.log_freq) == 0
131
132
  # Note that the input logs correspond to the fit before the weights are updated
132
133
  logs = self.correct_logs(logs)
134
+
135
+ # WARNING: this line seems to be necessary for jax
136
+ # otherwise the validation model itself cannot run compute_losses
137
+ # but it needs to be run every epoch, which makes no sense
138
+ if K.backend() == "jax":
139
+ _ = self.model.compute_losses()
140
+
133
141
  self.stopping_object.monitor_chi2(logs, epoch, print_stats=print_stats)
134
142
  if self.stopping_object.stop_here():
135
143
  self.model.stop_training = True
@@ -1,5 +1,5 @@
1
1
  """
2
- Library of functions that modify the internal state of Keras/Tensorflow
2
+ Library of functions that modify the internal state of Keras/Tensorflow
3
3
  """
4
4
 
5
5
  import os
@@ -21,7 +21,8 @@ import numpy as np
21
21
  log = logging.getLogger(__name__)
22
22
 
23
23
  # Prepare Keras-backend dependent functions
24
- if K.backend() in ("torch", "jax"):
24
+ if (kback := K.backend()) == "torch":
25
+
25
26
  import torch
26
27
 
27
28
  def set_eager(flag=True):
@@ -56,6 +57,16 @@ elif K.backend() == "tensorflow":
56
57
  "Could not set tensorflow parallelism settings from n3fit, maybe tensorflow is already initialized by a third program"
57
58
  )
58
59
 
60
+ elif K.backend() == "jax":
61
+
62
+ import jax
63
+
64
+ def set_eager(flag=True):
65
+ pass
66
+
67
+ def set_threading(threads, core):
68
+ pass
69
+
59
70
  else:
60
71
  # Keras should've failed by now, if it doesn't it could be a new backend that works ootb?
61
72
  log.warning(f"Backend {K.backend()} not recognized. You are entering uncharted territory")
@@ -1,29 +1,29 @@
1
1
  """
2
- This module contains the list of operations that can be used within the
3
- ``call`` method of the ``n3fit`` layers as well as operations that can
4
- act on layers.
5
-
6
- This includes an implementation of the NNPDF operations on fktable in the keras
7
- language (with the mapping ``c_to_py_fun``) into Keras ``Lambda`` layers.
8
-
9
- The rest of the operations in this module are divided into four categories:
10
- numpy to tensor:
11
- Operations that take a numpy array and return a tensorflow tensor
12
- layer to layer:
13
- Operations that take a layer and return another layer
14
- tensor to tensor:
15
- Operations that take a tensor and return a tensor
16
- layer generation:
17
- Instanciate a layer to be applied by the calling function
18
-
19
- Most of the operations in this module are just aliases to the backend
20
- (Keras in this case) so that, when implementing new backends, it is clear
21
- which operations may need to be overwritten.
22
- For a few selected operations, a more complicated wrapper to e.g., make
23
- them into layers or apply some default, is included.
24
-
25
- Note that tensor operations can also be applied to layers as the output of a layer is a tensor
26
- equally operations are automatically converted to layers when used as such.
2
+ This module contains the list of operations that can be used within the
3
+ ``call`` method of the ``n3fit`` layers as well as operations that can
4
+ act on layers.
5
+
6
+ This includes an implementation of the NNPDF operations on fktable in the keras
7
+ language (with the mapping ``c_to_py_fun``) into Keras ``Lambda`` layers.
8
+
9
+ The rest of the operations in this module are divided into four categories:
10
+ numpy to tensor:
11
+ Operations that take a numpy array and return a tensorflow tensor
12
+ layer to layer:
13
+ Operations that take a layer and return another layer
14
+ tensor to tensor:
15
+ Operations that take a tensor and return a tensor
16
+ layer generation:
17
+ Instanciate a layer to be applied by the calling function
18
+
19
+ Most of the operations in this module are just aliases to the backend
20
+ (Keras in this case) so that, when implementing new backends, it is clear
21
+ which operations may need to be overwritten.
22
+ For a few selected operations, a more complicated wrapper to e.g., make
23
+ them into layers or apply some default, is included.
24
+
25
+ Note that tensor operations can also be applied to layers as the output of a layer is a tensor
26
+ equally operations are automatically converted to layers when used as such.
27
27
  """
28
28
 
29
29
  from keras import backend as K
@@ -70,12 +70,12 @@ elif K.backend() == "jax":
70
70
  decorator_compiler = lambda f: f
71
71
  elif K.backend() == "tensorflow":
72
72
  tensor_to_numpy_or_python = lambda x: x.numpy()
73
- lambda ret: {k: i.numpy() for k, i in ret.items()}
74
73
  import tensorflow as tf
75
74
 
76
75
  decorator_compiler = tf.function
77
76
 
78
77
  dict_to_numpy_or_python = lambda ret: {k: tensor_to_numpy_or_python(i) for k, i in ret.items()}
78
+ variable_to_numpy = lambda x: x.numpy()
79
79
 
80
80
 
81
81
  def as_layer(operation, op_args=None, op_kwargs=None, **kwargs):
@@ -17,10 +17,10 @@ import copy
17
17
  import logging
18
18
  import os
19
19
 
20
- import hyperopt
21
20
  from hyperopt.pyll.base import scope
22
21
  import numpy as np
23
22
 
23
+ import hyperopt
24
24
  from n3fit.backends import MetaLayer, MetaModel
25
25
  from n3fit.hyper_optimization.filetrials import FileTrials
26
26
 
@@ -125,11 +125,6 @@ def hyper_scan_wrapper(replica_path_set, model_trainer, hyperscanner, max_evals=
125
125
  a ``HyperScanner`` object defining the scan
126
126
  max_evals: int
127
127
  Number of trials to run
128
-
129
- Returns
130
- -------
131
- dict
132
- parameters of the best trial as found by ``hyperopt``
133
128
  """
134
129
  # Tell the trainer we are doing hpyeropt
135
130
  model_trainer.set_hyperopt(True, keys=hyperscanner.hyper_keys)
@@ -181,14 +176,13 @@ def hyper_scan_wrapper(replica_path_set, model_trainer, hyperscanner, max_evals=
181
176
  )
182
177
  if hyperscanner.parallel_hyperopt:
183
178
  trials.start_mongo_workers()
184
- best = hyperopt.fmin(**fmin_args, show_progressbar=True, max_queue_len=trials.num_workers)
179
+ hyperopt.fmin(**fmin_args, show_progressbar=True, max_queue_len=trials.num_workers)
185
180
  trials.stop_mongo_workers()
186
181
  # stop mongod command and compress database
187
182
  hyperscanner.mongod_runner.stop(mongod)
188
183
  trials.compress_mongodb_database()
189
184
  else:
190
- best = hyperopt.fmin(**fmin_args, show_progressbar=False, trials_save_file=trials.pkl_file)
191
- return hyperscanner.space_eval(best)
185
+ hyperopt.fmin(**fmin_args, show_progressbar=False, trials_save_file=trials.pkl_file)
192
186
 
193
187
 
194
188
  class ActivationStr:
@@ -18,6 +18,7 @@ And return a float to be added to the hyperscan loss.
18
18
  New penalties can be added directly in this module.
19
19
  The name in the runcard must match the name used in this module.
20
20
  """
21
+
21
22
  import numpy as np
22
23
 
23
24
  from n3fit.vpinterface import N3PDF, integrability_numbers
@@ -48,11 +49,12 @@ def saturation(pdf_model=None, n=100, min_x=1e-6, max_x=1e-4, flavors=None, **_k
48
49
  Example
49
50
  -------
50
51
  >>> from n3fit.hyper_optimization.penalties import saturation
51
- >>> from n3fit.model_gen import pdfNN_layer_generator
52
+ >>> from n3fit.model_gen import generate_pdf_model, ReplicaSettings
52
53
  >>> fake_fl = [{'fl' : i, 'largex' : [0,1], 'smallx': [1,2]} for i in ['u', 'ubar', 'd', 'dbar', 'c', 'g', 's', 'sbar']]
53
- >>> pdf_model = pdfNN_layer_generator(nodes=[8], activations=['linear'], seed=0, flav_info=fake_fl, fitbasis="FLAVOUR")
54
- >>> isinstance(saturation(pdf_model, 5), float)
55
- True
54
+ >>> rp = [ReplicaSettings(nodes = [8], activations=["linear"], seed=0)]
55
+ >>> pdf_model = generate_pdf_model(rp, flav_info=fake_fl, fitbasis="FLAVOUR")
56
+ >>> saturation(pdf_model, 5)
57
+ array([0.00014878])
56
58
 
57
59
  """
58
60
  if flavors is None:
@@ -128,11 +130,12 @@ def integrability(pdf_model=None, **_kwargs):
128
130
  Example
129
131
  -------
130
132
  >>> from n3fit.hyper_optimization.penalties import integrability
131
- >>> from n3fit.model_gen import pdfNN_layer_generator
133
+ >>> from n3fit.model_gen import generate_pdf_model, ReplicaSettings
132
134
  >>> fake_fl = [{'fl' : i, 'largex' : [0,1], 'smallx': [1,2]} for i in ['u', 'ubar', 'd', 'dbar', 'c', 'g', 's', 'sbar']]
133
- >>> pdf_model = pdfNN_layer_generator(nodes=[8], activations=['linear'], seed=0, flav_info=fake_fl, fitbasis="FLAVOUR")
134
- >>> isinstance(integrability(pdf_model), float)
135
- True
135
+ >>> rp = [ReplicaSettings(nodes = [8], activations=["linear"], seed=0)]
136
+ >>> pdf_model = generate_pdf_model(rp, flav_info=fake_fl, fitbasis="FLAVOUR")
137
+ >>> integrability(pdf_model)
138
+ 5.184705528587072e+21
136
139
 
137
140
  """
138
141
  pdf_instance = N3PDF(pdf_model.split_replicas())
@@ -36,7 +36,7 @@ from typing import Callable
36
36
 
37
37
  import numpy as np
38
38
 
39
- from n3fit.vpinterface import N3PDF, compute_phi
39
+ from n3fit.vpinterface import N3PDF, HyperoptMetrics, compute_hyperopt_metrics
40
40
  from validphys.core import DataGroupSpec
41
41
  from validphys.pdfgrids import distance_grids, xplotting_grid
42
42
 
@@ -103,7 +103,7 @@ IMPLEMENTED_STATS = {
103
103
  "best_worst": _best_worst,
104
104
  "std": _std,
105
105
  }
106
- IMPLEMENTED_LOSSES = ["chi2", "phi2"]
106
+ IMPLEMENTED_LOSSES = ["chi2", "phi2", "logp", "chi2p"]
107
107
 
108
108
 
109
109
  def _pdfs_to_n3pdfs(pdfs_per_fold):
@@ -156,8 +156,10 @@ class HyperLoss:
156
156
 
157
157
  self.loss_type = self._parse_loss(loss_type)
158
158
 
159
- self.phi2_vector = []
160
- self.chi2_matrix = []
159
+ self.hyper_chi2_vector = []
160
+ self.hyper_phi2_vector = []
161
+ self.hyper_logp_vector = []
162
+ self.exp_chi2_matrix = []
161
163
  self.penalties = {}
162
164
 
163
165
  self.reduce_over_replicas = self._parse_statistic(replica_statistic, "replica")
@@ -167,7 +169,7 @@ class HyperLoss:
167
169
  self,
168
170
  penalties: dict[str, np.ndarray],
169
171
  validation_loss: np.ndarray,
170
- kfold_loss: np.ndarray,
172
+ experimental_loss: np.ndarray,
171
173
  pdf_object: N3PDF,
172
174
  experimental_data: list[DataGroupSpec],
173
175
  fold_idx: int = 0,
@@ -192,8 +194,6 @@ class HyperLoss:
192
194
  List of tuples containing `validphys.core.DataGroupSpec` instances for each group data set
193
195
  fold_idx: int
194
196
  k-fold index. Defaults to 0.
195
- include_penalties: float
196
- Whether to include the penalties in the returned loss value
197
197
 
198
198
  Returns
199
199
  -------
@@ -213,7 +213,7 @@ class HyperLoss:
213
213
  >>> ds = Loader().check_dataset("NMC_NC_NOTFIXED_P_EM-SIGMARED", variant="legacy", theoryid=399, cuts="internal")
214
214
  >>> experimental_data = [Loader().check_experiment("My DataGroupSpec", [ds])]
215
215
  >>> fake_fl = [{'fl' : i, 'largex' : [0,1], 'smallx': [1,2]} for i in ['u', 'ubar', 'd', 'dbar', 'c', 'g', 's', 'sbar']]
216
- >>> pdf_model = generate_pdf_model(nodes=[8], activations=['linear'], seed=0, num_replicas=2, flav_info=fake_fl, fitbasis="FLAVOUR")
216
+ >>> pdf_model = generate_pdf_model(nodes=[8], activations=['linear'], seed=[0,2], flav_info=fake_fl, fitbasis="FLAVOUR")
217
217
  >>> pdf = N3PDF(pdf_model.split_replicas())
218
218
  >>> loss = hyper.compute_loss(penalties, experimental_loss, pdf, experimental_data)
219
219
  """
@@ -225,21 +225,38 @@ class HyperLoss:
225
225
  best_indexes = np.argsort(validation_loss, axis=0)[:num_best]
226
226
  best_validation_losses = validation_loss[best_indexes]
227
227
 
228
- # calculate phi for a given k-fold using vpinterface and validphys
228
+ # Select the `N3PDF` models to be used to compute the hyperopt metrics. The models
229
+ # are selected based on the validation losses using `self._proportion`.
229
230
  pdf_object_reduced = pdf_object.select_models(best_indexes)
230
- phi2_per_fold = compute_phi(pdf_object_reduced, experimental_data) ** 2
231
231
 
232
- # update hyperopt metrics
233
- # these are saved in the `phi2_vector` and `chi2_matrix` attributes, excluding penalties
234
- self._save_hyperopt_metrics(phi2_per_fold, kfold_loss, penalties, fold_idx)
232
+ # Compute the different hyperopt metrics
233
+ hypermetics: HyperoptMetrics = compute_hyperopt_metrics(
234
+ n3pdf=pdf_object_reduced, experimental_data=experimental_data
235
+ )
236
+
237
+ # Extract & save the values of the hyperopt metrics
238
+ hyper_chi2_per_fold = hypermetics.chi2 # computed with PDF covmat
239
+ hyper_phi2_per_fold = hypermetics.phi2 # computed without PDF covmat
240
+ hyper_logp_per_fold = hypermetics.logp # computed with PDF covmat
241
+
242
+ # Update hyperopt metrics history
243
+ self._save_hyperopt_metrics(
244
+ hyper_chi2_per_fold,
245
+ hyper_phi2_per_fold,
246
+ hyper_logp_per_fold,
247
+ experimental_loss,
248
+ penalties,
249
+ fold_idx,
250
+ )
235
251
 
236
252
  # Prepare the output loss, including penalties if necessary
237
253
  if self._penalties_in_loss:
238
254
  # include penalties to experimental loss
239
- kfold_loss += sum(penalties.values())
240
-
241
- # add penalties to phi in the form of a sum of per-replicas averages
242
- phi2_per_fold += sum(np.mean(penalty) for penalty in penalties.values())
255
+ experimental_loss += sum(penalties.values())
256
+ # add penalties to `phi2` and `logp` in the form of a sum of per-replicas averages
257
+ sum_penalties = sum(np.mean(penalty) for penalty in penalties.values())
258
+ hyper_phi2_per_fold += sum_penalties
259
+ hyper_logp_per_fold += sum_penalties
243
260
 
244
261
  # define loss for hyperopt according to the chosen loss_type
245
262
  if self.loss_type == "chi2":
@@ -247,38 +264,48 @@ class HyperLoss:
247
264
 
248
265
  # Construct the final loss as a sum of:
249
266
  # 1. The validation chi2
250
- # 2. The distance to 2 for the kfold chi2
267
+ # 2. The distance to 2 for the experimental chi2
251
268
  # In the hyperopt paper we used 80% and 10% respectively, as a proxy for:
252
269
  # "80% of the replicas should be good, but only a small % has to cover the folds"
253
270
  # Currently take reduce_proportion for a) and 1.0 - reduce_proportion for b)
254
271
  validation_loss_average = self.reduce_over_replicas(best_validation_losses)
255
272
 
256
- nselect = int(np.ceil((1.0 - self._proportion) * len(kfold_loss)))
257
- best_kfold_losses = np.sort(kfold_loss, axis=0)[:nselect]
258
- kfold_loss_average = self.reduce_over_replicas(best_kfold_losses)
273
+ nselect = int(np.ceil((1.0 - self._proportion) * len(experimental_loss)))
274
+ best_exp_losses = np.sort(experimental_loss, axis=0)[:nselect]
275
+ exp_loss_average = self.reduce_over_replicas(best_exp_losses)
259
276
 
260
- loss = validation_loss_average + (max(kfold_loss_average, 2.0) - 2.0)
277
+ loss = validation_loss_average + (max(exp_loss_average, 2.0) - 2.0)
261
278
  elif self.loss_type == "phi2":
262
- loss = phi2_per_fold
279
+ loss = hyper_phi2_per_fold
280
+ elif self.loss_type == "logp":
281
+ loss = hyper_logp_per_fold
282
+ elif self.loss_type == "chi2p":
283
+ loss = hyper_chi2_per_fold
263
284
 
264
285
  return loss
265
286
 
266
287
  def _save_hyperopt_metrics(
267
288
  self,
268
- phi2_per_fold: float,
269
- chi2_per_fold: np.ndarray,
289
+ hyper_chi2_per_fold: float,
290
+ hyper_phi2_per_fold: float,
291
+ hyper_logp_per_fold: float,
292
+ exp_chi2_per_fold: np.ndarray,
270
293
  penalties: dict[str, np.ndarray],
271
294
  fold_idx: int = 0,
272
295
  ) -> None:
273
296
  """
274
- Save all chi2 and phi calculated metrics per replica and per fold, including penalties.
297
+ Save all the calculated metrics per replica and per fold, including penalties.
275
298
 
276
299
  Parameters
277
300
  ----------
278
- phi2_per_fold: float
301
+ hyper_chi2_per_fold: float
302
+ Computed chi2 for a given k-fold
303
+ hyper_phi2_per_fold: float
279
304
  Computed phi2 for a given k-fold
280
- chi2_per_fold: np.ndarray
281
- Computed chi2 for each replica for a given k-fold
305
+ hyper_logp_per_fold: float
306
+ Computed logp for a given k-fold
307
+ exp_chi2_per_fold: np.ndarray
308
+ Computed experimental chi2 for all the replica for a given k-fold
282
309
  penalties: Dict[str, np.ndarray]
283
310
  dictionary of all penalties with their names
284
311
  fold_idx: int
@@ -286,13 +313,17 @@ class HyperLoss:
286
313
  """
287
314
  # reset chi2 and phi arrays for every trial
288
315
  if fold_idx == 0:
289
- self.phi2_vector = []
290
- self.chi2_matrix = []
316
+ self.hyper_chi2_vector = []
317
+ self.hyper_phi2_vector = []
318
+ self.hyper_logp_vector = []
319
+ self.exp_chi2_matrix = []
291
320
  self.penalties = {}
292
321
 
293
322
  # populate chi2 matrix and phi vector calculated for a given k-fold
294
- self.chi2_matrix.append(chi2_per_fold)
295
- self.phi2_vector.append(phi2_per_fold)
323
+ self.hyper_chi2_vector.append(hyper_chi2_per_fold)
324
+ self.hyper_phi2_vector.append(hyper_phi2_per_fold)
325
+ self.hyper_logp_vector.append(hyper_logp_per_fold)
326
+ self.exp_chi2_matrix.append(exp_chi2_per_fold)
296
327
 
297
328
  # save penalties per replica for a given k-fold
298
329
  for name, values in penalties.items():
@@ -366,7 +397,7 @@ class HyperLoss:
366
397
 
367
398
  selected_statistic = IMPLEMENTED_STATS[statistic]
368
399
 
369
- if self.loss_type == "chi2":
400
+ if self.loss_type == "chi2" or self.loss_type == "logp" or self.loss_type == "chi2p":
370
401
  return selected_statistic
371
402
  elif self.loss_type == "phi2":
372
403
  # In case of phi2, calculate the inverse of the applied statistics