Source code for credible.utils

# SPDX-FileCopyrightText: Copyright © 2023 Idiap Research Institute <contact@idiap.ch>
#
# SPDX-License-Identifier: GPL-3.0-or-later
"""Basic typing used throughout this package."""

import typing

import numpy.typing

CIFunctor: typing.TypeAlias = typing.Callable[
    [int, int], tuple[float, float, float]
]
"""A confidence-interval functor.

Exchangeable functors follow this prototype:

.. code:: python

   def f(successes: int, failures: int) -> tuple[float, float, float]:
       '''Returns best estimate, lower, and upper bounds of metric.'''
       pass

Functors allow evaluation of bayesian credible regions or confidence intervals
without coverage or :math:`\\lambda` parameterisation (implicit).  It works as
a partial function in which those parameters are predefined.
"""


CIArrayFunctor: typing.TypeAlias = typing.Callable[
    [typing.Iterable[int], typing.Iterable[int]],
    tuple[
        numpy.typing.NDArray[numpy.double],
        numpy.typing.NDArray[numpy.double],
        numpy.typing.NDArray[numpy.double],
    ],
]
"""A confidence-interval functor that works with arrays.

Functors allow evaluation of bayesian credible regions or confidence
intervals without coverage or :math:`\\lambda` parameterisation
(implicit).  It works as a partial function in which those parameters
are predefined.
"""


[docs] def as_int_arrays( input_: typing.Sequence[int | typing.Iterable[int]], ) -> tuple[numpy.typing.NDArray[numpy.int_], ...]: """Convert integer sequences into arrays, and checks for matching lenghts. Uses :py:func:`numpy.asarray`, which only converts arguments if they are not already integer arrays. We then use :py:func:`numpy.atleast_1d` to ensure all output arrays have at least 1 dimension. Parameters ---------- input_ Integer sequences to be converted into numpy arrays of integers. Returns ------- A tuple with input arrays converted. All input arrays contain are at least one dimensional. Raises ------ TypeError If the dimensions of the various arrays do not match. """ retval = tuple( numpy.atleast_1d(numpy.asarray(k, dtype=numpy.int_)) for k in input_ ) shapes = tuple(k.shape for k in retval) if len(set(shapes)) != 1: shape_str = ", ".join( [f"input_[{i}].shape = {k}" for i, k in enumerate(shapes)] ) raise TypeError( f"The number of dimensions on input arrays is different: {shape_str}" ) return retval
[docs] def safe_divide(n: int | float, d: int | float) -> float: r"""Divide n by d. Returns 0.0 in case of a division by zero. Parameters ---------- n Numerator. d Denominator. Returns ------- :math:`\frac{n}{d}`` if :math:`d \ne 0`, else 0. """ return n / (d + (d == 0))