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.transform — Function
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)).
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.
Bijectors are also callable objects, so b(x) is equivalent to transform(b, x).
Inverses
InverseFunctions.inverse — Function
inverse(t::Transform)Returns the inverse of transform t.
Log-absolute determinant of the Jacobian
Bijectors.logabsdetjac — Function
logabsdetjac(b, x)Return log(abs(det(J(b, x)))), where J(b, x) is the jacobian of b at x.
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.
Bijectors.logabsdetjacinv — Function
logabsdetjacinv(b, y)Just an alias for logabsdetjac(inverse(b), y).
ChangesOfVariables.with_logabsdet_jacobian — Function
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.
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.
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.elementwise — Function
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)Columnwise transformation
Likewise:
Bijectors.columnwise — Function
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)Working with distributions
Bijectors.bijector — Function
bijector(d::Distribution)Returns the constrained-to-unconstrained bijector for distribution d.
Bijectors.link — Function
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.0Bijectors.invlink — Function
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.0Bijectors.logpdf_with_trans — Function
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()), ℯ)
-1Bijectors.output_size — Function
output_size(f, sz)Returns the size of f(x) when given an input x of size sz.
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.
Bijectors.transformed — Method
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.
Bijectors.ordered — Function
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.
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.
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.
Utilities
Bijectors.isinvertible — Function
isinvertible(t)Return true if t is invertible, and false otherwise.
Bijectors.isclosedform — Method
isclosedform(b::Transform)::bool
isclosedform(b⁻¹::Inverse{<:Transform})::boolReturns 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.