Bounded Variables¶
PyMC3 includes the construct Bound
for
placing constraints on existing probability distributions. It modifies a given
distribution to take values only within a specified interval.
Note that the Bound class does not directly create a bounded distribution: instead it creates a Callable class that can be invoked to create a bounded distribution, as the example below illustrates.
Some types of variables require constraints. For instance, it doesn’t make
sense for a standard deviation to have a negative value, so something like a
Normal prior on a parameter that represents a standard deviation would be
inappropriate. PyMC3 includes distributions that have positive support, such
as Gamma
or
Exponential
. PyMC3 also includes
several bounded distributions, such as
Uniform
,
HalfNormal
, and
HalfCauchy
, that are restricted to a
specific domain.
All univariate distributions in PyMC3 can be given bounds. The distribution of a continuous variable that has been bounded is automatically transformed into an unnormalized distribution whose domain is unconstrained. The transformation improves the efficiency of sampling and variational inference algorithms.
Usage¶
For example, one may have prior information that suggests that the value of a parameter representing a standard deviation is near one. One could use a Normal distribution while constraining the support to be positive. The specification of a bounded distribution should go within the model block:
import pymc3 as pm
with pm.Model() as model:
BoundedNormal = pm.Bound(pm.Normal, lower=0.0)
x = BoundedNormal('x', mu=1.0, sigma=3.0)
If the bound will be applied to a single variable in the model, it may be cleaner notationally to define both the bound and variable together.
with model:
x = pm.Bound(pm.Normal, lower=0.0)('x', mu=1.0, sigma=3.0)
However, it is possible to create multiple different random variables that have the same bound applied to them:
with model:
BoundNormal = pm.Bound(pm.Normal, lower=0.0)
hyper_mu = BoundNormal("hyper_mu", mu=1, sigma=0.5)
mu = BoundNormal("mu", mu=hyper_mu, sigma=1)
Bounds can also be applied to a vector of random variables. With the same
BoundedNormal
object we created previously we can write:
with model:
x_vector = BoundedNormal('x_vector', mu=1.0, sigma=3.0, shape=3)
Caveats¶
Bounds cannot be given to variables that are
observed
. To model truncated data, use aPotential()
in combination with a cumulative probability function. See this example notebook.The automatic transformation applied to continuous distributions results in an unnormalized probability distribution. This doesn’t effect inference algorithms but may complicate some model comparison procedures.
Bounded Variable API¶
- class pymc3.distributions.bound.Bound(distribution, lower=None, upper=None)¶
Create a Bound variable object that can be applied to create a new upper, lower, or upper and lower bounded distribution.
The resulting distribution is not normalized anymore. This is usually fine if the bounds are constants. If you need truncated distributions, use Bound in combination with a
Potential
with the cumulative probability function.The bounds are inclusive for discrete distributions.
- Parameters
- distribution: pymc3 distribution
Distribution to be transformed into a bounded distribution.
- lower: float or array like, optional
Lower bound of the distribution.
- upper: float or array like, optional
Upper bound of the distribution.
Examples
with pm.Model(): NegativeNormal = pm.Bound(pm.Normal, upper=0.0) par1 = NegativeNormal('par`', mu=0.0, sigma=1.0, testval=-0.5) # you can use the Bound object multiple times to # create multiple bounded random variables par1_1 = NegativeNormal('par1_1', mu=-1.0, sigma=1.0, testval=-1.5) # you can also define a Bound implicitly, while applying # it to a random variable par2 = pm.Bound(pm.Normal, lower=-1.0, upper=1.0)( 'par2', mu=0.0, sigma=1.0, testval=1.0)