Skip to main content

surjectors

ci codecov

Surjection layers for density estimation with normalizing flows

Surjectors is a light-weight library for density estimation using inference and generative surjective normalizing flows, i.e., flows that can reduce or increase dimensionality while retaining exact-likelihood training. For completeness, we also provide implementations of conventional NF-based bijections.

Surjectors makes use of

  • Haiku's module system for neural networks,
  • Distrax for probability distributions and base bijectors,
  • Optax for gradient-based optimization,
  • JAX for autodiff and XLA computation.

Why surjections? A conventional normalizing flow is a chain of bijections: every layer is invertible and dimensionality-preserving, and the change of variables uses the Jacobian log-determinant. A surjection relaxes invertibility — it may map to a lower- (inference) or higher-dimensional (generative) space — and replaces the log-determinant with an exact likelihood contribution. This lets a flow reduce dimensionality as part of the model while still being trained by maximum likelihood.

Quickstart

A flow is built from three objects: a base distribution, a transform (a bijector, a surjector, or a Chain of them), and a TransformedDistribution that ties them together and exposes log_prob, sample, sample_and_log_prob and inverse_and_log_prob.

import distrax
import haiku as hk
from jax import numpy as jnp, random as jr
from surjectors import Slice, LULinear, Chain, TransformedDistribution
from surjectors.nn import make_mlp

def decoder_fn(n_dim):
    def _fn(z):
        params = make_mlp([32, 32, n_dim * 2])(z)
        means, log_scales = jnp.split(params, 2, -1)
        return distrax.Independent(distrax.Normal(means, jnp.exp(log_scales)))
    return _fn

@hk.without_apply_rng
@hk.transform
def flow(x):
    base_distribution = distrax.Independent(
        distrax.Normal(jnp.zeros(5), jnp.ones(5)), 1
    )
    transform = Chain([Slice(5, decoder_fn(5)), LULinear(5)])
    pushforward = TransformedDistribution(base_distribution, transform)
    return pushforward.log_prob(x)

x = jr.normal(jr.PRNGKey(1), (1, 10))
params = flow.init(jr.PRNGKey(2), x)
lp = flow.apply(params, x)

Everything runs inside hk.transform, so a TransformedDistribution method can be called directly, as above, when only one method is needed. To expose several methods (e.g. both log_prob and sample) from the same set of parameters, use surjectors.util.as_model, which wraps hk.multi_transform and returns each named method as a typed callable on model.apply.

Examples

Narrative walkthroughs, each self-contained enough to paste into a script or notebook:

Installation

Make sure to have a working JAX installation. Depending on whether you want to use CPU/GPU/TPU, follow these instructions.

To install from PyPI, call:

pip install surjectors

To install the latest GitHub <RELEASE>:

pip install git+https://github.com/dirmeier/surjectors@<RELEASE>

API reference

All layers implement forward_and_likelihood_contribution / inverse_and_likelihood_contribution; bijections additionally expose the Distrax-compatible forward_and_log_det / inverse_and_log_det. Import everything from the top-level surjectors namespace.

surjectors

Object Description
TransformedDistribution A base distribution pushed through a transform; exposes log_prob, sample, sample_and_log_prob, inverse_and_log_prob.
Chain Compose several bijectors/surjectors into a single transform.

surjectors.bijectors

Object Description
MaskedAutoregressive Masked autoregressive layer with a MADE conditioner and a configurable inner bijector.
AffineMaskedAutoregressive Masked autoregressive layer with an affine inner bijector.
MaskedCoupling RealNVP-style masked coupling layer with a configurable inner bijector.
AffineMaskedCoupling Masked coupling with an affine inner bijector.
RationalQuadraticSplineMaskedCoupling Masked coupling with a rational-quadratic-spline inner bijector.
Permutation Permute the dimensions of the input.
LULinear Invertible linear transform parameterised by an LU decomposition.

surjectors.surjectors

Object Description
Slice Drop dimensions, modelling the removed ones with a decoder distribution.
MLPInferenceFunnel LU-linear funnel with an MLP-parameterised decoder.
MaskedCouplingInferenceFunnel Coupling funnel with a configurable inner bijector and decoder.
AffineMaskedCouplingInferenceFunnel Coupling funnel with an affine inner bijector.
RationalQuadraticSplineMaskedCouplingInferenceFunnel Coupling funnel with an RQ-spline inner bijector.
MaskedAutoregressiveInferenceFunnel Autoregressive funnel with a configurable inner bijector.
AffineMaskedAutoregressiveInferenceFunnel Autoregressive funnel with an affine inner bijector.
RationalQuadraticSplineMaskedAutoregressiveInferenceFunnel Autoregressive funnel with an RQ-spline inner bijector.

surjectors.nn

Object Description
MADE Masked autoregressive density-estimator network (autoregressive conditioner).
make_mlp Build an MLP conditioner/decoder network.
make_transformer Build a transformer conditioner.

surjectors.util

Object Description
as_batch_iterator Turn a labelled dataset (named_dataset) into a minibatch iterator.
make_alternating_binary_mask Build an alternating boolean mask for coupling layers.
unstack Split an array along an axis into a list of arrays.

Contributing

Contributions in the form of pull requests are more than welcome. A good way to start is to check out issues labelled good first issue.

In order to contribute:

  1. Clone surjectors and install uv from here.

  2. Install all dependencies using uv sync --all-groups.

  3. Install the Git hooks:

    uv run pre-commit install -t pre-commit -t commit-msg
    
  4. Create a new branch locally, e.g. git checkout -b feature/my-new-feature.

  5. Implement your contribution and ideally a test case.

  6. Check your work (see below).

  7. Submit a PR 🙂.

Development commands

The project uses uv for everything (there is no Makefile):

uv sync --all-groups
uv run pytest
uv run ruff check surjectors examples
uv run ruff check --fix surjectors examples
uv run ruff format surjectors examples
uv run mypy surjectors examples
uv run pre-commit run --all-files

Citing Surjectors

If you find our work relevant to your research, please consider citing:

@article{dirmeier2024surjectors,
    author = {Simon Dirmeier},
    title = {Surjectors: surjection layers for density estimation with normalizing flows},
    year = {2024},
    journal = {Journal of Open Source Software},
    publisher = {The Open Journal},
    volume = {9},
    number = {94},
    pages = {6188},
    doi = {10.21105/joss.06188}
}

Metadata

Release files for surjectors 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for surjectors 0.4.0
File Size Uploaded
surjectors-0.4.0.tar.gz 279.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for surjectors 0.4.0
File Interpreter ABI Platform
surjectors-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 343.9 kB

Release files / surjectors-0.4.0.tar.gz

Download URL surjectors-0.4.0.tar.gz
Size 279.3 kB
Tags Source
SHA-256 checksum
How to use checksums
8bee1da7a6f6dc5e0410ffbe6431873bc8300ed5958648d189debd4a533483d8
BLAKE2b-256 checksum
How to use checksums
da0c57eda87312da8bb00509f8acecea31f984388a1ff4b5a0c35948b7dcb51b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / surjectors-0.4.0-py3-none-any.whl

Download URL surjectors-0.4.0-py3-none-any.whl
Size 64.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
264528db0a539396ee90be46ac02fc45e2cd1ec8706c8cfcc1878a8af3c05c83
BLAKE2b-256 checksum
How to use checksums
d26ed975d055c2bf473384586233c80e5cdb5a14aab2d28a383382dc34e6ed6d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page