From 9f2cf2c2571aaa598adfe5b10541fce280d37ecb Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 21 Jul 2026 15:18:13 +0100 Subject: [PATCH] Reduce CGT band donors to 30 per band for L0 sparsity economics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first full 512-epoch build with 300 donors per band passed the taxpayer-count outcome test but failed total gains (£20.7bn vs £65.9bn) and concentration (20% vs ~61% from £1m+): the L0-regularised calibration pays a gate penalty per active record, and keeping 2,700 light donors (weight ~7 each in the top band) open cost more than the band targets recovered, so donor weight shrank ~8x and the £2m+ bands landed 94-97% under target. The outcome tests gated the upload, so nothing was published. Thirty donors per band carry the same band-exact initial totals with a tenth of the gate cost. Co-Authored-By: Claude Fable 5 --- changelog.d/cgt-donor-sparsity.md | 1 + .../datasets/imputations/capital_gains.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog.d/cgt-donor-sparsity.md diff --git a/changelog.d/cgt-donor-sparsity.md b/changelog.d/cgt-donor-sparsity.md new file mode 100644 index 00000000..d8c1e698 --- /dev/null +++ b/changelog.d/cgt-donor-sparsity.md @@ -0,0 +1 @@ +- Reduce CGT band donors from 300 to 30 per HMRC size-of-gain band. The L0-regularised calibration pays a gate penalty per active record, so the first full build with 300 light donors per band (weight ~7 each in the top band) shrank total donor weight ~8x and left the £2m+ bands 94-97% under target, failing the built-dataset outcome tests before upload. Thirty donors per band carry the same band-exact initial totals with a tenth of the gate cost, making the per-band HMRC targets cheap for the optimiser to satisfy. diff --git a/policyengine_uk_data/datasets/imputations/capital_gains.py b/policyengine_uk_data/datasets/imputations/capital_gains.py index 4d048906..a7271d7c 100644 --- a/policyengine_uk_data/datasets/imputations/capital_gains.py +++ b/policyengine_uk_data/datasets/imputations/capital_gains.py @@ -189,7 +189,14 @@ def impute_capital_gains(dataset: UKSingleYearDataset) -> UKSingleYearDataset: # most likely to realise gains rather than on uniformly random households. HMRC_SIZE_BANDS_FILE = "capital_gains_size_distribution_hmrc.csv" -DONORS_PER_BAND = 300 +# Few, heavy donors rather than many light ones: the L0-regularised +# calibration pays a gate penalty per active record, so a band represented +# by 300 rows of weight ~7 is far more expensive to keep than 30 rows of +# weight ~67 carrying the same band total. The first full build with 300 +# donors/band shrank donor weight ~8x and left the top bands 94-97% under +# target; fewer gates make satisfying the band targets cheap for the +# optimiser while donors still spread across income profiles. +DONORS_PER_BAND = 30 _DONOR_SEED = 1 # HMRC's rows below £12,300 mix AEA regimes and are definitionally # incomplete (see the CSV header note); the spline body already covers