optima-ml 0.3.4a2__tar.gz → 0.3.5.dev1__tar.gz

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 (54) hide show
  1. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/builtin/evaluation.py +163 -86
  2. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/builtin/figures_of_merit.py +32 -20
  3. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/builtin/inputs.py +276 -512
  4. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/core/evaluation.py +471 -407
  5. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/core/inputs.py +33 -91
  6. optima_ml-0.3.5.dev1/OPTIMA/core/model.py +1265 -0
  7. optima_ml-0.3.5.dev1/OPTIMA/core/tools.py +211 -0
  8. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/core/training.py +168 -296
  9. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/core/variable_optimization.py +339 -234
  10. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/defaults.py +7 -0
  11. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/helpers/manage_ray_nodes.py +9 -7
  12. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/keras/model.py +45 -34
  13. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/keras/tools.py +109 -66
  14. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/keras/training.py +17 -18
  15. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/lightning/inputs.py +67 -67
  16. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/lightning/training.py +26 -52
  17. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/optima.py +534 -550
  18. optima_ml-0.3.5.dev1/OPTIMA/test.py +58 -0
  19. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/PKG-INFO +40 -20
  20. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/README.md +27 -19
  21. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/optima_ml.egg-info/PKG-INFO +40 -20
  22. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/optima_ml.egg-info/SOURCES.txt +1 -1
  23. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/tests/test_builtin.py +10 -9
  24. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/tests/test_core.py +0 -2
  25. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/tests/test_integration.py +0 -12
  26. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/tests/test_preprocessing.py +281 -419
  27. optima_ml-0.3.4a2/OPTIMA/core/model.py +0 -306
  28. optima_ml-0.3.4a2/OPTIMA/core/tools.py +0 -106
  29. optima_ml-0.3.4a2/tests/test_integration_sameMachine.py +0 -154
  30. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/LICENSE +0 -0
  31. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/__init__.py +0 -0
  32. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/__main__.py +0 -0
  33. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/builtin/__init__.py +0 -0
  34. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/builtin/model.py +0 -0
  35. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/builtin/search_space.py +0 -0
  36. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/core/__init__.py +0 -0
  37. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/core/search_space.py +0 -0
  38. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/hardware_configs/Dresden_Taurus.py +0 -0
  39. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/hardware_configs/__init__.py +0 -0
  40. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/hardware_configs/common.py +0 -0
  41. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/hardware_configs/helpers.py +0 -0
  42. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/helpers/__init__.py +0 -0
  43. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/helpers/extract_data_from_NTuples.py +0 -0
  44. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/keras/__init__.py +0 -0
  45. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/lightning/__init__.py +0 -0
  46. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/resources/__init__.py +0 -0
  47. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/resources/config_verification.py +0 -0
  48. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/OPTIMA/resources/pbt_with_seed.py +0 -0
  49. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/optima_ml.egg-info/dependency_links.txt +0 -0
  50. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/optima_ml.egg-info/entry_points.txt +0 -0
  51. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/optima_ml.egg-info/requires.txt +0 -0
  52. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/optima_ml.egg-info/top_level.txt +0 -0
  53. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/setup.cfg +0 -0
  54. {optima_ml-0.3.4a2 → optima_ml-0.3.5.dev1}/setup.py +0 -0
@@ -9,8 +9,6 @@ import matplotlib.pyplot as plt
9
9
  import seaborn as sns
10
10
  from sklearn.metrics import roc_curve, auc
11
11
 
12
- import ray
13
-
14
12
  import OPTIMA.core.model
15
13
  import OPTIMA.core.evaluation
16
14
 
@@ -18,14 +16,14 @@ import OPTIMA.core.evaluation
18
16
  def evaluate(
19
17
  run_config,
20
18
  model_path,
21
- inputs_split,
22
- targets_split,
23
- weights_split,
24
- normalized_weights_split,
19
+ model_config,
20
+ dataset_split,
21
+ input_handler,
25
22
  fig_dir,
26
23
  native_metrics=None,
27
24
  weighted_native_metrics=None,
28
- custom_FoMs=None,
25
+ custom_metrics=None,
26
+ composite_metrics=None,
29
27
  class_labels=None,
30
28
  cpus=1,
31
29
  results_dir=None,
@@ -34,7 +32,9 @@ def evaluate(
34
32
  return_unfilled=False,
35
33
  ratio=True,
36
34
  ):
37
- """_summary_.
35
+ """Built-in evaluation for classification tasks.
36
+
37
+ This function assumes that the entire dataset and the model predictions can fit into memory.
38
38
 
39
39
  Parameters
40
40
  ----------
@@ -42,13 +42,11 @@ def evaluate(
42
42
  _description_
43
43
  model_path : _type_
44
44
  _description_
45
- inputs_split : _type_
46
- _description_
47
- targets_split : _type_
45
+ model_config : _type_
48
46
  _description_
49
- weights_split : _type_
47
+ dataset_split : _type_
50
48
  _description_
51
- normalized_weights_split : _type_
49
+ input_handler : _type_
52
50
  _description_
53
51
  fig_dir : _type_
54
52
  _description_
@@ -56,8 +54,10 @@ def evaluate(
56
54
  _description_ (Default value = [])
57
55
  weighted_native_metrics : _type_
58
56
  _description_ (Default value = [])
59
- custom_FoMs : _type_
57
+ custom_metrics : _type_
60
58
  _description_ (Default value = [])
59
+ composite_metrics : _type_
60
+ _description_ (Default value = None)
61
61
  class_labels : _type_
62
62
  _description_ (Default value = None)
63
63
  cpus : _type_
@@ -78,34 +78,62 @@ def evaluate(
78
78
  _type_
79
79
  _description_
80
80
  """
81
- if custom_FoMs is None:
82
- custom_FoMs = []
83
- if weighted_native_metrics is None:
84
- weighted_native_metrics = []
85
81
  if native_metrics is None:
86
82
  native_metrics = []
87
-
88
- # fetch the inputs from the object store
89
- if len(inputs_split) == 2:
83
+ if weighted_native_metrics is None:
84
+ weighted_native_metrics = []
85
+ if custom_metrics is None:
86
+ custom_metrics = []
87
+ if composite_metrics is None:
88
+ composite_metrics = []
89
+
90
+ # fetch the target labels and optional sample weights --> this saves everything into this worker's memory. If OOM
91
+ # error occurs, define an evaluate-function in the run-config.
92
+ if len(dataset_split) == 2:
90
93
  explicit_testing_dataset = False
91
- inputs_train, inputs_val = ray.get(inputs_split)
92
- targets_train, targets_val = ray.get(targets_split)
93
- weights_train, weights_val = ray.get(weights_split)
94
- normalized_weights_train, normalized_weights_val = ray.get(normalized_weights_split)
94
+ dataset_train, dataset_val = dataset_split
95
+ train_data = list(dataset_train.iter_batches(batch_size=dataset_train.count()))[0]
96
+ val_data = list(dataset_val.iter_batches(batch_size=dataset_val.count()))[0]
97
+ targets_train = train_data["Target"]
98
+ targets_val = val_data["Target"]
99
+ if "ScaledWeight" in dataset_train.columns():
100
+ weights_train = train_data["ScaledWeight"]
101
+ weights_val = val_data["ScaledWeight"]
102
+ elif "Weight" in dataset_train.columns():
103
+ weights_train = train_data["Weight"]
104
+ weights_val = val_data["Weight"]
105
+ else:
106
+ weights_train = np.ones((targets_train.shape[0],))
107
+ weights_val = np.ones((targets_val.shape[0],))
95
108
  print(
96
109
  "testing model using {} training and {} validation events".format(
97
- inputs_train.shape[0], inputs_val.shape[0]
110
+ targets_train.shape[0], targets_val.shape[0]
98
111
  )
99
112
  )
100
113
  else:
101
114
  explicit_testing_dataset = True
102
- inputs_train, inputs_val, inputs_test = ray.get(inputs_split)
103
- targets_train, targets_val, targets_test = ray.get(targets_split)
104
- weights_train, weights_val, weights_test = ray.get(weights_split)
105
- normalized_weights_train, normalized_weights_val, normalized_weights_test = ray.get(normalized_weights_split)
115
+ dataset_train, dataset_val, dataset_test = dataset_split
116
+ train_data = list(dataset_train.iter_batches(batch_size=dataset_train.count()))[0]
117
+ val_data = list(dataset_val.iter_batches(batch_size=dataset_val.count()))[0]
118
+ test_data = list(dataset_test.iter_batches(batch_size=dataset_test.count()))[0]
119
+ targets_train = train_data["Target"]
120
+ targets_val = val_data["Target"]
121
+ targets_test = test_data["Target"]
122
+ if "ScaledWeight" in dataset_train.columns():
123
+ weights_train = train_data["ScaledWeight"]
124
+ weights_val = val_data["ScaledWeight"]
125
+ weights_test = test_data["ScaledWeight"]
126
+ elif "Weight" in dataset_train.columns():
127
+ weights_train = train_data["Weight"]
128
+ weights_val = val_data["Weight"]
129
+ weights_test = test_data["Weight"]
130
+ else:
131
+ weights_train = np.ones((targets_train.shape[0],))
132
+ weights_val = np.ones((targets_val.shape[0],))
133
+ weights_test = np.ones((targets_test.shape[0],))
106
134
  print(
107
135
  "testing model using {} training, {} validation and {} testing events".format(
108
- inputs_train.shape[0], inputs_val.shape[0], inputs_test.shape[0]
136
+ targets_train.shape[0], targets_val.shape[0], targets_test.shape[0]
109
137
  )
110
138
  )
111
139
 
@@ -116,12 +144,19 @@ def evaluate(
116
144
  if not os.path.exists(results_dir):
117
145
  os.makedirs(results_dir, exist_ok=True)
118
146
 
119
- # load the model and get the model predictions
120
- model = OPTIMA.core.model.load_model(run_config, model_path, cpus)
121
- pred_train = model.predict(inputs_train, verbose=0)
122
- pred_val = model.predict(inputs_val, verbose=0)
147
+ # load the model, prepare the datasets (i.e. convert from ray datasets to ML native datasets) and get the model
148
+ # predictions
149
+ model = OPTIMA.core.model.load_model(run_config, model_config, input_handler, model_path, cpus)
150
+ native_datasets = model.prepare_datasets(
151
+ dataset_train,
152
+ dataset_val,
153
+ dataset_test if explicit_testing_dataset else None,
154
+ )
155
+ preds = model.predict(native_datasets, verbose=0)
123
156
  if explicit_testing_dataset:
124
- pred_test = model.predict(inputs_test, verbose=0)
157
+ pred_train, pred_val, pred_test = preds
158
+ else:
159
+ pred_train, pred_val = preds
125
160
  num_outputs = pred_train.shape[1]
126
161
 
127
162
  # check if we have binary or multiclass classification
@@ -715,88 +750,130 @@ def evaluate(
715
750
  if explicit_testing_dataset:
716
751
  results_string_args.append(auc_test_classes[i])
717
752
 
753
+ # keep track of the calculated metrics to evaluate composite metrics
754
+ metric_values_dict = {}
755
+
718
756
  # loss
719
757
  results_string += " Loss:\n"
720
- train_loss = model.loss(
721
- inputs=inputs_train, y_true=targets_train, sample_weight=normalized_weights_train, y_pred=pred_train
722
- )
723
- val_loss = model.loss(inputs=inputs_val, y_true=targets_val, sample_weight=normalized_weights_val, y_pred=pred_val)
758
+ losses = model.loss(native_datasets)
724
759
  if explicit_testing_dataset:
725
- test_loss = model.loss(
726
- inputs=inputs_test, y_true=targets_test, sample_weight=normalized_weights_test, y_pred=pred_test
727
- )
760
+ train_loss, val_loss, test_loss = losses
761
+ else:
762
+ train_loss, val_loss = losses
728
763
  results_string += "\ttraining: {}\n".format("{:.3f}")
729
764
  results_string += "\tvalidation: {}\n".format("{:.3f}")
730
765
  if explicit_testing_dataset:
731
766
  results_string += "\ttesting: {}\n".format("{:.3f}")
732
- results_string_args += [train_loss, val_loss, test_loss] if explicit_testing_dataset else [train_loss, val_loss]
767
+ metric_values_dict["loss"] = train_loss
768
+ metric_values_dict["val_loss"] = val_loss
769
+ metric_values_dict["test_loss"] = test_loss
770
+ results_string_args += [train_loss, val_loss, test_loss]
771
+ else:
772
+ metric_values_dict["loss"] = train_loss
773
+ metric_values_dict["val_loss"] = val_loss
774
+ results_string_args += [train_loss, val_loss]
733
775
 
734
776
  if native_metrics != []:
735
777
  # instantiate native metrics
736
778
  native_metrics = [(name, metric(**kwargs)) for name, (metric, kwargs) in native_metrics]
737
779
 
780
+ # calculate the metric values
781
+ metric_values = model.calc_native_metrics(
782
+ native_metrics=[metric for (_, metric) in native_metrics],
783
+ data=native_datasets,
784
+ weighted=False,
785
+ )
786
+ if explicit_testing_dataset:
787
+ metric_values_train, metric_values_val, metric_values_test = metric_values
788
+ else:
789
+ metric_values_train, metric_values_val = metric_values
790
+
738
791
  results_string += " Native metrics:\n"
739
- for metric_name, metric in native_metrics:
740
- metric_value_train = OPTIMA.core.evaluation.calc_native_metric(
741
- run_config, metric, targets_train, pred_train
742
- )
743
- metric_value_val = OPTIMA.core.evaluation.calc_native_metric(run_config, metric, targets_val, pred_val)
792
+ for i, (metric_name, _) in enumerate(native_metrics):
793
+ metric_value_train = metric_values_train[i]
794
+ metric_value_val = metric_values_val[i]
744
795
  if explicit_testing_dataset:
745
- metric_value_test = OPTIMA.core.evaluation.calc_native_metric(
746
- run_config, metric, targets_test, pred_test
747
- )
796
+ metric_value_test = metric_values_test[i]
797
+
748
798
  results_string += "\t{} (training): {}\n".format(metric_name, "{:.3f}")
749
799
  results_string += "\t{} (validation): {}\n".format(metric_name, "{:.3f}")
750
800
  if explicit_testing_dataset:
751
801
  results_string += "\t{} (testing): {}\n".format(metric_name, "{:.3f}")
752
- results_string_args += (
753
- [metric_value_train, metric_value_val, metric_value_test]
754
- if explicit_testing_dataset
755
- else [metric_value_train, metric_value_val]
756
- )
802
+ metric_values_dict[f"{metric_name}"] = metric_value_train
803
+ metric_values_dict[f"val_{metric_name}"] = metric_value_val
804
+ metric_values_dict[f"test_{metric_name}"] = metric_value_test
805
+ results_string_args += [metric_value_train, metric_value_val, metric_value_test]
806
+ else:
807
+ metric_values_dict[f"{metric_name}"] = metric_value_train
808
+ metric_values_dict[f"val_{metric_name}"] = metric_value_val
809
+ results_string_args += [metric_value_train, metric_value_val]
757
810
 
758
811
  if weighted_native_metrics != []:
759
812
  # instantiate weighted native metrics
760
813
  weighted_native_metrics = [(name, metric(**kwargs)) for name, (metric, kwargs) in weighted_native_metrics]
761
814
 
815
+ # calculate the metric values
816
+ metric_values = model.calc_native_metrics(
817
+ native_metrics=[metric for (_, metric) in weighted_native_metrics],
818
+ data=native_datasets,
819
+ weighted=True,
820
+ )
821
+ if explicit_testing_dataset:
822
+ metric_values_train, metric_values_val, metric_values_test = metric_values
823
+ else:
824
+ metric_values_train, metric_values_val = metric_values
825
+
762
826
  results_string += " Weighted native metrics:\n"
763
- for metric_name, metric in weighted_native_metrics:
764
- metric_value_train = OPTIMA.core.evaluation.calc_native_metric(
765
- run_config, metric, targets_train, pred_train, sample_weight=normalized_weights_train
766
- )
767
- metric_value_val = OPTIMA.core.evaluation.calc_native_metric(
768
- run_config, metric, targets_val, pred_val, sample_weight=normalized_weights_val
769
- )
827
+ for i, (metric_name, _) in enumerate(weighted_native_metrics):
828
+ metric_value_train = metric_values_train[i]
829
+ metric_value_val = metric_values_val[i]
770
830
  if explicit_testing_dataset:
771
- metric_value_test = OPTIMA.core.evaluation.calc_native_metric(
772
- run_config, metric, targets_test, pred_test, sample_weight=normalized_weights_test
773
- )
831
+ metric_value_test = metric_values_test[i]
832
+
774
833
  results_string += "\t{} (training): {}\n".format(metric_name, "{:.3f}")
775
834
  results_string += "\t{} (validation): {}\n".format(metric_name, "{:.3f}")
776
835
  if explicit_testing_dataset:
777
836
  results_string += "\t{} (testing): {}\n".format(metric_name, "{:.3f}")
778
- results_string_args += (
779
- [metric_value_train, metric_value_val, metric_value_test]
780
- if explicit_testing_dataset
781
- else [metric_value_train, metric_value_val]
782
- )
837
+ metric_values_dict[f"{metric_name}"] = metric_value_train
838
+ metric_values_dict[f"val_{metric_name}"] = metric_value_val
839
+ metric_values_dict[f"test_{metric_name}"] = metric_value_test
840
+ results_string_args += [metric_value_train, metric_value_val, metric_value_test]
841
+ else:
842
+ metric_values_dict[f"{metric_name}"] = metric_value_train
843
+ metric_values_dict[f"val_{metric_name}"] = metric_value_val
844
+ results_string_args += [metric_value_train, metric_value_val]
783
845
 
784
- if custom_FoMs != []:
846
+ if custom_metrics != []:
785
847
  results_string += " Custom metrics:\n"
786
- for FoM_name, FoM_func in custom_FoMs:
787
- FoM_value_train = FoM_func(targets_train, pred_train, sample_weight=normalized_weights_train)
788
- FoM_value_val = FoM_func(targets_val, pred_val, sample_weight=normalized_weights_val)
789
- if explicit_testing_dataset:
790
- FoM_value_test = FoM_func(targets_test, pred_test, sample_weight=normalized_weights_test)
791
- results_string += "\t{} (training): {}\n".format(FoM_name, "{:.3f}")
792
- results_string += "\t{} (validation): {}\n".format(FoM_name, "{:.3f}")
848
+ custom_metric_values = model.calc_custom_metrics(
849
+ custom_metrics=[metric for (_, metric) in custom_metrics],
850
+ data=native_datasets,
851
+ skip_test_dataset=False,
852
+ )
853
+ for i, (metric_name, _) in enumerate(custom_metrics):
854
+ results_string += "\t{} (training): {}\n".format(metric_name, "{:.3f}")
855
+ results_string += "\t{} (validation): {}\n".format(metric_name, "{:.3f}")
793
856
  if explicit_testing_dataset:
794
- results_string += "\t{} (testing): {}\n".format(FoM_name, "{:.3f}")
795
- results_string_args += (
796
- [FoM_value_train, FoM_value_val, FoM_value_test]
797
- if explicit_testing_dataset
798
- else [FoM_value_train, FoM_value_val]
799
- )
857
+ results_string += "\t{} (testing): {}\n".format(metric_name, "{:.3f}")
858
+ metric_values_dict[f"train_{metric_name}"] = custom_metric_values[0][i]
859
+ metric_values_dict[f"val_{metric_name}"] = custom_metric_values[1][i]
860
+ metric_values_dict[f"test_{metric_name}"] = custom_metric_values[2][i]
861
+ results_string_args += [
862
+ custom_metric_values[0][i],
863
+ custom_metric_values[1][i],
864
+ custom_metric_values[2][i],
865
+ ]
866
+ else:
867
+ metric_values_dict[f"train_{metric_name}"] = custom_metric_values[0][i]
868
+ metric_values_dict[f"val_{metric_name}"] = custom_metric_values[1][i]
869
+ results_string_args += [custom_metric_values[0][i], custom_metric_values[1][i]]
870
+
871
+ if composite_metrics != []:
872
+ results_string += " Composite metrics:\n"
873
+ for metric_name, dep_metric_names, metric in composite_metrics:
874
+ dep_metric_values = (metric_values_dict[dep_metric_name] for dep_metric_name in dep_metric_names)
875
+ results_string += "\t{}: {}\n".format(metric_name, "{:.3f}")
876
+ results_string_args.append(metric(*dep_metric_values))
800
877
 
801
878
  if print_results:
802
879
  print(results_string.format(*results_string_args))
@@ -95,24 +95,28 @@ class FigureOfMerit:
95
95
  self.func = func
96
96
  self.kwargs = kwargs
97
97
 
98
- def __call__(self, y_true: np.ndarray, y_pred: np.ndarray, sample_weight: Optional[np.ndarray] = None) -> Any:
98
+ def __call__(self, inputs: tuple[np.ndarray], y_pred: np.ndarray) -> Any:
99
99
  """Gives arrays of target labels, predictions, sample weight and possible kwargs to self.func and returns the result.
100
100
 
101
101
  Parameters
102
102
  ----------
103
- y_true : np.ndarray
104
- Array of target labels.
103
+ inputs : tuple[np.ndarray]
104
+ A tuple of numpy arrays of length 2 or 3. The first two entries are assumed to be the input features and
105
+ the target labels. The third, optional entry is assumed to be the sample weights.
105
106
  y_pred : np.ndarray
106
107
  Array of predictions corresponding to the target labels.
107
- sample_weight : Optional[np.ndarray]
108
- Array of sample weights for each entry in y_true and y_pred. (Default value = None)
109
108
 
110
109
  Returns
111
110
  -------
112
111
  Any
113
112
  Return value of the figure of merit calculated by self.func.
114
113
  """
115
- return self.func(y_true, y_pred, sample_weight=sample_weight, **self.kwargs)
114
+ assert isinstance(inputs, tuple) or isinstance(inputs, list)
115
+ assert len(inputs) in [
116
+ 2,
117
+ 3,
118
+ ], f"An input-tuple or -list of length {len(inputs)} was provided, which is not supported."
119
+ return self.func(inputs[1], y_pred, sample_weight=inputs[2] if len(inputs) == 3 else None, **self.kwargs)
116
120
 
117
121
 
118
122
  class HistFigureOfMerit(FigureOfMerit):
@@ -132,7 +136,7 @@ class HistFigureOfMerit(FigureOfMerit):
132
136
  min_events_per_bin: Union[float, int] = 10.0,
133
137
  min_s_per_bin: Union[float, int] = 0,
134
138
  min_b_per_bin: Union[float, int] = 1.0,
135
- **kwargs: Any
139
+ **kwargs: Any,
136
140
  ) -> None:
137
141
  """Constructs a HistFigureOfMerit object.
138
142
 
@@ -169,24 +173,28 @@ class HistFigureOfMerit(FigureOfMerit):
169
173
  self.min_s_per_bin = min_s_per_bin
170
174
  self.min_b_per_bin = min_b_per_bin
171
175
 
172
- def __call__(self, y_true: np.ndarray, y_pred: np.ndarray, sample_weight: Optional[np.ndarray] = None) -> Any:
176
+ def __call__(self, inputs: tuple[np.ndarray], y_pred: np.ndarray) -> Any:
173
177
  """Calls the preprocess function to build the histograms, gives them to self.func and returns the result.
174
178
 
175
179
  Parameters
176
180
  ----------
177
- y_true : np.ndarray
178
- Array of target labels.
181
+ inputs : tuple[np.ndarray]
182
+ A tuple of numpy arrays of length 2 or 3. The first two entries are assumed to be the input features and
183
+ the target labels. The third, optional entry is assumed to be the sample weights.
179
184
  y_pred : np.ndarray
180
185
  Array of predictions corresponding to the target labels.
181
- sample_weight : Optional[np.ndarray]
182
- Array of sample weights for each entry in y_true and y_pred. (Default value = None)
183
186
 
184
187
  Returns
185
188
  -------
186
189
  Any
187
190
  Value of the figure of merit calculated by self.func.
188
191
  """
189
- s, b, bins = self.preprocess(y_true, y_pred, sample_weight=sample_weight)
192
+ assert isinstance(inputs, tuple) or isinstance(inputs, list)
193
+ assert len(inputs) in [
194
+ 2,
195
+ 3,
196
+ ], f"An input-tuple or -list of length {len(inputs)} was provided, which is not supported."
197
+ s, b, bins = self.preprocess(inputs[1], y_pred, sample_weight=inputs[2] if len(inputs) == 3 else None)
190
198
  return self.func(s, b, bins, **self.kwargs)
191
199
 
192
200
  def preprocess(
@@ -295,7 +303,7 @@ class NormHistFigureOfMerit(FigureOfMerit):
295
303
  min_events_per_bin: Union[float, int] = 0.0,
296
304
  min_s_per_bin: Union[float, int] = 0,
297
305
  min_b_per_bin: Union[float, int] = 0.0,
298
- **kwargs: Any
306
+ **kwargs: Any,
299
307
  ) -> None:
300
308
  """_summary_.
301
309
 
@@ -331,24 +339,28 @@ class NormHistFigureOfMerit(FigureOfMerit):
331
339
  self.min_s_per_bin = min_s_per_bin
332
340
  self.min_b_per_bin = min_b_per_bin
333
341
 
334
- def __call__(self, y_true: np.ndarray, y_pred: np.ndarray, sample_weight: Optional[np.ndarray] = None) -> Any:
342
+ def __call__(self, inputs: tuple[np.ndarray], y_pred: np.ndarray) -> Any:
335
343
  """Calls the preprocess function to build the histograms, gives them to self.func and returns the result.
336
344
 
337
345
  Parameters
338
346
  ----------
339
- y_true : np.ndarray
340
- Array of target labels.
347
+ inputs : tuple[np.ndarray]
348
+ A tuple of numpy arrays of length 2 or 3. The first two entries are assumed to be the input features and
349
+ the target labels. The third, optional entry is assumed to be the sample weights.
341
350
  y_pred : np.ndarray
342
351
  Array of predictions corresponding to the target labels.
343
- sample_weight : Optional[np.ndarray]
344
- Array of sample weights for each entry in y_true and y_pred. (Default value = None)
345
352
 
346
353
  Returns
347
354
  -------
348
355
  Any
349
356
  Value of the figure of merit calculated by self.func.
350
357
  """
351
- s, b, bins = self.preprocess(y_true, y_pred, sample_weight=sample_weight)
358
+ assert isinstance(inputs, tuple) or isinstance(inputs, list)
359
+ assert len(inputs) in [
360
+ 2,
361
+ 3,
362
+ ], f"An input-tuple or -list of length {len(inputs)} was provided, which is not supported."
363
+ s, b, bins = self.preprocess(inputs[1], y_pred, sample_weight=inputs[2] if len(inputs) == 3 else None)
352
364
  return self.func(s, b, bins, **self.kwargs)
353
365
 
354
366
  def preprocess(