Continuous Spaces and Morphisms¶
This page covers the ContinuousSpace hierarchy, the
ContinuousMorphism
interface, composition operations, the boundary between discrete and
continuous models, and normalizing flows. The full registry of
parameterized distribution families lives in
continuous families.
ContinuousSpace hierarchy¶
Continuous morphisms act between continuous measurable spaces. quivers provides a hierarchy of standard spaces:
ContinuousSpace (abstract)
├── Euclidean R^n with Lebesgue measure
├── UnitInterval (0, 1) with Lebesgue measure (defined via Euclidean)
├── Simplex {x in R^dim : x >= 0, sum x = 1}
├── PositiveReals (0, inf) with Lebesgue measure
├── CholeskyFactor K x K lower-triangular Cholesky factors with unit-norm rows
├── ProductSpace Cartesian product of spaces (and discrete objects)
├── Sphere unit sphere S^{N-1} in R^N
├── Ball closed ball of radius r in R^N
├── Covariance cone of D x D symmetric positive-definite matrices
├── Correlation D x D correlation matrices (unit diagonal, PD)
├── Orthogonal orthogonal group O(D)
├── Stiefel Stiefel manifold V_K(R^N) of N x K orthonormal-column matrices
├── LowerTriangular D x D lower-triangular matrices
└── Diagonal D x D diagonal matrices (identified with R^D)
Euclidean space¶
from quivers.continuous.spaces import Euclidean
R3 = Euclidean(name="R3", dim=3) # R^3
R2_bounded = Euclidean(name="R2", dim=2, low=0.0, high=1.0) # [0,1]^2
Unit interval¶
from quivers.continuous.spaces import UnitInterval
U = UnitInterval(name="U") # (0, 1); pass dim=k for [0, 1]^k
Simplex¶
from quivers.continuous.spaces import Simplex
S3 = Simplex(name="S3", dim=3) # 2-simplex in R^3 (3 categories with sum = 1)
PositiveReals¶
from quivers.continuous.spaces import PositiveReals
P2 = PositiveReals(name="P2", dim=2) # (0, inf)^2
ProductSpace¶
from quivers.continuous.spaces import ProductSpace, Euclidean, UnitInterval
R3 = Euclidean(name="R3", dim=3)
U = UnitInterval(name="U")
P = ProductSpace(components=(R3, U)) # R^3 x (0, 1); also R3 * U
ContinuousMorphism¶
A continuous morphism \(f : X \to Y\) between spaces \(X\) and \(Y\) defines a conditional distribution \(p(y \mid x)\) via two operations:
log_prob(x, y): evaluate \(\log p(y \mid x)\).rsample(x): generate reparameterized samples from \(p(\cdot \mid x)\).
from quivers.continuous.morphisms import ContinuousMorphism
from quivers.continuous.spaces import Euclidean
import torch
class MyMorphism(ContinuousMorphism):
def log_prob(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
"""Log density log p(y | x)."""
raise NotImplementedError()
def rsample(
self, x: torch.Tensor, sample_shape: torch.Size = torch.Size()
) -> torch.Tensor:
"""Reparameterized samples; shape (*sample_shape, batch, codomain.dim)."""
raise NotImplementedError()
Morphisms are
nn.Module
subclasses, so they are trainable.
Composition: SampledComposition¶
When composing continuous morphisms, the intermediate is continuous. The result is computed via ancestral sampling:
import torch
from quivers.continuous.spaces import Euclidean
from quivers.continuous.morphisms import SampledComposition
from quivers.continuous.families import ConditionalNormal
# f: X -> Y (continuous), g: Y -> Z (continuous)
X = Euclidean(name="X", dim=3)
Y = Euclidean(name="Y", dim=4)
Z = Euclidean(name="Z", dim=5)
f = ConditionalNormal(X, Y)
g = ConditionalNormal(Y, Z)
# Composition: (g . f)(x) samples from g(f(x))
composed = f >> g
assert isinstance(composed, SampledComposition)
# rsample: sample from intermediate
x = torch.randn(2, 3)
z_samples = composed.rsample(x, sample_shape=torch.Size((50,))) # (50, 2, 5)
When an intermediate is discrete (a
FinSet), composition uses exact
marginalization:
ProductContinuousMorphism¶
Tensor product of continuous morphisms:
from quivers.continuous.spaces import Euclidean, UnitInterval
from quivers.continuous.morphisms import ProductContinuousMorphism
from quivers.continuous.families import ConditionalNormal, ConditionalBeta
X = Euclidean(name="X", dim=3)
f = ConditionalNormal(X, Euclidean(name="Y1", dim=2))
g = ConditionalBeta(X, UnitInterval(name="Y2"))
# Product: (f @ g)(x) ~ p(y_1, y_2 | x) = p(y_1 | x) . p(y_2 | x)
fg = f @ g
# Domain and codomain are products
assert fg.domain == f.domain * g.domain # X x X
assert fg.codomain == f.codomain * g.codomain # R^2 x (0,1)
Discrete / continuous boundary¶
Embed: embed a discrete domain into a continuous codomain¶
Embed a finite-set value into a continuous space:
import torch
from quivers.continuous.spaces import Euclidean
from quivers.continuous.boundaries import Embed
from quivers.core.morphisms import morphism
from quivers.core.objects import FinSet
X = FinSet(name="X", cardinality=4)
Y = FinSet(name="Y", cardinality=5)
discrete_f = morphism(X, Y)
# Give each element of X a learnable Gaussian embedding in R^5
Y_continuous = Euclidean(name="Y", dim=5)
embedded_f = Embed(domain=X, codomain=Y_continuous)
# Now can compose with continuous morphisms
y_cont = torch.randn(4, 5)
log_p = embedded_f.log_prob(torch.arange(4), y_cont)
Discretize: bin a continuous space¶
Discretize a continuous space into a finite set:
import torch
from quivers.continuous.spaces import Euclidean
from quivers.continuous.boundaries import Discretize
# Discretize a bounded interval into 20 bins
# (Discretize requires bounded Euclidean)
U = Euclidean(name="U", dim=1, low=0.0, high=1.0)
discretized = Discretize(U, n_bins=20)
# Discretize bins the continuous domain into the codomain FinSet.
assert discretized.domain == U
assert discretized.codomain.cardinality == 20
Integration with discrete morphisms¶
Continuous and discrete morphisms integrate transparently via the
>> and @ operators:
# Discrete to Continuous
discrete_f = morphism(X, Y) # X -> Y (finite)
cont_g = ConditionalNormal(
domain=Y, # discrete domain, indexed lookup
codomain=Euclidean(name="Z", dim=3),
)
composed = discrete_f >> cont_g # exact marginalization over Y
# Continuous to Discrete: not directly composable without an
# explicit Discretize boundary; use Discretize to materialize a
# FinSet target first.
Normalizing flows¶
A normalizing flow transforms a simple base distribution via a chain of invertible transformations.
AffineCouplingLayer¶
An affine coupling layer partitions the input and applies an invertible affine transformation to each partition:
import torch
from quivers.continuous.flows import AffineCouplingLayer
from quivers.continuous.spaces import Euclidean
dim = 4
domain = Euclidean(name="X", dim=2)
layer = AffineCouplingLayer(domain, dim, hidden_dim=32)
# Forward (base to target), conditioned on x
x = torch.randn(10, 2)
z = torch.randn(10, dim)
z_out, log_det = layer.forward(x, z)
# Inverse (target to base)
z_back, log_det_inv = layer.inverse(x, z_out)
ConditionalFlow¶
A full normalizing flow conditioned on input:
import torch
from quivers.continuous.flows import ConditionalFlow
from quivers.continuous.spaces import Euclidean
domain = Euclidean(name="X", dim=5)
codomain = Euclidean(name="Y", dim=4)
flow = ConditionalFlow(
domain=domain,
codomain=codomain,
n_layers=6,
hidden_dim=64,
)
# Sample
x = torch.randn(8, 5)
y_samples = flow.rsample(x, sample_shape=torch.Size((50,)))
# Log probability
y = torch.randn(8, 4)
log_p = flow.log_prob(x, y)
The quivers.continuous.flows module
exposes two public building blocks:
AffineCouplingLayer,
a single invertible affine coupling layer, and
ConditionalFlow,
a stack of coupling layers wrapped as a ContinuousMorphism.
See also¶
- Continuous Families: the more than forty
parameterized distribution families used to build
ContinuousMorphisms and the event-rank table that drives the axis-role syntax. - Monadic Programs: how
ContinuousMorphisms compose into probabilistic programs. - Stochastic Morphisms: the finite-state counterpart and the Giry monad construction.
References¶
- Conor Durkan, Artur Bekasov, Iain Murray, and George Papamakarios. 2019. Neural spline flows. arXiv preprint arXiv:1906.04032.
- Diederik P. Kingma, Tim Salimans, Rafal Jozefowicz, Xi Chen, Ilya Sutskever, and Max Welling. 2016. Improving variational inference with inverse autoregressive flow. arXiv preprint arXiv:1606.04934.
- George Papamakarios, Theo Pavlakou, and Iain Murray. 2017. Masked autoregressive flow for density estimation. arXiv preprint arXiv:1705.07057.
- Laurent Dinh, Jascha Sohl-Dickstein, and Samy Bengio. 2016. Density estimation using Real NVP. arXiv preprint arXiv:1605.08803.