Skip to content

amisc.distribution

Module for probability distribution functions (PDFs).

Includes:

  • Distribution — an abstract interface for specifying a PDF.
  • Uniform — a uniform distribution.
  • Normal — a normal distribution.
  • Relative — a relative distribution (i.e. uniform within a percentage of a nominal value).
  • Tolerance — a tolerance distribution (i.e. uniform within a tolerance of a nominal value).
  • LogUniform — a log-uniform distribution.
  • LogNormal — a log-normal distribution.

Distribution objects can be converted easily to/from strings for serialization.

Distribution(dist_args)

Bases: ABC

Base class for PDF distributions that provide sample and pdf methods. Distributions should:

  • sample - return samples from the distribution
  • pdf - return the probability density function of the distribution
ATTRIBUTE DESCRIPTION
dist_args

the arguments that define the distribution (e.g. mean and std for a normal distribution)

TYPE: tuple

Source code in src/amisc/distribution.py
def __init__(self, dist_args: tuple):
    self.dist_args = dist_args

domain(dist_args=None)

Return the domain of this distribution. Defaults to dist_args

PARAMETER DESCRIPTION
dist_args

overrides self.dist_args

TYPE: tuple DEFAULT: None

Source code in src/amisc/distribution.py
def domain(self, dist_args: tuple = None) -> tuple:
    """Return the domain of this distribution. Defaults to `dist_args`

    :param dist_args: overrides `self.dist_args`
    """
    return dist_args or self.dist_args

from_string(dist_string) classmethod

Convert a string to a Distribution object.

PARAMETER DESCRIPTION
dist_string

specifies a PDF or distribution. Can be Normal(mu, std), Uniform(lb, ub), LogUniform(lb, ub), LogNormal(mu, std), Relative(pct), or Tolerance(tol). The shorthands N(0, 1), U(0, 1), LU(0, 1), LN(0, 1), rel(5), or tol(1) are also accepted.

TYPE: str

RETURNS DESCRIPTION
Distribution | None

the corresponding Distribution object

Source code in src/amisc/distribution.py
@classmethod
def from_string(cls, dist_string: str) -> Distribution | None:
    """Convert a string to a `Distribution` object.

    :param dist_string: specifies a PDF or distribution. Can be `Normal(mu, std)`, `Uniform(lb, ub)`,
                        `LogUniform(lb, ub)`, `LogNormal(mu, std)`,
                        `Relative(pct)`, or `Tolerance(tol)`. The shorthands `N(0, 1)`, `U(0, 1)`, `LU(0, 1)`,
                        `LN(0, 1)`, `rel(5)`, or `tol(1)` are also accepted.
    :return: the corresponding `Distribution` object
    """
    if not dist_string:
        return None

    dist_name, args, kwargs = parse_function_string(dist_string)
    if dist_name in ['N', 'Normal', 'normal']:
        # Normal distribution like N(0, 1)
        try:
            mu = float(kwargs.get('mu', args[0]))
            std = float(kwargs.get('std', args[1]))
            return Normal((mu, std))
        except Exception as e:
            raise ValueError(f'Normal distribution string "{dist_string}" is not valid: Try N(0, 1).') from e
    elif dist_name in ['U', 'Uniform', 'uniform']:
        # Uniform distribution like U(0, 1)
        try:
            lb = float(kwargs.get('lb', args[0]))
            ub = float(kwargs.get('ub', args[1]))
            return Uniform((lb, ub))
        except Exception as e:
            raise ValueError(f'Uniform distribution string "{dist_string}" is not valid: Try U(0, 1).') from e
    elif dist_name in ['R', 'Relative', 'relative', 'rel']:
        # Relative uniform distribution like rel(+-5%)
        try:
            pct = float(kwargs.get('pct', args[0]))
            return Relative((pct,))
        except Exception as e:
            raise ValueError(f'Relative distribution string "{dist_string}" is not valid: Try rel(5).') from e
    elif dist_name in ['T', 'Tolerance', 'tolerance', 'tol']:
        # Uniform distribution within a tolerance like tol(+-1)
        try:
            tol = float(kwargs.get('tol', args[0]))
            return Tolerance((tol,))
        except Exception as e:
            raise ValueError(f'Tolerance distribution string "{dist_string}" is not valid: Try tol(1).') from e
    elif dist_name in ['LogUniform', 'LU']:
        # LogUniform distribution like LU(1e-3, 1e-1)
        try:
            lb = float(kwargs.get('lb', args[0]))
            ub = float(kwargs.get('ub', args[1]))
            base = float(kwargs.get('base', args[2] if len(args) > 2 else 10))
            return LogUniform((lb, ub), base=base)
        except Exception as e:
            raise ValueError(f'LogUniform distr string "{dist_string}" is not valid: Try LU(1e-3, 1e-1).') from e
    elif dist_name in ['LogNormal', 'LN']:
        # LogNniform distribution like LN(-2, 1)
        try:
            mu = float(kwargs.get('mu', args[0]))
            std = float(kwargs.get('std', args[1]))
            base = float(kwargs.get('base', args[2] if len(args) > 2 else 10))
            return LogNormal((mu, std), base=base)
        except Exception as e:
            raise ValueError(f'LogNormal distr string "{dist_string}" is not valid: Try LN(-2, 1).') from e
    else:
        raise NotImplementedError(f'The distribution "{dist_string}" is not recognized.')

nominal(dist_args=None)

Return the nominal value of this distribution. Defaults to middle of domain.

PARAMETER DESCRIPTION
dist_args

overrides self.dist_args

TYPE: tuple DEFAULT: None

Source code in src/amisc/distribution.py
def nominal(self, dist_args: tuple = None) -> float:
    """Return the nominal value of this distribution. Defaults to middle of domain.

    :param dist_args: overrides `self.dist_args`
    """
    lb, ub = self.domain(dist_args=dist_args)
    return (lb + ub) / 2

pdf(x, dist_args=None) abstractmethod

Evaluate the pdf of this distribution at the x locations.

PARAMETER DESCRIPTION
x

the locations at which to evaluate the pdf

TYPE: ndarray

dist_args

overrides Distribution.dist_args

TYPE: tuple DEFAULT: None

RETURNS DESCRIPTION
ndarray

the pdf evaluations

Source code in src/amisc/distribution.py
@abstractmethod
def pdf(self, x: np.ndarray, dist_args: tuple = None) -> np.ndarray:
    """Evaluate the pdf of this distribution at the `x` locations.

    :param x: the locations at which to evaluate the pdf
    :param dist_args: overrides `Distribution.dist_args`
    :return: the pdf evaluations
    """
    raise NotImplementedError

sample(shape, nominal=None, dist_args=None) abstractmethod

Sample from the distribution.

PARAMETER DESCRIPTION
shape

shape of the samples to return

TYPE: int | tuple

nominal

a nominal value(s) for sampling (e.g. for relative distributions)

TYPE: float | ndarray DEFAULT: None

dist_args

overrides Distribution.dist_args

TYPE: tuple DEFAULT: None

RETURNS DESCRIPTION
ndarray

the samples of the given shape

Source code in src/amisc/distribution.py
@abstractmethod
def sample(self, shape: int | tuple, nominal: float | np.ndarray = None, dist_args: tuple = None) -> np.ndarray:
    """Sample from the distribution.

    :param shape: shape of the samples to return
    :param nominal: a nominal value(s) for sampling (e.g. for relative distributions)
    :param dist_args: overrides `Distribution.dist_args`
    :return: the samples of the given shape
    """
    raise NotImplementedError

LogNormal(dist_args, base=10)

Bases: Distribution

A LogNormal distribution. Specify by string as LogNormal(mu, sigma) or LN(mu, sigma) in shorthand. Uses base-10 by default.

Example

x = LogNormal((-2, 1))  # log10(x) ~ N(-2, 1)
Source code in src/amisc/distribution.py
def __init__(self, dist_args: tuple, base=10):
    self.base = base
    super().__init__(dist_args)

domain(dist_args=None)

Defaults the domain of the distribution to 3 standard deviations above and below the mean.

Source code in src/amisc/distribution.py
def domain(self, dist_args=None):
    """Defaults the domain of the distribution to 3 standard deviations above and below the mean."""
    mu, std = dist_args or self.dist_args
    return self.base ** (mu - 3 * std), self.base ** (mu + 3 * std)

LogUniform(dist_args, base=10)

Bases: Distribution

A LogUniform distribution. Specify by string as LogUniform(lb, ub) or LU(lb, ub) in shorthand. Uses base-10 by default.

Example

x = LogUniform((1e-3, 1e-1))  # log10(x) ~ U(-3, -1)
Source code in src/amisc/distribution.py
def __init__(self, dist_args: tuple, base=10):
    self.base = base
    super().__init__(dist_args)

Normal(dist_args)

Bases: Distribution

A Normal distribution. Specify by string as Normal(mu, std) or N(mu, std) in shorthand.

Source code in src/amisc/distribution.py
def __init__(self, dist_args: tuple):
    self.dist_args = dist_args

domain(dist_args=None)

Defaults the domain of the distribution to 3 standard deviations above and below the mean.

Source code in src/amisc/distribution.py
def domain(self, dist_args=None):
    """Defaults the domain of the distribution to 3 standard deviations above and below the mean."""
    mu, std = dist_args or self.dist_args
    return mu - 3 * std, mu + 3 * std

Relative(dist_args)

Bases: Distribution

A Relative distribution. Specify by string as Relative(pct) or rel(pct%) in shorthand. Will attempt to sample uniformly within the given percent of a nominal value.

Source code in src/amisc/distribution.py
def __init__(self, dist_args: tuple):
    self.dist_args = dist_args

Tolerance(dist_args)

Bases: Distribution

A Tolerance distribution. Specify by string as Tolerance(tol) or tol(tol) in shorthand. Will attempt to sample uniformly within a given absolute tolerance of a nominal value.

Source code in src/amisc/distribution.py
def __init__(self, dist_args: tuple):
    self.dist_args = dist_args

Uniform(dist_args)

Bases: Distribution

A Uniform distribution. Specify by string as "Uniform(lb, ub)" or "U(lb, ub)" in shorthand.

Source code in src/amisc/distribution.py
def __init__(self, dist_args: tuple):
    self.dist_args = dist_args