Skip to content

Bijectors

Invertible maps with log-Jacobian determinants, used to move between a family's unconstrained parameters and its support, and by the TransformReparam strategy.

bijectors

Bijector library for the compositional measure algebra.

A Bijector is a measurable bijection \(f: X \to Y\) between Borel spaces, together with its inverse \(f^{-1}\) and the log-determinant of its Jacobian. Pushing forward a measure \(\mu\) along \(f\) produces a new measure \(f_*\mu\) with density

\[p_{f_*\mu}(y) = p_\mu(f^{-1}(y))\,\bigl|\det J_{f^{-1}}(y)\bigr|.\]

In log-space (the only sane computational discipline; see TensorFlow Probability's Bijector rationale):

\[\log p_{f_*\mu}(y) = \log p_\mu(f^{-1}(y)) + \log\bigl|\det J_{f^{-1}}(y)\bigr|.\]

Every bijector exposes:

  • forward(x) evaluating \(f\),
  • inverse(y) evaluating \(f^{-1}\),
  • forward_log_det_jacobian(x) evaluating \(\log\bigl|\det J_f(x)\bigr|\),
  • inverse_log_det_jacobian(y) evaluating \(\log\bigl|\det J_{f^{-1}}(y)\bigr|\) (equal to \(-\text{forward\_log\_det\_jacobian}(f^{-1}(y))\) when both are finite).

The library is closed under composition via Compose(b1, b2) and under inversion via Inverse(b), so the family forms a groupoid of measurable isomorphisms.

BIJECTORS_BY_NAME module-attribute

BIJECTORS_BY_NAME: Mapping[str, type[Bijector]] = MappingProxyType({'Identity': IdentityBijector, 'Exp': ExpBijector, 'Log': LogBijector, 'Sigmoid': SigmoidBijector, 'Logit': LogitBijector, 'Softplus': SoftplusBijector, 'StickBreaking': StickBreakingBijector})

The bijectors a QVR source names by their surface spelling.

Pushforward(base, Exp) resolves Exp here. Only bijectors that take no arguments are listed; an affine or composite map is built in Python and passed as a value.

Bijector

Bases: ABC

Abstract base for measurable bijections used in Pushforward.

Implementations override the four primitive methods; the __call__, Compose, and inv helpers compose them.

inverse_log_det_jacobian

inverse_log_det_jacobian(y: Tensor) -> Tensor

Default implementation: invert the forward Jacobian's log-determinant. Subclasses with a more numerically stable closed form should override.

Source code in src/quivers/continuous/bijectors.py
def inverse_log_det_jacobian(self, y: Tensor) -> Tensor:
    """Default implementation: invert the forward Jacobian's
    log-determinant. Subclasses with a more numerically stable
    closed form should override.
    """
    return -self.forward_log_det_jacobian(self.inverse(y))

inv

inv() -> Bijector

Return the bijector's inverse, swapping forward / inverse and negating the Jacobian log-determinant.

Source code in src/quivers/continuous/bijectors.py
def inv(self) -> Bijector:
    """Return the bijector's inverse, swapping `forward` /
    `inverse` and negating the Jacobian log-determinant.
    """
    return InverseBijector(self)

InverseBijector

InverseBijector(base: Bijector)

Bases: Bijector

The inverse of a bijector, deferring all four primitives to the wrapped instance with their roles swapped.

Source code in src/quivers/continuous/bijectors.py
def __init__(self, base: Bijector) -> None:
    self.base = base

ComposeBijector

ComposeBijector(outer: Bijector, inner: Bijector)

Bases: Bijector

Composition outer ∘ inner. Forward applies inner first then outer; inverse applies outer.inverse first then inner.inverse. Log-Jacobians add by the chain rule.

Source code in src/quivers/continuous/bijectors.py
def __init__(self, outer: Bijector, inner: Bijector) -> None:
    self.outer = outer
    self.inner = inner

IdentityBijector

Bases: Bijector

The identity map. Useful as a neutral element in compositions and as the trivial pushforward.

ExpBijector

Bases: Bijector

The exponential map \(f(x) = e^x\) from \(\mathbb{R}\) to \((0, \infty)\). Jacobian log-determinant is \(x\).

LogBijector

Bases: Bijector

The logarithm \(f(x) = \log x\) from \((0, \infty)\) to \(\mathbb{R}\). Inverse of ExpBijector; Jacobian log-determinant is \(-\log x\).

SigmoidBijector

Bases: Bijector

The logistic sigmoid \(f(x) = 1/(1 + e^{-x})\) from \(\mathbb{R}\) to \((0, 1)\).

The Jacobian log-determinant is the log-density of the standard logistic distribution, \(-x - 2\log(1 + e^{-x})\), in a form that is stable in both tails.

LogitBijector

Bases: Bijector

The logit \(f(x) = \log(x / (1 - x))\) from \((0, 1)\) to \(\mathbb{R}\). Inverse of SigmoidBijector.

SoftplusBijector

Bases: Bijector

The softplus map \(f(x) = \log(1 + e^x)\) from \(\mathbb{R}\) to \((0, \infty)\). Smooth alternative to ExpBijector with linear tail growth on the positive side.

Forward Jacobian: \(df/dx = \sigma(x)\), so \(\log|df/dx| = -\text{softplus}(-x)\).

Inverse: \(f^{-1}(y) = \log(e^y - 1)\). Inverse Jacobian: \(df^{-1}/dy = 1/(1 - e^{-y})\), so \(\log|df^{-1}/dy| = -\log(1 - e^{-y}) = -\log(-\mathrm{expm1}(-y))\).

AffineBijector

AffineBijector(scale: Tensor | float, shift: Tensor | float)

Bases: Bijector

The affine map \(f(x) = \text{scale} \cdot x + \text{shift}\). scale must be strictly positive; the Jacobian log-determinant is \(\log\,\text{scale}\), broadcast to the input shape.

Source code in src/quivers/continuous/bijectors.py
def __init__(
    self,
    scale: Tensor | float,
    shift: Tensor | float,
) -> None:
    self.scale = torch.as_tensor(scale)
    self.shift = torch.as_tensor(shift)
    if torch.any(self.scale <= 0):
        raise ValueError(
            f"Affine: scale must be strictly positive; got {self.scale!r}"
        )

StickBreakingBijector

Bases: Bijector

The stick-breaking map from \(\mathbb{R}^{K-1}\) to the open \(K\)-simplex \(\{p \in \mathbb{R}^K_{>0} : \sum_k p_k = 1\}\).

Forward: \(y_k = z_k \cdot \prod_{j<k}(1 - z_j)\) for \(k < K-1\), where \(z_k = \sigma(x_k - \log(K - k - 1))\), and \(y_{K-1} = 1 - \sum_{k<K-1} y_k\). The shift by \(\log(K - k - 1)\) centres the prior on the uniform simplex when \(x \sim \mathrm{Normal}(0, 1)\), matching the convention used by Stan and PyMC.

Inverse: \(x_k = \sigma^{-1}(z_k) + \log(K - k - 1)\) where \(z_k = y_k / (1 - \sum_{j<k} y_j)\).

The Jacobian log-determinant in the forward direction sums log(z_k) + log(1 - z_k) + log(remaining) across the K-1 stick-breaks. Used in the desugaring of LogisticNormal and other simplex-valued transforms.