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.

Householder & trainable orthogonals

Products of Householder reflections and the Cayley / matrix-exponential maps — orthogonal by construction, log-det 0, trainable end-to-end

01 — Householder & trainable orthogonals

Notebook 00 fit a rotation once (PCA, ICA, random) and froze it. Inside a parametric Gaussianization flow we instead want to learn the rotation end-to-end, jointly with the marginal transforms, by gradient descent. That raises a problem: a rotation must satisfy Q⊤Q=IQ^\top Q = I, and an unconstrained matrix updated by SGD will not stay orthogonal. We need a parameterisation — a smooth map from free parameters θ∈Rm\theta\in\mathbb{R}^m to an orthogonal Q(θ)Q(\theta) — that is orthogonal for every θ.

Two classic constructions do this, and gauss_flows ships both:

Both keep ∣det⁡Q∣=1|\det Q| = 1 exactly, so — echoing the “rotations are free” thread from Part 0 — the log-determinant they contribute stays exactly 0 at every gradient step.

What you will see

1. The Householder reflection

A Householder reflection is the simplest non-trivial orthogonal map. Given a unit vector vv, it reflects every point across the hyperplane through the origin orthogonal to vv:

H(v)=I−2 vv⊤∥v∥2.H(v) = I - 2\,\frac{vv^\top}{\|v\|^2}.

It is orthogonal (H⊤H=IH^\top H = I), involutive (H2=IH^2 = I, a reflection undoes itself), and has det⁡H=−1\det H = -1 — it flips orientation. Here it is from scratch, reflecting a 2D shape across the line ⊥v\perp v.

orthogonal? ||H^T H - I|| = 4.4e-16
involutive? ||H@H - I||   = 4.4e-16
det H = -1.000  (reflection flips orientation)
<Figure size 520x520 with 1 Axes>

2. Products of reflections span O(d)O(d)

One reflection is not much, but products are everything: a classical theorem (the basis of the QR / Householder decomposition) says every orthogonal matrix in O(d)O(d) is a product of at most dd Householder reflections. Stacking KK of them,

Q=H(vK) H(vK−1)⋯H(v1),det⁡Q=(−1)K,Q = H(v_K)\,H(v_{K-1})\cdots H(v_1), \qquad \det Q = (-1)^K,

gives an exactly-orthogonal QQ whose determinant is set by the parity of KK: odd KK → orientation-flipping (det⁡=−1\det=-1), even KK → a proper rotation (det⁡=+1\det=+1). gf.HouseholderRotation(n_reflections=K, shape=(d,)) is exactly this product, and — being orthogonal — its log-determinant is identically 0.

K=1: log_det=+0.0e+00  det Q=-1.00  (-1)^K=-1  round-trip=2.2e-16
K=2: log_det=+0.0e+00  det Q=+1.00  (-1)^K=+1  round-trip=3.1e-16
K=3: log_det=+0.0e+00  det Q=-1.00  (-1)^K=-1  round-trip=3.1e-16
K=4: log_det=+0.0e+00  det Q=+1.00  (-1)^K=+1  round-trip=3.1e-16

Every product is orthogonal to machine precision, log-det is exactly 0, and det⁡Q\det Q tracks (−1)K(-1)^K. So to represent a general orthogonal map you take K=dK=d reflections; to guarantee a proper rotation you take an even KK. This is the workhorse trainable orthogonal in flow libraries (Householder flow, Tomczak & Welling (2016)).

3. Training the rotation, end-to-end

The point of a parameterisation is that its free parameters are unconstrained: any real values give a valid orthogonal QQ. So we can drop gf.HouseholderRotation straight into an optax loop and train it by plain gradient descent — no orthogonality constraint, no re-projection — with the log-det pinned at 0 the whole way. (The reflection of §1 was built by hand to see the construction; from here on we train the real gauss_flows layer.) We demonstrate by learning the rotation that reproduces a target Q⋆Q_\star.

One subtlety, straight from §2: a product of KK reflections has det⁡=(−1)K\det = (-1)^K, so to fit a proper rotation (det⁡Q⋆=+1\det Q_\star = +1) we must use an even number of reflections. We will return to what happens when the parity is wrong in §5 — it is not a bug, it is geometry.

target det = +1.00
gf.HouseholderRotation (K=2) final fit ||Q - Q*||^2 = 1.63e-31
max |log|det Q|| over training = 0.0e+00  (stays 0)
<Figure size 1100x420 with 2 Axes>

The fit drops to machine zero — two reflections reproduce the target rotation exactly — and the right panel is the headline: log⁡∣det⁡Q∣\log|\det Q| never leaves 0 (to ∼10−13\sim10^{-13}). The optimiser walks freely through the unconstrained parameter space while Q(θ)Q(\theta) stays glued to the orthogonal manifold. That is what makes it a free trainable rotation: it adds expressive mixing to a flow without ever charging the log-likelihood a log-det.

4. The Cayley map and the matrix exponential

Householder builds QQ from reflections. The other route builds it from a skew-symmetric matrix A=−A⊤A = -A^\top, which has exactly d(d−1)/2d(d-1)/2 free entries. Two maps send skew → orthogonal:

Cayley:Q=(I−A)(I+A)−1,matrix exp:Q=exp⁡(A).\textbf{Cayley:}\quad Q = (I - A)(I + A)^{-1}, \qquad \textbf{matrix exp:}\quad Q = \exp(A).

Both land in SO(d)SO(d) — det⁡Q=+1\det Q = +1 always — and both are the identity at A=0A=0, the natural initialisation for a flow layer. We first confirm the two formulas really are orthogonal, then train gf.OrthogonalRotation (which uses the Cayley map — one linear solve, no matrix exponential) on the same target.

Cayley : orthogonal? 3.3e-16   det = +1.000
expm   : orthogonal? 3.3e-16   det = +1.000
gf.OrthogonalRotation: log_det=+0.0e+00, identity at init? True
gf.OrthogonalRotation final fit to the same Q_star = 6.21e-22   (max |log-det| = 0.0e+00)

Both formulas are orthogonal to machine precision with det⁡=+1\det = +1, the Cayley OrthogonalRotation starts at the identity with log-det 0, and it fits the (proper-rotation) target to machine zero. So for a rotation, all three parameterisations are interchangeable — pick by cost: Cayley is one linear solve in d(d−1)/2d(d-1)/2 parameters, Householder is KK rank-one updates.

5. Which can reach what — the determinant-parity wall

There is one real difference, and it is about reachability. The Cayley map and matrix exponential only ever produce det⁡=+1\det = +1: they parameterise the rotation subgroup SO(d)SO(d) and cannot represent an orientation flip. A Householder product can — by using an odd number of reflections. We make the gap concrete by trying to fit a reflection (det⁡Q⋆=−1\det Q_\star = -1) with each.

target is a reflection: det Q_ref = -1.00

gf.HouseholderRotation K=3 (odd, det -1): fit = 4.78e-31   <- reaches the reflection
gf.OrthogonalRotation  (SO(d), det +1)  : fit = 4.00e+00   <- stuck at the parity wall

The Householder product nails the reflection; the Cayley map plateaus at ∥Q−Q⋆∥2=4\|Q-Q_\star\|^2 = 4 — the minimal distance between two orthogonal matrices of opposite determinant (it is 2d−2 tr(Q⊤Q⋆)2d - 2\,\mathrm{tr}(Q^\top Q_\star) maximised over the wrong component). That flat plateau is not a local minimum or a tuning failure: no skew AA can give det⁡=−1\det = -1, so the target is simply unreachable. The same wall is why fitting a proper rotation needs an even number of reflections (§3): parity is geometry, not optimisation.

For a Gaussianization flow this rarely bites — a downstream sign flip in the marginal step absorbs orientation — but it is the precise mathematical reason HouseholderRotation (all of O(d)O(d)) and OrthogonalRotation (only SO(d)SO(d)) are not interchangeable in general.

Recap

parameterisationfromspansdetcostAPI
Householder productKK reflection vectorsO(d)O(d)(−1)K(-1)^KKK rank-1 updatesgf.HouseholderRotation
Cayley mapskew AASO(d)SO(d)+1one linear solvegf.OrthogonalRotation
matrix exponentialskew AASO(d)SO(d)+1one exp⁡\exp(jsl.expm)

All three are orthogonal by construction, so they train under unconstrained gradient descent with the log-determinant pinned at 0 — a free, learnable mixing layer for a flow. The only catch is the determinant-parity wall: match the target’s orientation (even/odd reflections), or stay in SO(d)SO(d) on purpose.

Next up. Sometimes we do not want to learn the rotation — we want to fix it from the data (a PCA frame) and only learn the marginals, or warm-start a trainable stack from a target QQ. 02 — Fixed orthogonal & PCA warm starts covers gf.FixedRotation, the PCA frame, and how to initialise a Householder stack to match a given QQ.

References
  1. Tomczak, J. M., & Welling, M. (2016). Improving Variational Auto-Encoders using Householder Flow. NeurIPS Workshop on Bayesian Deep Learning.