Interface

This page describes the user-facing interface of Bijectors.jl. You should be able to use all the functions documented here with any bijector defined in Bijectors.jl.

Transformation

Bijectors.transformFunction
transform(b, x)

Transform x using b.

If with_logabsdet_jacobian is already implemented for b, the default implementation of transform will call first(with_logabsdet_jacobian(b, x)).

source
Bijectors.transform!Function
transform!(b, x[, y])

Transform x using b, storing the result in y.

If y is not provided, x is used as the output.

source

Bijectors are also callable objects, so b(x) is equivalent to transform(b, x).

Inverses

Log-absolute determinant of the Jacobian

Bijectors.logabsdetjac!Function
logabsdetjac!(b, x[, logjac])

Compute log(abs(det(J(b, x)))) and store the result in logjac, where J(b, x) is the jacobian of b at x.

source
ChangesOfVariables.with_logabsdet_jacobianFunction
with_logabsdet_jacobian(t::Transform, x)

Semantically, this must return a tuple of (y, logabsdetjac), where y = transform(t, x) and logabsdetjac = logabsdetjac(t, x). However, you can implement this function to exploit shared computation between the two quantities.

source
Bijectors.with_logabsdet_jacobian!Function
with_logabsdet_jacobian!(b, x[, y, logjac])

Compute transform(b, x) and logabsdetjac(b, x), storing the result in y and logjac, respetively.

If y is not provided, then x will be used in its place.

Defaults to calling with_logabsdet_jacobian(b, x) and updating y and logjac with the result.

source

Transform wrappers

Elementwise transformation

Some transformations are well-defined for different types of inputs, e.g. exp can also act elementwise on an N-dimensional Array{<:Real,N}. To specify that a transformation should act elementwise, we can wrap it in the elementwise wrapper:

Bijectors.elementwiseFunction
elementwise(f)

Alias for Base.Fix1(broadcast, f).

In the case where f::ComposedFunction, the result is Base.Fix1(broadcast, f.outer) ∘ Base.Fix1(broadcast, f.inner) rather than Base.Fix1(broadcast, f).

Examples

julia> x = [1.0, 2.0, 3.0];

julia> f = elementwise(exp);

julia> f(x)
3-element Vector{Float64}:
  2.718281828459045
  7.38905609893065
 20.085536923187668

julia> with_logabsdet_jacobian(f, x)
([2.718281828459045, 7.38905609893065, 20.085536923187668], 6.0)
source

Columnwise transformation

Likewise:

Bijectors.columnwiseFunction
columnwise(f)

Alias for Base.Fix1(eachcolmaphcat, f).

Represents a function f which is applied to each column of an input.

Examples

julia> x = [4.0 5.0 6.0; 1.0 2.0 3.0];

julia> my_reverse(v) = reverse(v);  # To avoid type piracy.

julia> f = columnwise(my_reverse);

julia> f(x)
2×3 Matrix{Float64}:
 1.0  2.0  3.0
 4.0  5.0  6.0

julia> # We can't use `with_logabsdet_jacobian` on `f` until we define it
       # for `my_reverse`, since we need to sum over columns.
       Bijectors.with_logabsdet_jacobian(::typeof(my_reverse), xs) = my_reverse(xs), 0.0;

julia> with_logabsdet_jacobian(f, x)
([1.0 2.0 3.0; 4.0 5.0 6.0], 0.0)
source

Working with distributions

Bijectors.bijectorFunction
bijector(d::Distribution)

Returns the constrained-to-unconstrained bijector for distribution d.

source
Bijectors.linkFunction
link(d::Distribution, x)

Transforms the input x using the constrained-to-unconstrained bijector for distribution d.

See also: invlink.

Example

julia> using Bijectors

julia> d = LogNormal()   # support is (0, Inf)
LogNormal{Float64}(μ=0.0, σ=1.0)

julia> b = bijector(d)   # log function transforms to unconstrained space
(::Base.Fix1{typeof(broadcast), typeof(log)}) (generic function with 2 methods)

julia> b(1.0)
0.0

julia> link(LogNormal(), 1.0)
0.0
source
Bijectors.invlinkFunction
invlink(d::Distribution, y)

Performs the inverse transform on a value y that was transformed using the constrained-to-unconstrained bijector for distribution d.

It should hold that invlink(d, link(d, x)) = x.

See also: link.

Example

julia> using Bijectors

julia> d = LogNormal()    # support is (0, Inf)
LogNormal{Float64}(μ=0.0, σ=1.0)

julia> link(LogNormal(), 1.0)   # uses a log transform
0.0

julia> invlink(LogNormal(), 0.0)
1.0
source
Bijectors.logpdf_with_transFunction
logpdf_with_trans(d::Distribution, x, transform::Bool)

If transform is false, logpdf_with_trans calculates the log probability density function (logpdf) of distribution d at x.

If transform is true, x is transformed using the constrained-to-unconstrained bijector for distribution d, and then the logpdf of the resulting value is calculated with respect to the unconstrained (transformed) distribution. Equivalently, if x is distributed according to d and y = link(d, x) is distributed according to td = transformed(d), then logpdf_with_trans(d, x, true) = logpdf(td, y). This is accomplished by subtracting the log Jacobian of the transformation.

Example

julia> using Bijectors

julia> logpdf_with_trans(LogNormal(), ℯ, false)
-2.4189385332046727

julia> logpdf(LogNormal(), ℯ)  # Same as above
-2.4189385332046727

julia> logpdf_with_trans(LogNormal(), ℯ, true)
-1.4189385332046727

julia> # If x ~ LogNormal(), then log(x) ~ Normal()
       logpdf(Normal(), 1.0)   
-1.4189385332046727

julia> # The difference between the two is due to the Jacobian
       logabsdetjac(bijector(LogNormal()), ℯ)
-1
source
Bijectors.output_sizeFunction
output_size(f, sz)

Returns the size of f(x) when given an input x of size sz.

source
output_size(f, dist::Distribution)

Returns the output size of f given an input drawn from the distribution dist.

By default this just calls output_size(f, size(dist)), but this can be overloaded for specific distributions. This is useful when Base.size(dist) is not defined, e.g. for ProductNamedTupleDistribution and in particular is used by DynamicPPL when generating new random values for transformed distributions.

source
Bijectors.transformedMethod
transformed(d::Distribution)
transformed(d::Distribution, b::Bijector)

Couples distribution d with the bijector b by returning a TransformedDistribution.

If no bijector is provided, i.e. transformed(d) is called, then transformed(d, bijector(d)) is returned.

source
Bijectors.orderedFunction
ordered(d::Distribution)

Return a Distribution whose support are ordered vectors, i.e., vectors with increasingly ordered elements.

Specifically, d is restricted to the subspace of its domain containing only ordered elements.

Warning

rand is implemented using rejection sampling, which can be slow for high-dimensional distributions. In such cases, consider using MCMC methods to sample from the distribution instead.

Warning

The resulting ordered distribution is un-normalized, which can cause issues in some contexts, e.g. in hierarchical models where the parameters of the ordered distribution are themselves sampled. See the notes below for a more detailed discussion.

Notes on ordered being un-normalized

The resulting ordered distribution is un-normalized. This is not a problem if used in a context where the normalizing factor is irrelevant, but if the value of the normalizing factor impacts the resulting computation, the results may be inaccurate.

For example, if the distribution is used in sampling a posterior distribution with MCMC and the parameters of the ordered distribution are themselves sampled, then the normalizing factor would in general be needed for accurate sampling, and ordered should not be used. However, if the parameters are fixed, then since MCMC does not require distributions be normalized, ordered may be used without problems.

A common case is where the distribution being ordered is a joint distribution of n identical univariate distributions. In this case the normalization factor works out to be the constant n!, and ordered can again be used without problems even if the parameters of the univariate distribution are sampled.

source

Utilities

Bijectors.isclosedformMethod
isclosedform(b::Transform)::bool
isclosedform(b⁻¹::Inverse{<:Transform})::bool

Returns true or false depending on whether or not evaluation of b has a closed-form implementation.

Most transformations have closed-form evaluations, but there are cases where this is not the case. For example the inverse evaluation of PlanarLayer requires an iterative procedure to evaluate.

source