mixle.models.moment_propagation module¶
Gaussian(-mixture) law propagation through the real causal transformer in mixle.models.transformer.
This is a surrogate: instead of running a forward pass on concrete activations, it pushes a Gaussian LAW
x ~ N(mu, Sigma) (representing the distribution of a token’s residual-stream vector under some input
distribution) analytically through each layer type the real CausalLM is built from – nn.Linear,
nn.LayerNorm, nn.GELU, and mixle.models.transformer.CausalAttention – and returns the
propagated law at every layer together with a genuine, locally-computed “closure error” receipt: how far the
closed-form law is from a small Monte Carlo sample pushed through the REAL torch layer at that point.
Propagated laws are represented with this codebase’s own
mixle.stats.multivariate.multivariate_gaussian.MultivariateGaussianDistribution (mean + full
covariance), not a parallel numpy-only representation.
What is exact vs. approximate¶
Linear (
y = Wx + b): exact. A Gaussian pushed through an affine map is exactly Gaussian.Attention: the output-given-query map is derived to be exactly affine in the query (see
attention_law()), via the MGF identity for jointly-Gaussian (K, V). Composed with the exactly-linearqkv/projprojections, the whole attention branch is an exact affine function of its LayerNorm’d input – conditional on treating the key/value population as a single stationary Gaussian (see caveats inattention_law()).LayerNorm: nonlinear and data-dependent; propagated via a first-order Taylor (“re-anchoring”) expansion of the true LayerNorm map around the input mean (see
layernorm_law()for the closed-form Jacobian and documented failure modes).GELU: the first two output MOMENTS (mean and per-dimension variance) are exact closed-form expressions in
(mu, sigma)(derived via Stein’s lemma + the bivariate normal CDF, seegelu_law()); the OFF-diagonal output covariance is a first-order (Jacobian) linearization – the same “delta method” used for LayerNorm’s covariance push-forward.Residual connections (
x + branch(x)): the two summands are correlated (both are functions of the samex), which the propagation accounts for through a chained JACOBIAN of the branch mapping (composed from the exact/linearized per-layer Jacobians above), givingCov(x, branch(x)) ~= Sigma_x @ J_branch^Tand hence an (approximately) correctSigma_out = Sigma_x + Sigma_branch + Cov + Cov^T.
Execution contract (streaming / layer-local / constant memory)¶
propagate_moments() mirrors the walking pattern of
mixle.inference.precision_plan.recommend_compute_precision(): it inspects the model’s structure and
processes it piece by piece rather than materializing everything at once. Concretely, at any point during the
walk only (a) the CURRENT running law (mu, Sigma) – an O(d_model^2) object – and (b) the ONE block
currently being processed are resident; once a block’s output law and closure-error receipt are recorded, its
intermediate quantities (the attention MGF terms, the GELU Jacobian, the local Monte Carlo samples used for
the receipt, etc.) are dropped. This is the moment-propagation analogue of a real forward pass that would
otherwise have to materialize per-layer activations for the whole depth of the network simultaneously (or
lean on gradient checkpointing to avoid it, as CausalLM.gradient_checkpointing does for the real module).
Peak memory therefore does not scale with network depth – only with d_model – which is verified directly
in mixle/tests/moment_propagation_test.py.
References
Stein’s lemma (Gaussian integration by parts):
E[(X-mu) g(X)] = sigma^2 E[g'(X)]forX ~ N(mu, sigma^2)– used throughout to get closed forms forE[GELU(X)]and its derivative.The Gaussian-product / MGF identity
E[Y e^{t^T X}] = M_X(t) (mu_Y + Sigma_YX t)for jointly Gaussian(X, Y)– the exact identity behindattention_law().Data-free quantization (DFQ) BatchNorm-based calibration is the closest prior art for the LayerNorm “re-anchoring” step: both re-derive a cheap closed-form summary of what a normalization layer does to a law, without touching real data.
- class LayerMoments(index, name, law, closure_error)[source]
Bases:
objectOne layer’s propagated law plus its locally-computed closure-error receipt.
- linear_law(law, weight, bias=None)[source]
Exact Gaussian push-forward of
y = Wx + b.x ~ N(mu, Sigma) => y ~ N(W mu + b, W Sigma W^T)exactly – no approximation. Returns the new law together with the Jacobiandy/dx = W(used to chain residual cross-covariances).
- layernorm_law(law, weight, bias, eps=1e-5)[source]
Propagate a Gaussian law through
LayerNorm:y = weight * (x - m(x)) / sqrt(v(x) + eps) + bias, wherem(x) = mean_d(x)andv(x) = mean_d((x - m(x))^2)are the PER-SAMPLE (per-token) statistics LayerNorm computes over the feature axis – this is the nonlinear, data-dependent step in the block.Derivation (“re-anchoring”)¶
LayerNorm has no closed-form pushforward of a full law in general (
m(x)andv(x)are themselves random, nonlinear functions ofx). We use a first-order Taylor expansion around the CURRENT meanmu, i.e. anchor the (unknown, per-sample) normalization statistics at their EXPECTED values under the current law, not merely their values evaluated at the mean vector.m(x) = mean_d(x)is itself LINEAR inx, soE[m(x)] = mean_d(mu)exactly – no correction needed there. Butv(x) = mean_d((x - m(x))^2)is QUADRATIC inx, so evaluating it atmualone (v(mu) = mean((mu - m)^2)) drops a systematic bias term: writingP = I - (1/d) 11^Tfor the feature-centering projector (symmetric, idempotent),v(x) = (1/d) x^T P x, and the standard quadratic-form expectation identity givesE[v(x)] = v(mu) + (1/d) trace(P @ Sigma) = v(mu) + (1/d) (trace(Sigma) - (1/d) sum(Sigma)).The second term is the “spread of x around its own per-token mean” contribution that
v(mu)alone misses entirely; it is NOT a higher-order correction that can be dropped onceSigmais non-negligible relative tod– for smalld_model(e.g. an 8-wide toy model) it routinely dominatesv(mu), which without this correction makes the anchoredstd = sqrt(v(mu) + eps)far too small and blows the propagated mean/covariance up by an order of magnitude relative to the true LayerNorm output law. We therefore re-anchor at the BIAS-CORRECTEDv = E[v(x)]above (still a first-order/delta-method treatment of the covariance push-forward – only the anchor point forvis corrected, not the linearization itself). This is the LLM analogue of BatchNorm-based data-free-quantization (DFQ) calibration, which likewise re-derives cheap closed-form layer statistics (there, running mean/var; here, the expected LayerNorm mean/var under the CURRENT propagated law) without touching real data.The mean is propagated by evaluating the true (nonlinear) LayerNorm map at
mubut with the bias-correctedv:mu_out = weight * (mu - m) / sqrt(v + eps) + bias.The covariance is propagated via the JACOBIAN of LayerNorm evaluated at
muwith the same bias-correctedv(a standard, textbook LayerNorm-backward-style derivative applied at the corrected anchor):d(norm_i)/d(x_j) |_{x=mu} = (1/sqrt(v+eps)) * (delta_ij - 1/d - (mu_i - m)(mu_j - m) / (d*(v+eps)))J_ij = weight_i * d(norm_i)/d(x_j)Sigma_out ~= J Sigma J^T.Known failure modes (feeds the closure-error receipt)¶
Small ``d_model``:
m(x)andv(x)are averages over onlyd_modelfeatures, so their sample-to-sample fluctuation around their (now bias-corrected) expectation – which this delta-method approximation still ignores, since the Jacobian itself is frozen at the anchor – is large relative to their magnitude whend_modelis small. The approximation is progressively worse asd_modelshrinks, even after the mean-bias correction above.Heavy-tailed pre-norm activations: the first-order Taylor expansion is only locally valid; if the input law has high kurtosis / is far from Gaussian in practice (despite being MODELED as Gaussian here), the true per-sample
(m, v)can swing far from their Gaussian-law expectations, and the linearization degrades.A deep stack of blocks compounds both effects: even a small per-layer LayerNorm error can accumulate across
n_layerre-anchoring steps.
- gelu_law(law)[source]
Propagate a Gaussian law through elementwise
GELUusing the closed-form per-dimension moments in_gelu_scalar_moments().The per-dimension MEAN and VARIANCE are exact closed-form expressions (no Monte Carlo, no approximation of the GELU functional form). The OFF-diagonal output covariance –
Cov(GELU(x_i), GELU(x_j))fori != j– has no simple closed form (it needs the joint bivariate distribution of(x_i, x_j)through a nonlinearity) and is instead approximated by the standard delta-method / Price’s-theorem-style linearizationCov(y_i, y_j) ~= J_ii * J_jj * Cov(x_i, x_j)withJ_ii = d E[GELU(x_i)]/d mu_ithe EXACT mean-derivative from Stein’s lemma. The diagonal is then overwritten with the exact closed-form variance so the marginal moments stay exact even though the correlation structure is linearized.
- attention_law(law, qkv_weight, qkv_bias, proj_weight, proj_bias, n_head)[source]
Propagate a Gaussian law through one
mixle.models.transformer.CausalAttentionlayer.Modeling assumption: the key/value population attended over (across sequence positions) is treated as a single STATIONARY Gaussian – the same joint law as the query’s – rather than tracking per-position laws. This is the “R2 MGF” population-level approximation the roadmap specifies; it does not model the causal mask explicitly (a real causal mask makes early positions attend to a smaller, non-stationary population). That mismatch is a known limitation, checked directly against Monte Carlo softmax attention in
mixle/tests/moment_propagation_test.py(tightest for a roughly-stationary population, looser for strongly non-stationary / short sequences).Derivation¶
For jointly Gaussian
(K, V)(a key vector and its associated value vector from the SAME token) and a fixed queryq, the requested MGF identity isE[exp(q^T K / sqrt(d)) V] = exp(q^T mu_K/sqrt(d) + 0.5 q^T Sigma_KK q / d) * (mu_V + Sigma_VK q / sqrt(d))which is the standard “
E[Y e^{t^T X}] = M_X(t) (mu_Y + Sigma_YX t)” identity for jointly Gaussian(X, Y)witht = q / sqrt(d),X = K,Y = V. The attention DENOMINATOR (the softmax normalizer) is exactly the same MGF evaluated withVreplaced by the constant1:E[exp(q^T K / sqrt(d))] = exp(q^T mu_K/sqrt(d) + 0.5 q^T Sigma_KK q / d).- Both share the identical exponential prefactor, so it CANCELS in the ratio:
softmax-attention-output(q) ~= E[exp(q^T K/sqrt(d)) V] / E[exp(q^T K/sqrt(d))] = mu_V + (Sigma_VK / sqrt(d)) q.
This is a remarkably clean result: the MGF-approximated attention output, as a function of the query, is EXACTLY AFFINE in
q(no exponential term survives). Sinceqitself has a propagated Gaussian law (the marginal of the joint (Q, K, V) law after theqkvprojection), pushing that law through this affine map is exact (reusinglinear_law()’s formula) – there is no additional approximation beyond the population-stationarity assumption above and (for the covariance) the affine relation being evaluated with theSigma_VKestimated from the SAME joint law used for the mean.Per head, per token position,
y_h(q) = mu_{V,h} + (Sigma_{VK,h} / sqrt(d_head)) q_h. Stacking heads into a block-diagonal mapA(each head only reads its own query slice) and reusing the FULL (cross-head) query covariance from theqkvprojection gives the cross-head covariance of the output “for free” – cross-head correlation inQ(already present inSigma_QQ’s off-diagonal blocks) propagates throughA Sigma_QQ A^Teven thoughAitself only mixes within a head.
- propagate_moments(model, input_law, n_mc=128, seq_len=16, seed=0)[source]
Streaming, layer-local, constant-memory Gaussian-law propagation through a real
mixle.models.transformer.CausalLM.input_lawmodels the distribution of a token’s residual-stream vector ENTERING the block stack (i.e. after token + position embedding) – anN(mu, Sigma)overR^{d_model}.Execution contract: this walks
model.blocksone block at a time, then the finalmodel.lnandmodel.head. At each step only the CURRENT running law and the layer being processed are resident; see the module docstring for the full memory-contract discussion and its relation tomixle.inference.precision_plan.recommend_compute_precision()’s inspect-then-decide walking pattern. Verified empirically (peak memory vs. depth) inmixle/tests/moment_propagation_test.py.Returns a list of
LayerMoments, one per block plus one for the final LayerNorm and one for the (weight-tied) output head, in execution order.