Add WeightedZeroSumNormal distribution and transform - #8410
Conversation
|
|
Generalizes ZeroSumNormal to a weighted constraint sum(weights * x) = 0 along the last axis, i.e. N(0, sigma^2 (I - u u^T)) with u = w/|w|. With equal weights it reproduces ZeroSumNormal exactly (pointwise transform parity and logp parity are tested). The transform is the restriction of the Householder reflection sending u to -e_n (sign choice keeps the denominator 1 + u_n >= 1, so there is no near-singular regime for positive weights); it is an isometry, so log_jac_det = 0. Provided in both the tensor and pymc.dims APIs. Motivation: hierarchical models with lopsided group sizes (e.g. MMM campaign splits with 90/5/3/2 spend shares), where centring deviations under the spend-weighted mean instead of the uniform mean decorrelates the group mean from the dominant unit's deviation. Measured 6.5-8.7x ESS/sec on the group mean vs ZeroSumNormal on such cases. Related to pymc-devs#7874. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bcda022 to
d394c9b
Compare
Documentation build overview
17 files changed ·
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #8410 +/- ##
==========================================
+ Coverage 91.87% 91.89% +0.01%
==========================================
Files 128 128
Lines 21256 21386 +130
==========================================
+ Hits 19530 19653 +123
- Misses 1726 1733 +7
🚀 New features to boost your workflow:
|
|
Thanks for the PR — the math is right and cleanly implemented. I checked the Householder construction, the isometry claim, exact parity with That said, I don't think it is mergeable as-is: there are three functional bugs in exactly the regimes the PR description advertises (batched Blocking1. Batched
|

Description
Adds
pm.WeightedZeroSumNormal(andpmd.WeightedZeroSumNormalin the dims API): a generalization ofZeroSumNormalwhere draws satisfy a weighted zero-sum constraintsum(weights * x) = 0along the last axis, i.e.With equal weights it reproduces
ZeroSumNormalexactly — pointwise transform parity and logp parity are covered by tests.Motivation. In hierarchical models with lopsided group sizes, centring group deviations under a weighted mean instead of the uniform mean decorrelates the shared mean from the dominant unit's deviation. The motivating case is MMM campaign hierarchies (e.g. a 90/5/3/2 spend split within a channel), where the likelihood pins down the spend-weighted combination of effects.
Implementation notes.
WeightedZeroSumTransform(tensor and dims variants): the restriction of the Householder reflection sendinguto-e_n. Thev = u + e_nsign choice keeps the working denominator at1 + u_n ≥ 1for positive weights, so there is no near-singular regime. The map is an isometry, hencelog_jac_det = 0.WeightedZeroSumNormalRV(SymbolicRandomVariable) rather than extendingZeroSumNormalRV, keeping the existing RV's input signature untouched."[rng],[size],(),(n)->[rng],(n)"; the dimsxrv_optherefore needs no dummy support-shape dim.floatXat every entry point, and the logp's on-constraint check uses a dtype-aware tolerance (float32 draws satisfy the constraint only to ~1e-7).pmd.Data), soset_datakeeps the constraint in sync.Happy to adjust the API (e.g. folding this into
ZeroSumNormalas aweights=kwarg was considered and rejected to keep the existing RV signature and class behavior untouched — but open to maintainer preference).Related Issue
Checklist
Type of change
🤖 Generated with Claude Code