Skip to content

base

base

Base distribution abstraction for ngboost-lightning.

Distribution

Distribution(params: NDArray[floating])

Bases: ABC

Abstract base class for probability distributions.

A Distribution represents a parametric family of distributions. Instances hold arrays of parameters (one set per sample) and provide methods for scoring, gradient computation, and sampling.

Subclasses must define
  • n_params: number of internal parameters
  • fit(y): estimate initial parameters from data
  • score(y): per-sample negative log-likelihood
  • d_score(y): gradient of score w.r.t. internal params
  • metric(): Fisher Information matrix
  • mean(): point prediction (conditional mean)
  • sample(n): draw random samples
  • cdf(y): cumulative distribution function
  • ppf(q): percent point function (inverse CDF)
  • logpdf(y): log probability density (or mass) function
Note on scoring rules

The score/d_score/metric methods implement the LogScore (negative log-likelihood). CRPS is supported via separate crps_score/crps_d_score/crps_metric methods — see :mod:ngboost_lightning.scoring for the strategy-pattern wiring.

ATTRIBUTE DESCRIPTION
n_params

Number of internal (unconstrained) parameters.

TYPE: int

Construct distribution from internal parameters.

PARAMETER DESCRIPTION
params

Internal parameters, shape [n_samples, n_params].

TYPE: NDArray[floating]

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def __init__(self, params: NDArray[np.floating]) -> None:
    """Construct distribution from internal parameters.

    Args:
        params: Internal parameters, shape ``[n_samples, n_params]``.
    """

fit abstractmethod staticmethod

fit(
    y: NDArray[floating],
    sample_weight: NDArray[floating] | None = None,
) -> NDArray[floating]

Estimate initial parameters from target data.

PARAMETER DESCRIPTION
y

Target values, shape [n_samples].

TYPE: NDArray[floating]

sample_weight

Per-sample weights, shape [n_samples]. If None, all samples are weighted equally.

TYPE: NDArray[floating] | None DEFAULT: None

RETURNS DESCRIPTION
NDArray[floating]

Internal parameter vector, shape [n_params]. These are the

NDArray[floating]

starting point for boosting (analogous to NGBoost's

NDArray[floating]

init_params).

Source code in ngboost_lightning/distributions/base.py
@staticmethod
@abstractmethod
def fit(
    y: NDArray[np.floating],
    sample_weight: NDArray[np.floating] | None = None,
) -> NDArray[np.floating]:
    """Estimate initial parameters from target data.

    Args:
        y: Target values, shape ``[n_samples]``.
        sample_weight: Per-sample weights, shape ``[n_samples]``.
            If ``None``, all samples are weighted equally.

    Returns:
        Internal parameter vector, shape ``[n_params]``. These are the
        starting point for boosting (analogous to NGBoost's
        ``init_params``).
    """

score abstractmethod

score(y: NDArray[floating]) -> NDArray[floating]

Per-sample negative log-likelihood (LogScore).

PARAMETER DESCRIPTION
y

Observed target values, shape [n_samples].

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

NLL values, shape [n_samples]. Lower is better.

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def score(self, y: NDArray[np.floating]) -> NDArray[np.floating]:
    """Per-sample negative log-likelihood (LogScore).

    Args:
        y: Observed target values, shape ``[n_samples]``.

    Returns:
        NLL values, shape ``[n_samples]``. Lower is better.
    """

d_score abstractmethod

d_score(y: NDArray[floating]) -> NDArray[floating]

Gradient of score w.r.t. internal parameters.

PARAMETER DESCRIPTION
y

Observed target values, shape [n_samples].

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

Gradient array, shape [n_samples, n_params].

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def d_score(self, y: NDArray[np.floating]) -> NDArray[np.floating]:
    """Gradient of score w.r.t. internal parameters.

    Args:
        y: Observed target values, shape ``[n_samples]``.

    Returns:
        Gradient array, shape ``[n_samples, n_params]``.
    """

metric abstractmethod

metric() -> NDArray[floating]

Fisher Information matrix.

RETURNS DESCRIPTION
NDArray[floating]

FI tensor, shape [n_samples, n_params, n_params].

Note

For Normal with (mean, log_scale) parameterization, the FI is diagonal: diag(1/scale^2, 2). The full [n_samples, n_params, n_params] return shape is required to support non-diagonal Fisher matrices in future distributions.

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def metric(self) -> NDArray[np.floating]:
    """Fisher Information matrix.

    Returns:
        FI tensor, shape ``[n_samples, n_params, n_params]``.

    Note:
        For Normal with (mean, log_scale) parameterization, the FI is
        diagonal: ``diag(1/scale^2, 2)``. The full
        ``[n_samples, n_params, n_params]`` return shape is required
        to support non-diagonal Fisher matrices in future distributions.
    """

mean abstractmethod

mean() -> NDArray[floating]

Conditional mean (point prediction).

RETURNS DESCRIPTION
NDArray[floating]

Mean values, shape [n_samples].

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def mean(self) -> NDArray[np.floating]:
    """Conditional mean (point prediction).

    Returns:
        Mean values, shape ``[n_samples]``.
    """

sample abstractmethod

sample(n: int) -> NDArray[floating]

Draw random samples from the distribution.

PARAMETER DESCRIPTION
n

Number of samples to draw per distribution instance.

TYPE: int

RETURNS DESCRIPTION
NDArray[floating]

Samples, shape [n, n_samples].

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def sample(self, n: int) -> NDArray[np.floating]:
    """Draw random samples from the distribution.

    Args:
        n: Number of samples to draw per distribution instance.

    Returns:
        Samples, shape ``[n, n_samples]``.
    """

cdf abstractmethod

cdf(y: NDArray[floating]) -> NDArray[floating]

Cumulative distribution function.

PARAMETER DESCRIPTION
y

Values at which to evaluate the CDF.

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

CDF values, same shape as y.

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def cdf(self, y: NDArray[np.floating]) -> NDArray[np.floating]:
    """Cumulative distribution function.

    Args:
        y: Values at which to evaluate the CDF.

    Returns:
        CDF values, same shape as ``y``.
    """

ppf abstractmethod

ppf(q: NDArray[floating]) -> NDArray[floating]

Percent point function (inverse CDF / quantile function).

PARAMETER DESCRIPTION
q

Quantiles, values in [0, 1].

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

Values at the given quantiles, same shape as q.

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def ppf(self, q: NDArray[np.floating]) -> NDArray[np.floating]:
    """Percent point function (inverse CDF / quantile function).

    Args:
        q: Quantiles, values in [0, 1].

    Returns:
        Values at the given quantiles, same shape as ``q``.
    """

logpdf abstractmethod

logpdf(y: NDArray[floating]) -> NDArray[floating]

Log probability density (or mass) function.

For continuous distributions this is the log-PDF. For discrete distributions (e.g. Poisson) this returns the log-PMF.

PARAMETER DESCRIPTION
y

Values at which to evaluate.

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

Log-density (or log-mass) values, same shape as y.

Source code in ngboost_lightning/distributions/base.py
@abstractmethod
def logpdf(self, y: NDArray[np.floating]) -> NDArray[np.floating]:
    """Log probability density (or mass) function.

    For continuous distributions this is the log-PDF. For discrete
    distributions (e.g. Poisson) this returns the log-PMF.

    Args:
        y: Values at which to evaluate.

    Returns:
        Log-density (or log-mass) values, same shape as ``y``.
    """

logsf

logsf(y: NDArray[floating]) -> NDArray[floating]

Log survival function: log(1 - CDF(y)).

Required for right-censored survival analysis. Subclasses that support censored observations (Exponential, LogNormal, Weibull) should override this with scipy's numerically stable logsf.

PARAMETER DESCRIPTION
y

Values at which to evaluate.

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

Log-survival values, same shape as y.

Source code in ngboost_lightning/distributions/base.py
def logsf(self, y: NDArray[np.floating]) -> NDArray[np.floating]:
    """Log survival function: log(1 - CDF(y)).

    Required for right-censored survival analysis. Subclasses that
    support censored observations (Exponential, LogNormal, Weibull)
    should override this with scipy's numerically stable ``logsf``.

    Args:
        y: Values at which to evaluate.

    Returns:
        Log-survival values, same shape as ``y``.
    """
    raise NotImplementedError(f"{type(self).__name__} does not implement logsf.")

crps_score

crps_score(y: NDArray[floating]) -> NDArray[floating]

Per-sample CRPS (Continuous Ranked Probability Score).

Subclasses that support CRPS must override this method.

PARAMETER DESCRIPTION
y

Observed target values, shape [n_samples].

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

CRPS values, shape [n_samples]. Lower is better.

Source code in ngboost_lightning/distributions/base.py
def crps_score(self, y: NDArray[np.floating]) -> NDArray[np.floating]:
    """Per-sample CRPS (Continuous Ranked Probability Score).

    Subclasses that support CRPS must override this method.

    Args:
        y: Observed target values, shape ``[n_samples]``.

    Returns:
        CRPS values, shape ``[n_samples]``. Lower is better.
    """
    raise NotImplementedError(
        f"{type(self).__name__} does not implement CRPS scoring."
    )

crps_d_score

crps_d_score(y: NDArray[floating]) -> NDArray[floating]

Gradient of CRPS w.r.t. internal parameters.

Subclasses that support CRPS must override this method.

PARAMETER DESCRIPTION
y

Observed target values, shape [n_samples].

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

Gradient array, shape [n_samples, n_params].

Source code in ngboost_lightning/distributions/base.py
def crps_d_score(self, y: NDArray[np.floating]) -> NDArray[np.floating]:
    """Gradient of CRPS w.r.t. internal parameters.

    Subclasses that support CRPS must override this method.

    Args:
        y: Observed target values, shape ``[n_samples]``.

    Returns:
        Gradient array, shape ``[n_samples, n_params]``.
    """
    raise NotImplementedError(
        f"{type(self).__name__} does not implement CRPS scoring."
    )

crps_metric

crps_metric() -> NDArray[floating]

Riemannian metric tensor for the CRPS natural gradient.

This is NOT the Fisher Information (which is the metric for LogScore). Each distribution must derive its own CRPS metric.

RETURNS DESCRIPTION
NDArray[floating]

Metric tensor, shape [n_samples, n_params, n_params].

Source code in ngboost_lightning/distributions/base.py
def crps_metric(self) -> NDArray[np.floating]:
    """Riemannian metric tensor for the CRPS natural gradient.

    This is NOT the Fisher Information (which is the metric for LogScore).
    Each distribution must derive its own CRPS metric.

    Returns:
        Metric tensor, shape ``[n_samples, n_params, n_params]``.
    """
    raise NotImplementedError(
        f"{type(self).__name__} does not implement CRPS scoring."
    )

natural_gradient

natural_gradient(y: NDArray[floating]) -> NDArray[floating]

Compute the natural gradient: FI^{-1} @ d_score.

Default implementation solves the linear system. Subclasses with diagonal Fisher (e.g. Normal) can override for efficiency.

PARAMETER DESCRIPTION
y

Observed target values, shape [n_samples].

TYPE: NDArray[floating]

RETURNS DESCRIPTION
NDArray[floating]

Natural gradient, shape [n_samples, n_params].

Source code in ngboost_lightning/distributions/base.py
def natural_gradient(self, y: NDArray[np.floating]) -> NDArray[np.floating]:
    """Compute the natural gradient: FI^{-1} @ d_score.

    Default implementation solves the linear system. Subclasses with
    diagonal Fisher (e.g. Normal) can override for efficiency.

    Args:
        y: Observed target values, shape ``[n_samples]``.

    Returns:
        Natural gradient, shape ``[n_samples, n_params]``.
    """
    grad = self.d_score(y)
    fi = self.metric()
    # Solve FI @ nat_grad = grad for each sample
    # fi: [n_samples, n_params, n_params], grad: [n_samples, n_params]
    # np.linalg.solve with 2D b treats it as (M, K) matrix columns, not
    # a batch of vectors. Expand to [n, n_params, 1] then squeeze back.
    result: NDArray[np.floating] = np.linalg.solve(fi, grad[..., np.newaxis])[
        ..., 0
    ]
    return result

total_score

total_score(
    y: NDArray[floating],
    sample_weight: NDArray[floating] | None = None,
) -> float

Weighted mean negative log-likelihood across all samples.

PARAMETER DESCRIPTION
y

Observed target values, shape [n_samples].

TYPE: NDArray[floating]

sample_weight

Per-sample weights, shape [n_samples]. If None, all samples are weighted equally.

TYPE: NDArray[floating] | None DEFAULT: None

RETURNS DESCRIPTION
float

Scalar (weighted) mean NLL.

Source code in ngboost_lightning/distributions/base.py
def total_score(
    self,
    y: NDArray[np.floating],
    sample_weight: NDArray[np.floating] | None = None,
) -> float:
    """Weighted mean negative log-likelihood across all samples.

    Args:
        y: Observed target values, shape ``[n_samples]``.
        sample_weight: Per-sample weights, shape ``[n_samples]``.
            If ``None``, all samples are weighted equally.

    Returns:
        Scalar (weighted) mean NLL.
    """
    return float(np.average(self.score(y), weights=sample_weight))