-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' into feat-bypass-dispatch-instance
- Loading branch information
Showing
35 changed files
with
1,435 additions
and
435 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import numpy as np | ||
|
||
from .elementwise_transform import ElementwiseTransform | ||
|
||
|
||
class AsTimeSeries(ElementwiseTransform): | ||
""" | ||
The `.as_time_series` transform can be used to indicate that | ||
variables shall be treated as time series. | ||
Currently, all this transformation does is to ensure that the variable | ||
arrays are at least 3D. The 2rd dimension is treated as the | ||
time series dimension and the 3rd dimension as the data dimension. | ||
In the future, the transform will have more advanced behavior | ||
to better ensure the correct treatment of time series data. | ||
Useage: | ||
adapter = ( | ||
bf.Adapter() | ||
.as_time_series(["x", "y"]) | ||
) | ||
""" | ||
|
||
def forward(self, data: np.ndarray, **kwargs) -> np.ndarray: | ||
return np.atleast_3d(data) | ||
|
||
def inverse(self, data: np.ndarray, **kwargs) -> np.ndarray: | ||
if data.shape[2] == 1: | ||
return np.squeeze(data, axis=2) | ||
|
||
return data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,14 @@ | ||
from .plots import calibration_ecdf | ||
from .plots import calibration_histogram | ||
from .plots import loss | ||
from .plots import mc_calibration | ||
from .plots import mc_confusion_matrix | ||
from .plots import mmd_hypothesis_test | ||
from .plots import pairs_posterior | ||
from .plots import pairs_prior | ||
from .plots import pairs_samples | ||
from .plots import recovery | ||
from .plots import z_score_contraction | ||
from .metrics import root_mean_squared_error, calibration_error, posterior_contraction | ||
|
||
from .plots import ( | ||
calibration_ecdf, | ||
calibration_histogram, | ||
loss, | ||
mc_calibration, | ||
mc_confusion_matrix, | ||
mmd_hypothesis_test, | ||
pairs_posterior, | ||
pairs_samples, | ||
recovery, | ||
z_score_contraction, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .calibration_error import calibration_error | ||
from .posterior_contraction import posterior_contraction | ||
from .root_mean_squared_error import root_mean_squared_error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
from typing import Sequence, Any, Mapping, Callable | ||
|
||
import numpy as np | ||
|
||
from ...utils.dict_utils import dicts_to_arrays | ||
|
||
|
||
def calibration_error( | ||
targets: Mapping[str, np.ndarray] | np.ndarray, | ||
references: Mapping[str, np.ndarray] | np.ndarray, | ||
resolution: int = 20, | ||
aggregation: Callable = np.median, | ||
min_quantile: float = 0.005, | ||
max_quantile: float = 0.995, | ||
variable_names: Sequence[str] = None, | ||
) -> Mapping[str, Any]: | ||
"""Computes an aggregate score for the marginal calibration error over an ensemble of approximate | ||
posteriors. The calibration error is given as the aggregate (e.g., median) of the absolute deviation | ||
between an alpha-CI and the relative number of inliers from ``prior_samples`` over multiple alphas in | ||
(0, 1). | ||
Parameters | ||
---------- | ||
targets : np.ndarray of shape (num_datasets, num_draws, num_variables) | ||
The random draws from the approximate posteriors over ``num_datasets`` | ||
references : np.ndarray of shape (num_datasets, num_variables) | ||
The corresponding ground-truth values sampled from the prior | ||
resolution : int, optional, default: 20 | ||
The number of credibility intervals (CIs) to consider | ||
aggregation : callable or None, optional, default: np.median | ||
The function used to aggregate the marginal calibration errors. | ||
If ``None`` provided, the per-alpha calibration errors will be returned. | ||
min_quantile : float in (0, 1), optional, default: 0.005 | ||
The minimum posterior quantile to consider. | ||
max_quantile : float in (0, 1), optional, default: 0.995 | ||
The maximum posterior quantile to consider. | ||
variable_names : Sequence[str], optional (default = None) | ||
Optional variable names to select from the available variables. | ||
Returns | ||
------- | ||
result : dict | ||
Dictionary containing: | ||
- "values" : float or np.ndarray | ||
The aggregated calibration error per variable | ||
- "metric_name" : str | ||
The name of the metric ("Calibration Error"). | ||
- "variable_names" : str | ||
The (inferred) variable names. | ||
""" | ||
|
||
samples = dicts_to_arrays(targets=targets, references=references, variable_names=variable_names) | ||
|
||
# Define alpha values and the corresponding quantile bounds | ||
alphas = np.linspace(start=min_quantile, stop=max_quantile, num=resolution) | ||
regions = 1 - alphas | ||
lowers = regions / 2 | ||
uppers = 1 - lowers | ||
|
||
# Compute quantiles for each alpha, for each dataset and parameter | ||
quantiles = np.quantile(samples["targets"], [lowers, uppers], axis=1) | ||
|
||
# Shape: (2, resolution, num_datasets, num_params) | ||
lower_bounds, upper_bounds = quantiles[0], quantiles[1] | ||
|
||
# Compute masks for inliers | ||
lower_mask = lower_bounds <= samples["references"][None, ...] | ||
upper_mask = upper_bounds >= samples["references"][None, ...] | ||
|
||
# Logical AND to identify inliers for each alpha | ||
inlier_id = np.logical_and(lower_mask, upper_mask) | ||
|
||
# Compute the relative number of inliers for each alpha | ||
alpha_pred = np.mean(inlier_id, axis=1) | ||
|
||
# Calculate absolute error between predicted inliers and alpha | ||
absolute_errors = np.abs(alpha_pred - alphas[:, None]) | ||
|
||
# Aggregate errors across alpha | ||
error = aggregation(absolute_errors, axis=0) | ||
|
||
return {"values": error, "metric_name": "Calibration Error", "variable_names": variable_names} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from typing import Sequence, Any, Mapping, Callable | ||
|
||
import numpy as np | ||
|
||
from ...utils.dict_utils import dicts_to_arrays | ||
|
||
|
||
def posterior_contraction( | ||
targets: Mapping[str, np.ndarray] | np.ndarray, | ||
references: Mapping[str, np.ndarray] | np.ndarray, | ||
aggregation: Callable = np.median, | ||
variable_names: Sequence[str] = None, | ||
) -> Mapping[str, Any]: | ||
"""Computes the posterior contraction (PC) from prior to posterior for the given samples. | ||
Parameters | ||
---------- | ||
targets : np.ndarray of shape (num_datasets, num_draws_post, num_variables) | ||
Posterior samples, comprising `num_draws_post` random draws from the posterior distribution | ||
for each data set from `num_datasets`. | ||
references : np.ndarray of shape (num_datasets, num_variables) | ||
Prior samples, comprising `num_datasets` ground truths. | ||
aggregation : callable, optional (default = np.median) | ||
Function to aggregate the PC across draws. Typically `np.mean` or `np.median`. | ||
variable_names : Sequence[str], optional (default = None) | ||
Optional variable names to select from the available variables. | ||
Returns | ||
------- | ||
result : dict | ||
Dictionary containing: | ||
- "values" : float or np.ndarray | ||
The aggregated posterior contraction per variable | ||
- "metric_name" : str | ||
The name of the metric ("Posterior Contraction"). | ||
- "variable_names" : str | ||
The (inferred) variable names. | ||
Notes | ||
----- | ||
Posterior contraction measures the reduction in uncertainty from the prior to the posterior. | ||
Values close to 1 indicate strong contraction (high reduction in uncertainty), while values close to 0 | ||
indicate low contraction. | ||
""" | ||
|
||
samples = dicts_to_arrays(targets=targets, references=references, variable_names=variable_names) | ||
|
||
post_vars = samples["targets"].var(axis=1, ddof=1) | ||
prior_vars = samples["references"].var(axis=0, keepdims=True, ddof=1) | ||
contraction = 1 - (post_vars / prior_vars) | ||
contraction = aggregation(contraction, axis=0) | ||
return {"values": contraction, "metric_name": "Posterior Contraction", "variable_names": samples["variable_names"]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from typing import Sequence, Any, Mapping, Callable | ||
|
||
import numpy as np | ||
|
||
from ...utils.dict_utils import dicts_to_arrays | ||
|
||
|
||
def root_mean_squared_error( | ||
targets: Mapping[str, np.ndarray] | np.ndarray, | ||
references: Mapping[str, np.ndarray] | np.ndarray, | ||
normalize: bool = True, | ||
aggregation: Callable = np.median, | ||
variable_names: Sequence[str] = None, | ||
) -> Mapping[str, Any]: | ||
"""Computes the (Normalized) Root Mean Squared Error (RMSE/NRMSE) for the given posterior and prior samples. | ||
Parameters | ||
---------- | ||
targets : np.ndarray of shape (num_datasets, num_draws_post, num_variables) | ||
Posterior samples, comprising `num_draws_post` random draws from the posterior distribution | ||
for each data set from `num_datasets`. | ||
references : np.ndarray of shape (num_datasets, num_variables) | ||
Prior samples, comprising `num_datasets` ground truths. | ||
normalize : bool, optional (default = True) | ||
Whether to normalize the RMSE using the range of the prior samples. | ||
aggregation : callable, optional (default = np.median) | ||
Function to aggregate the RMSE across draws. Typically `np.mean` or `np.median`. | ||
variable_names : Sequence[str], optional (default = None) | ||
Optional variable names to select from the available variables. | ||
Notes | ||
----- | ||
Aggregation is performed after computing the RMSE for each posterior draw, instead of first aggregating | ||
the posterior draws and then computing the RMSE between aggregates and ground truths. | ||
Returns | ||
------- | ||
result : dict | ||
Dictionary containing: | ||
- "values" : np.ndarray | ||
The aggregated (N)RMSE for each variable. | ||
- "metric_name" : str | ||
The name of the metric ("RMSE" or "NRMSE"). | ||
- "variable_names" : str | ||
The (inferred) variable names. | ||
""" | ||
|
||
samples = dicts_to_arrays(targets=targets, references=references, variable_names=variable_names) | ||
|
||
rmse = np.sqrt(np.mean((samples["targets"] - samples["references"][:, None, :]) ** 2, axis=0)) | ||
|
||
if normalize: | ||
rmse /= (samples["references"].max(axis=0) - samples["references"].min(axis=0))[None, :] | ||
metric_name = "NRMSE" | ||
else: | ||
metric_name = "RMSE" | ||
|
||
rmse = aggregation(rmse, axis=0) | ||
return {"values": rmse, "metric_name": metric_name, "variable_names": samples["variable_names"]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.