Skip to content

Commit

Permalink
Avoid initialization of HyperLoss in checks
Browse files Browse the repository at this point in the history
  • Loading branch information
APJansen committed Mar 7, 2024
1 parent e996b95 commit 33b6d97
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 71 deletions.
8 changes: 4 additions & 4 deletions n3fit/src/n3fit/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from n3fit.hyper_optimization import penalties as penalties_module
from n3fit.hyper_optimization import rewards as rewards_module
from n3fit.hyper_optimization.rewards import HyperLoss
from n3fit.hyper_optimization.rewards import IMPLEMENTED_LOSSES, IMPLEMENTED_STATS
from reportengine.checks import CheckError, make_argcheck
from validphys.core import PDF
from validphys.pdfbases import check_basis
Expand Down Expand Up @@ -259,22 +259,22 @@ def check_kfold_options(kfold):

loss_type = kfold.get("loss_type")
if loss_type is not None:
if loss_type not in HyperLoss().implemented_losses:
if loss_type not in IMPLEMENTED_LOSSES:
raise CheckError(
f"Loss type '{loss_type}' is not recognized, "
"ensure it is implemented in the HyperLoss class in hyper_optimization/rewards.py."
"Options so far are 'chi2' or 'phi2'."
)
replica_statistic = kfold.get("replica_statistic")
if replica_statistic is not None:
if replica_statistic not in HyperLoss().implemented_stats:
if replica_statistic not in IMPLEMENTED_STATS:
raise CheckError(
f"The replica statistic '{replica_statistic}' is not recognized, "
"ensure it is implemented in the HyperLoss class in hyper_optimization/rewards.py"
)
fold_statistic = kfold.get("fold_statistic")
if fold_statistic is not None:
if fold_statistic not in HyperLoss().implemented_stats:
if fold_statistic not in IMPLEMENTED_STATS:
raise CheckError(
f"The fold statistic '{fold_statistic}' is not recognized, "
"ensure it is implemented in the HyperLoss class in hyper_optimization/rewards.py"
Expand Down
131 changes: 64 additions & 67 deletions n3fit/src/n3fit/hyper_optimization/rewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
- Detailed tracking and storage of loss metrics for further analysis.
New statistics can be added directly in this class as staticmethods and
via :attr:`~HyperLoss.implemented_stats`; their name in the runcard must
via `IMPLEMENTED_STATS`; their name in the runcard must
match the name in the module
Example
Expand Down Expand Up @@ -43,6 +43,64 @@
log = logging.getLogger(__name__)


def _average(fold_losses: np.ndarray, axis: int = 0) -> float:
"""
Compute the average of the input array along the specified axis.
Parameters
----------
fold_losses: np.ndarray
Input array.
axis: int, optional
Axis along which the mean is computed. Default is 0.
Returns
-------
float: The average along the specified axis.
"""
return np.average(fold_losses, axis=axis).item()


def _best_worst(fold_losses: np.ndarray, axis: int = 0) -> float:
"""
Compute the maximum value of the input array along the specified axis.
Parameters
----------
fold_losses: np.ndarray
Input array.
axis: int, optional
Axis along which the maximum is computed. Default is 0.
Returns
-------
float: The maximum value along the specified axis.
"""
return np.max(fold_losses, axis=axis).item()


def _std(fold_losses: np.ndarray, axis: int = 0) -> float:
"""
Compute the standard deviation of the input array along the specified axis.
Parameters
----------
fold_losses: np.ndarray
Input array.
axis: int, optional
Axis along which the standard deviation is computed. Default is 0.
Returns
-------
float: The standard deviation along the specified axis.
"""
return np.std(fold_losses, axis=axis).item()


IMPLEMENTED_STATS = {"average": _average, "best_worst": _best_worst, "std": _std}
IMPLEMENTED_LOSSES = ["chi2", "phi2"]


def _pdfs_to_n3pdfs(pdfs_per_fold):
"""Convert a list of multi-replica PDFs to a list of N3PDFs"""
return [N3PDF(pdf.split_replicas(), name=f"fold_{k}") for k, pdf in enumerate(pdfs_per_fold)]
Expand Down Expand Up @@ -71,13 +129,6 @@ class HyperLoss:
def __init__(
self, loss_type: str = None, replica_statistic: str = None, fold_statistic: str = None
):
self.implemented_stats = {
"average": self._average,
"best_worst": self._best_worst,
"std": self._std,
}
self.implemented_losses = ["chi2", "phi2"]

self._default_statistic = "average"
self._default_loss = "chi2"

Expand Down Expand Up @@ -218,8 +269,8 @@ def _parse_loss(self, loss_type: str) -> str:
loss_type = self._default_loss
log.warning(f"No loss_type selected in HyperLoss, defaulting to {loss_type}")
else:
if loss_type not in self.implemented_losses:
valid_options = ", ".join(self.implemented_losses)
if loss_type not in IMPLEMENTED_LOSSES:
valid_options = ", ".join(IMPLEMENTED_LOSSES)
raise ValueError(
f"Invalid loss type '{loss_type}'. Valid options are: {valid_options}"
)
Expand Down Expand Up @@ -255,15 +306,15 @@ def _parse_statistic(self, statistic: str, name: str) -> Callable:
statistic = self._default_statistic
log.warning(f"No {name} selected in HyperLoss, defaulting to {statistic}")
else:
if statistic not in self.implemented_stats:
valid_options = ", ".join(self.implemented_stats.keys())
if statistic not in IMPLEMENTED_STATS:
valid_options = ", ".join(IMPLEMENTED_STATS.keys())
raise ValueError(
f"Invalid {name} '{statistic}'. Valid options are: {valid_options}"
)

log.info(f"Using '{statistic}' as the {name} for hyperoptimization")

selected_statistic = self.implemented_stats[statistic]
selected_statistic = IMPLEMENTED_STATS[statistic]

if self.loss_type == "chi2":
return selected_statistic
Expand All @@ -273,60 +324,6 @@ def _parse_statistic(self, statistic: str, name: str) -> Callable:
# This is only used when calculating statistics over folds
return lambda x: np.reciprocal(selected_statistic(x))

@staticmethod
def _average(fold_losses: np.ndarray, axis: int = 0) -> float:
"""
Compute the average of the input array along the specified axis.
Parameters
----------
fold_losses: np.ndarray
Input array.
axis: int, optional
Axis along which the mean is computed. Default is 0.
Returns
-------
float: The average along the specified axis.
"""
return np.average(fold_losses, axis=axis).item()

@staticmethod
def _best_worst(fold_losses: np.ndarray, axis: int = 0) -> float:
"""
Compute the maximum value of the input array along the specified axis.
Parameters
----------
fold_losses: np.ndarray
Input array.
axis: int, optional
Axis along which the maximum is computed. Default is 0.
Returns
-------
float: The maximum value along the specified axis.
"""
return np.max(fold_losses, axis=axis).item()

@staticmethod
def _std(fold_losses: np.ndarray, axis: int = 0) -> float:
"""
Compute the standard deviation of the input array along the specified axis.
Parameters
----------
fold_losses: np.ndarray
Input array.
axis: int, optional
Axis along which the standard deviation is computed. Default is 0.
Returns
-------
float: The standard deviation along the specified axis.
"""
return np.std(fold_losses, axis=axis).item()


def fit_distance(pdfs_per_fold=None, **_kwargs):
"""Loss function for hyperoptimization based on the distance of
Expand Down

0 comments on commit 33b6d97

Please sign in to comment.