Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

The coupling pattern

Split, condition, transform — why a coupling layer has a triangular Jacobian, a free log-det, and an analytic inverse with no network inversion

00 — The coupling pattern

Part 4 used coupling flows as a black box and saw they are more expressive per parameter than diagonal marginals. Part 5 opens the box. The coupling layer (Dinh et al., RealNVP Dinh et al. (2017)) is a deceptively simple idea with three remarkable properties. Split the coordinates into two halves x=(xA,xB)x = (x_A, x_B) via a mask, then

zA=xA,zB=Tθ(xA)(xB),z_A = x_A, \qquad z_B = T_{\theta(x_A)}(x_B),

where TT is any 1-D bijector and its parameters θ=cϕ(xA)\theta = c_\phi(x_A) come from a conditioner network reading the unchanged half. The passive half xAx_A is copied through; only xBx_B is transformed, and its transform is conditioned on xAx_A. From this one move follow:

  1. a triangular Jacobian, so the log-determinant is a cheap sum;
  2. an analytic inverse that never inverts the conditioner network;
  3. arbitrary expressiveness — cϕc_\phi can be any network, TT any bijector.

What you will see

The coupling layer at a glance

The whole mechanism in one picture: the mask splits the input into a passive half xAx_A and an active half xBx_B. The passive half is copied straight to the output (zA=xAz_A = x_A) and fed to a conditioner network that emits the bijector’s parameters θ=cϕ(xA)\theta = c_\phi(x_A). The active half is then transformed by the bijector TθT_\theta (zB=Tθ(xB)z_B = T_\theta(x_B)). Because the conditioner only sees the copied half, the Jacobian is triangular and the log-det is the cheap sum over the active half — everything below makes this concrete.

<Figure size 1000x480 with 1 Axes>

1. Split, condition, transform

gf.AffineCoupling is the canonical example: the active half is transformed by an affine map zB=s(xA)⊙xB+t(xA)z_B = s(x_A)\odot x_B + t(x_A), with the log-scale ss and shift tt predicted by an MLP from the passive half. We build one on a 4-D event and see exactly which coordinates it touches.

input  x = [-1.184 -0.116  0.173  0.957]
output z = [-1.184 -0.116  0.363  0.679]
passive half (unchanged): coords [0, 1]
active half (transformed): coords [2, 3]

The first two coordinates pass straight through; the last two are rescaled and shifted by amounts the conditioner computed from the first two. That conditioning is what lets a coupling model dependence — the transform of xBx_B bends with xAx_A — while keeping everything else exactly invertible.

2. The triangular Jacobian → a free log-det

Here is the magic. Because zA=xAz_A = x_A does not depend on xBx_B, and zBz_B depends on xBx_B only through the (elementwise) bijector TT, the Jacobian ∂z/∂x\partial z/\partial x is block-triangular:

J=(I0∂zB/∂xAdiag(s)).J = \begin{pmatrix} I & 0 \\ \partial z_B/\partial x_A & \mathrm{diag}(s) \end{pmatrix}.

A triangular matrix’s determinant is the product of its diagonal, so log⁡∣det⁡J∣=∑i∈Blog⁡∣si∣\log|\det J| = \sum_{i\in B}\log|s_i| — just the active half’s log-scales, with no Jacobian ever formed. We confirm against an autodiff Jacobian.

Jacobian:
[[ 1.     0.     0.     0.   ]
 [ 0.     1.     0.     0.   ]
 [-0.08  -0.036  1.031  0.   ]
 [ 0.02   0.027  0.     0.963]]

strictly-upper-triangular part max |·| = 0.0e+00  (zero → triangular)
log|det J|  (from autodiff Jacobian) = -0.00768
sum of log|diagonal|                 = -0.00768
coupling.transform_and_log_det       = -0.00768   (all equal)
<Figure size 460x420 with 2 Axes>

The strictly-upper-triangular block is exactly zero, the passive diagonal is 1, and the three estimates of the log-det agree to machine precision. This is the whole reason coupling flows scale: a general invertible map needs an O(d3)O(d^3) determinant, but a coupling layer’s is an O(d)O(d) sum — and the conditioner can be arbitrarily expressive without changing that, because it only fills the off-diagonal block, which the determinant ignores.

3. The inverse never inverts the network

Inverting a coupling is just as cheap, and this is the second surprise. Given z=(zA,zB)z = (z_A, z_B):

xA=zA,θ=cϕ(zA) (=cϕ(xA)),xB=Tθ−1(zB).x_A = z_A, \qquad \theta = c_\phi(z_A)\ (= c_\phi(x_A)), \qquad x_B = T^{-1}_\theta(z_B).

Because the conditioner reads the unchanged half, we recover xA=zAx_A = z_A for free, recompute the same parameters θ with a forward pass, and invert only the simple bijector TT. The conditioner network cϕc_\phi — which may be huge — is never inverted. We round-trip to confirm.

round-trip max error = 4.44e-16  (no network inversion)
forward log_det = -0.00768, inverse log_det = +0.00768 (sum = 0.0e+00)

Machine-precision round-trip, and the inverse log-det is exactly the negative of the forward — both obtained by a forward evaluation of the conditioner. This asymmetry-free invertibility is why coupling flows give fast sampling and fast density, unlike autoregressive flows where one direction is sequential.

4. The bijector is swappable — affine → mixture-CDF

Nothing above used the affine form of TT beyond “elementwise and invertible”. Swap TT for a more expressive monotone bijector and the pattern — triangular Jacobian, free log-det, analytic inverse — is unchanged. gauss_flows ships a menu; MixtureGaussianCDFCoupling uses the Gaussian-mixture CDF of Part 1 as TT. We check it has the same structural properties.

MixtureGaussianCDFCoupling:
  triangular?  upper |·| max = 0.0e+00
  log-det matches diagonal-sum? True
  round-trip = 3.7e-07
  -> same coupling contract, a more expressive transform

Identical structure, richer TT — the bijector and the conditioner are independent choices, which is exactly why the next two notebooks can tour them separately.

Recap

propertywhycost
split x=(xA,xB)x=(x_A,x_B), zB=Tθ(xA)(xB)z_B = T_{\theta(x_A)}(x_B)conditioner reads the unchanged half—
triangular JacobianzAz_A ⟂ xBx_B; zBz_B elementwise in xBx_Blog⁡∣det⁡∣=∑Blog⁡∣s∣\log\lvert\det\rvert=\sum_{B}\log\lvert s\rvert, O(d)O(d)
analytic inverserecompute θ=cϕ(zA)\theta=c_\phi(z_A), invert only TTno network inversion
swappable bijector TTstructure independent of TT’s formaffine → mixture-CDF → spline

A coupling layer transforms only half its inputs, so a single layer is not a full bijector of all coordinates — you alternate the mask (Part 5 03 — mask design) so every coordinate is both transformed and used as context.

Next up. The triangular trick works for any monotone TT. 01 — Bijector menu tours the choices gauss_flows offers — affine, mixture-CDF, deep-sigmoid, rational-quadratic spline — and what each buys in expressiveness.

References
  1. Dinh, L., Sohl-Dickstein, J., & Bengio, S. (2017). Density Estimation using Real NVP. International Conference on Learning Representations (ICLR).