Prior distributions in PEtab

This notebook gives a brief overview of the prior distributions in PEtab and how they are represented in the PEtab library.

Prior distributions are used to specify the prior knowledge about the parameters. Parameter priors are specified in the parameter table. A prior is defined by its type and its parameters. Each prior type has a specific set of parameters. For example, the normal distribution has two parameters: the mean and the standard deviation.

There are two types of priors in PEtab - objective priors and initialization priors:

  • Objective priors are used to specify the prior knowledge about the parameters that are to be estimated. They will enter the objective function of the optimization problem. They are specified in the objectivePriorType and objectivePriorParameters columns of the parameter table.

  • Initialization priors can be used as a hint for the optimization algorithm. They will not enter the objective function. They are specified in the initializationPriorType and initializationPriorParameters columns of the parameter table.

import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

from petab.v1.C import *
from petab.v1.parameters import unscale
from petab.v1.priors import Prior

sns.set_style(None)


def plot(prior: Prior):
    """Visualize a distribution."""
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
    sample = prior.sample(20_000, x_scaled=True)

    fig.suptitle(str(prior))

    plot_single(prior, ax=ax1, sample=sample, scaled=False)
    plot_single(prior, ax=ax2, sample=sample, scaled=True)
    plt.tight_layout()
    plt.show()


def plot_single(
    prior: Prior, scaled: bool = False, ax=None, sample: np.array = None
):
    fig = None
    if ax is None:
        fig, ax = plt.subplots()

    if sample is None:
        sample = prior.sample(20_000)

    # assuming scaled sample
    if not scaled:
        sample = unscale(sample, prior.transformation)
        bounds = prior.bounds
    else:
        bounds = (
            (prior.lb_scaled, prior.ub_scaled)
            if prior.bounds is not None
            else None
        )

    # plot pdf
    xmin = min(
        sample.min(), bounds[0] if prior.bounds is not None else sample.min()
    )
    xmax = max(
        sample.max(), bounds[1] if prior.bounds is not None else sample.max()
    )
    padding = 0.1 * (xmax - xmin)
    xmin -= padding
    xmax += padding
    x = np.linspace(xmin, xmax, 500)
    y = prior.pdf(x, x_scaled=scaled, rescale=scaled)
    ax.plot(x, y, color="red", label="pdf")

    sns.histplot(sample, stat="density", ax=ax, label="sample")

    # plot bounds
    if prior.bounds is not None:
        for bound in bounds:
            if bound is not None and np.isfinite(bound):
                ax.axvline(bound, color="black", linestyle="--", label="bound")

    if fig is not None:
        ax.set_title(str(prior))

    if scaled:
        ax.set_xlabel(
            f"Parameter value on parameter scale ({prior.transformation})"
        )
        ax.set_ylabel("Rescaled density")
    else:
        ax.set_xlabel("Parameter value")

    ax.grid(False)
    handles, labels = ax.get_legend_handles_labels()
    unique_labels = dict(zip(labels, handles, strict=False))
    ax.legend(unique_labels.values(), unique_labels.keys())

    if ax is None:
        plt.show()

The basic distributions are the uniform, normal, Laplace, log-normal, and log-laplace distributions:

plot_single(Prior(UNIFORM, (0, 1)))
plot_single(Prior(NORMAL, (0, 1)))
plot_single(Prior(LAPLACE, (0, 1)))
plot_single(Prior(LOG_NORMAL, (0, 1)))
plot_single(Prior(LOG_LAPLACE, (1, 0.5)))
../_images/4ed302c0c29d801142cf1513a0b63437be17a95800ac349900006447da37364b.png ../_images/37ab24eaf05be70269a919e2d6adf15c5893fdadacf76a2f92fb598315846a41.png ../_images/cb1bcc9fbf35452cabf05a06a56dca2685da1b49dc27ab9f5cddde7b21407b48.png ../_images/6f6e9119f6437a0ea1556bdb6991f55d535a02717f461788f99dadf45c163d09.png ../_images/8b11720405011fbea774fa8dfcf145a84e40b760ebce262d0e5c2f5ae041fa4e.png

If a parameter scale is specified (parameterScale=lin|log|log10), the distribution parameters are used as is without applying the parameterScale to them. The exception are the parameterScale*-type distributions, as explained below. In the context of PEtab prior distributions, parameterScale will only be used for the start point sampling for optimization, where the sample will be transformed accordingly. This is demonstrated below. The left plot always shows the prior distribution for unscaled parameter values, and the right plot shows the prior distribution for scaled parameter values. Note that in the objective function, the prior is always on the unscaled parameters.

plot(Prior(NORMAL, (10, 2), transformation=LIN))
plot(Prior(NORMAL, (10, 2), transformation=LOG))

# Note that the log-normal distribution is different
#  from a log-transformed normal distribution:
plot(Prior(LOG_NORMAL, (10, 2), transformation=LIN))
../_images/7aa031a1d1112d2e65981f8059c800f98c811d9472f1a67b51be01e16e1da293.png ../_images/6bbb074b39857db83b4f2f78e6a5a35c343a493b758c33a6ecf80730986da6a7.png ../_images/8b73a2b41f8d399f905e37f0b9440dc2e1682e0a5d39cb5a2046a03d9cfb8fde.png

On the log-transformed parameter scale, Log* and parameterScale* distributions are equivalent:

plot(Prior(LOG_NORMAL, (10, 2), transformation=LOG))
plot(Prior(PARAMETER_SCALE_NORMAL, (10, 2)))
../_images/34026983c85e97642a10843641afde94b144cf63c2f62c7fb5e78efa780ac7a8.png ../_images/6b2d7bd6aa0c38a484643d9fe72ef9116db4a2c91e023ab5557c6ed3f0f76e6a.png

Prior distributions can also be defined on the scaled parameters (i.e., transformed according to parameterScale) by using the types parameterScaleUniform, parameterScaleNormal or parameterScaleLaplace. In these cases, the distribution parameters are interpreted on the transformed parameter scale (but not the parameter bounds, see below). This implies, that for parameterScale=lin, there is no difference between parameterScaleUniform and uniform.

# different, because transformation!=LIN
plot(Prior(UNIFORM, (0.01, 2), transformation=LOG10))
plot(Prior(PARAMETER_SCALE_UNIFORM, (0.01, 2), transformation=LOG10))

# same, because transformation=LIN
plot(Prior(UNIFORM, (0.01, 2), transformation=LIN))
plot(Prior(PARAMETER_SCALE_UNIFORM, (0.01, 2), transformation=LIN))
../_images/49156cb1656ce2fa4bc8845818bc7d2a1c4c72b99c490b2c88cc30b790ac44b0.png ../_images/ce9ef6961ea459a5037043921935d882cce276b7d5134283c471e7fd72f54907.png ../_images/434bcfd5dcad9264d7bdf0b83b96d6ab995221955d5263dfc88fb915e0b98cda.png ../_images/8032b4ef357d10cf2b519c6a5c2b95800eea45226330ab8fd85ff372469991aa.png

The given distributions are truncated at the bounds defined in the parameter table:

plot(Prior(NORMAL, (0, 1), bounds=(-2, 2)))
plot(Prior(UNIFORM, (0, 1), bounds=(0.1, 0.9)))
plot(Prior(UNIFORM, (1e-8, 1), bounds=(0.1, 0.9), transformation=LOG10))
plot(Prior(LAPLACE, (0, 1), bounds=(-0.5, 0.5)))
plot(
    Prior(
        PARAMETER_SCALE_UNIFORM,
        (-3, 1),
        bounds=(1e-2, 1),
        transformation=LOG10,
    )
)
../_images/40ab904757469118c00ff7b1dd8140fcc34bd44e82791761c43bcc547b9a8a4a.png ../_images/e0e32e230199bddd08ea27c2831ed1e54867e8bc9241b1e97c5f8e62aea67b53.png ../_images/42655995378603ffc9954f23e40d5faa65fdc5af37b492bf731fddc38fba16fb.png ../_images/8433de76a5f637778da99d6f28859782e56d726299986f006b44fe2fa21db637.png ../_images/a60e962201b9094f4b61e7426731a9fe9c78ecf2d034892b1ea9555512d2203a.png

This results in a constant shift in the probability density, compared to the non-truncated version (https://en.wikipedia.org/wiki/Truncated_distribution), such that the probability density still sums to 1.

Further distribution examples:

plot(Prior(NORMAL, (10, 1), bounds=(6, 11), transformation="log10"))
plot(
    Prior(
        PARAMETER_SCALE_NORMAL,
        (2, 1),
        bounds=(10**0, 10**3),
        transformation="log10",
    )
)
plot(Prior(LAPLACE, (10, 2), bounds=(6, 14)))
plot(Prior(LOG_LAPLACE, (1, 0.5), bounds=(0.5, 8)))
plot(Prior(LOG_NORMAL, (2, 1), bounds=(0.5, 8)))
../_images/4b586ca4dca47bd590e4869896bdcd6c954f887778f3001ddec8275bc5d90d2c.png ../_images/e3c8b0290e35994d7271ab74c8ecc901cf57b8cde77d28250406644d56bb478e.png ../_images/3d82c14aac5fc1a98acc979ff57a82ffbceb2c894bd465cb67b5a1674080dfd7.png ../_images/6bc6b14dfe315c32c370445a3346e3cc3f86d81cb04008ced4d55292eed056fc.png ../_images/541dfcdacff3ee419d9a86a5ceaf3b57f8329ce0b103d88c2af8143691566a9b.png