Source code for fortuna.conformal.regression.onedim_uncertainty

from typing import Optional

import jax.numpy as jnp

from fortuna.conformal.regression.base import ConformalRegressor
from fortuna.typing import Array


[docs]class OneDimensionalUncertaintyConformalRegressor(ConformalRegressor):
[docs] def score( self, val_preds: Array, val_uncertainties: Array, val_targets: Array, ) -> jnp.ndarray: """ Compute the conformal scores. Parameters ---------- val_preds: Array A two-dimensional array of predictions over the validation data points. val_uncertainties: Array A two-dimensional array of uncertainty estimates (e.g. the standard deviation). The first dimension is over the validation inputs. The second must have only one component. val_targets: Array A two-dimensional array of validation target variables. Returns ------- jnp.ndarray Scores. """ if val_preds.ndim != 2 or val_preds.shape[1] != 1: raise ValueError( """`val_preds` must be a two-dimensional array. The second dimension must have only one component.""" ) if val_uncertainties.ndim != 2 or val_uncertainties.shape[1] != 1: raise ValueError( """`val_uncertainties` must be a two-dimensional array. The second dimension must have only one component.""" ) if (val_uncertainties <= 0).any(): raise ValueError( """All elements in `val_uncertainties` must be strictly positive.""" ) if val_targets.shape[1] != 1: raise ValueError( """The second dimension of the array(s) in `val_targets` must have only one component.""" ) return (jnp.abs(val_targets - val_preds) / val_uncertainties).squeeze(1)
[docs] def quantile( self, val_preds: Array, val_uncertainties: Array, val_targets: Array, error: float, scores: Optional[Array] = None, ) -> Array: """ Compute a quantile of the scores. Parameters ---------- val_preds: Array A two-dimensional array of predictions over the validation data points. val_uncertainties: Array A two-dimensional array of uncertainty estimates (e.g. the standard deviation). The first dimension is over the validation inputs. The second must have only one component. val_targets: Array A two-dimensional array of validation target variables. error: float Coverage error. This must be a scalar between 0 and 1, extremes included. scores: Optional[float] Conformal scores. This should be the output of :meth:`~fortuna.conformal.regression.onedim_uncertainty.OneDimensionalUncertaintyConformalRegressor.score`. Returns ------- float The conformal quantile. """ if error < 0 or error > 1: raise ValueError("""`error` must be a scalar between 0 and 1.""") if scores is None: scores = self.score(val_preds, val_uncertainties, val_targets) n = scores.shape[0] return jnp.quantile(scores, jnp.ceil((n + 1) * (1 - error)) / n)
[docs] def conformal_interval( self, val_preds: Array, val_uncertainties: Array, test_preds: Array, test_uncertainties: Array, val_targets: Array, error: float, quantile: Optional[float] = None, ) -> jnp.ndarray: """ Coverage interval of each of the test inputs, at the desired coverage error. This is supported only for one-dimensional target variables. Parameters ---------- val_preds: Array A two-dimensional array of predictions over the validation data points. test_preds: Array A two-dimensional array of predictions over the test data points. val_uncertainties: Array A two-dimensional array of uncertainty estimates (e.g. the standard deviation). The first dimension is over the validation inputs. The second must have only one component. test_uncertainties: Array A two-dimensional array of uncertainty estimates (e.g. the standard deviation). The first dimension is over the test inputs. The second must have only one component. val_targets: Array A two-dimensional array of validation target variables. error: float Coverage error. This must be a scalar between 0 and 1, extremes included. quantile: Optional[float] Conformal quantile. This should be the output of :meth:`~fortuna.conformal.regression.onedim_uncertainty.OneDimensionalUncertaintyConformalRegressor.quantile`. Returns ------- jnp.ndarray The conformal intervals. The two components of the second axis correspond to the left and right interval bounds. """ if test_preds.ndim != 2 or test_preds.shape[1] != 1: raise ValueError( """`test_preds` must be a two-dimensional array. The second dimension must have only one component.""" ) if test_uncertainties.ndim != 2 or val_uncertainties.shape[1] != 1: raise ValueError( """`test_uncertainties` must be a two-dimensional array. The second dimension must have only one component.""" ) if (test_uncertainties <= 0).any(): raise ValueError( """All elements in `test_uncertainties` must be strictly positive.""" ) if quantile is None: quantile = self.quantile(val_preds, val_uncertainties, val_targets, error) lows = test_preds - test_uncertainties * quantile highs = test_preds + test_uncertainties * quantile return jnp.array(list(zip(lows.squeeze(1), highs.squeeze(1))))