The WAS imputation saved `property_wealth` (DVPropertyR8) as its own
household column. policyengine-uk defines `property_wealth` as the sum of
main_residence_value, other_residential_property_value and
non_residential_property_value, and a saved column overrides that sum. The
saved column has no uprating index, so on release 1.57.4 it was identical per
household in 2024, 2025 and 2026 while its components were uprated, and in
2026 it fell below main_residence_value for 29,677 of 35,672 owner
households. Household land, total land and total wealth inherited the freeze.
Keep the WAS total in the sequential QRF, since the later outputs are
predicted conditional on it, but stop writing it, and stop rescaling it in the
regional HPI step. Add synthetic (Hypothesis) and built-dataset tests that
property_wealth equals the sum of its components in every year, grows with
them, and is at least main_residence_value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Draft. This changes the built data, so it lands only in a batched uk-data release, on Max's go (decision d833). Please don't merge or release it on its own.
Part of the constituency-level LVT analysis: household land is
property_wealth × regional intensity + owned_land, so a frozenproperty_wealthfreezes household land.The bug
impute_wealthsaves the WAS totalDVPropertyR8as a household column,property_wealth(datasets/imputations/wealth.py:50, 66, written by the loop at:343-350on main).uprate_property_by_regionmultiplies owners'property_wealthby the same regional HPI factor asmain_residence_value(regional_property_uprating.py:58-59, 80on main).property_wealthwithadds = ["residential_property_value", "non_residential_property_value"], andresidential_property_valueaddsmain_residence_valueandother_residential_property_value. That holds at the locked 2.93.0 and at main4ec7ef1.property_wealthof [1, 2, 3], the model returned [1, 2, 3] in 2024 and in 2026. Without the column it returned the component sums, uprated.property_wealthhas noupratingattribute. It appears in neither policyengine-uk'sdata/uprating_indices.yaml(2.93.0 or main) nor uk-data'sstorage/uprating_factors.csv, whichcreate_policyengine_uprating_factors_tablegenerates from those attributes. The three components are in both.enhanced_frs_2024_25.h5, time_period 2024, policyengine-uk 2.93.0):property_wealth < main_residence_valuefor 1,291, then 28,937, then 29,677 of 35,672 owner households (2024, 2025, 2026).Is the saved value the sum of its components in the base year?
No. Measured on 1.57.4 in 2024, before any uprating:
main_residence_value£6,792.1bn,other_residential_property_value£1,895.9bn andnon_residential_property_value£246.1bn.What the saved value carries that the components do not
_get_sequential_predictors).property_wealthcomes second, before the three components, so they are drawn conditional on it. Nothing makes them sum to it.DVPropertyR8is UNVERIFIED. I could not read the WAS data dictionary in this task, so I can't say whether it is net of mortgages or includes land or overseas property. ONS describes published "net property wealth" as "the value of all properties minus mortgage debt". I have not confirmed thatDVPropertyR8is that variable. One pointer: the saved total is at least the main residence for 96.4% of owners in 2024, which would be unusual for a total net of mortgages. That is an inference from the imputed file, not a check against WAS.Why (a), dropping the saved column, rather than (b), uprating it
main_residence_valuefor 1,291 owners in the base year, and uprating does not change that.upratingattributes.total_wealthas gross assets:net_wealthsubtractsmortgage_debtfrom it (main4ec7ef1).household_land_valueaddsowned_land, the WASDVLUKValR8_sum.What this PR does
IMPUTE_VARIABLES. Removing it would change the predictor set, and so the draws, of every later output.CONDITIONING_ONLY_VARIABLESand is no longer written; the write loop moves intostore_wealth_predictions.uprate_property_by_regionno longer touches it.Side effects
Consumers in policyengine-uk (main
4ec7ef1, fromgit grep)property_wealth, so they change:household_land_value(property_wealth × intensity[region] + owned_land) →land_value→LVT.total_wealth→household_wealth_decile,wealth_tax(ubi_center),non_primary_residence_wealth_tax(cec) andnet_wealth.net_wealthexists on main but not in 2.93.0.LVT,wealth_taxandnon_primary_residence_wealth_taxfeedhousehold_taxandgov_tax, andLVTalso feedshbai_household_net_income. All three have default rates of 0, so baseline taxes don't move; reforms that set those rates do.capital/sources.yamllistsother_residential_property_valueandnon_residential_property_valueand excludes the main residence.high_value_council_tax_surcharge(main_residence_value).main_residence_value,other_residential_property_value,non_residential_property_value).corporate_land_valueand the employer NI capital incidence (corporate_sector_wealth).Measured on 1.57.4's weights with the column dropped (no recalibration)
property_wealthhousehold_land_valueland_valuetotal_wealthhousehold_wealth_decilechanges for 10.3% of households (weighted) in both 2025 and 2026: 6.5% move up and 3.8% move down.Calibration targets
property_wealthdirectly.mhclg_regional_land.pybuilds its ownproperty_wealthcolumn from dwellings × average price, for regional shares only.household_land_value:ons/household_land_value;ons/land_value, throughland_value;ons/household_land_value/{REGION}.targets/build_loss_matrix.py:306-315andtargets/compute/other.py:65-74, and the national matrix is used by both the constituency and local-authority calibrations.TESTING=1(32 epochs, 2 OA clones).test_land_value_targets.py(±65%) andtest_household_land_less_than_property_wealth, both at 1.57.4's weights.Invariants and how they're tested
property_wealthcolumntest_impute_wealth_does_not_save_property_wealth(impute_wealth with a stub model); the Hypothesis property;test_built_dataset_does_not_save_property_wealthproperty_wealth=main_residence_value + other_residential_property_value + non_residential_property_valueper household (rtol 1e-5, atol £1)uprate_datasetto the calibration yeartest_built_property_wealth_is_the_sum_of_its_components[year]property_wealth ≥ main_residence_valueper householdtest_built_property_wealth_at_least_main_residence[year]property_wealth(2026)/property_wealth(2024)equals the same ratio for the component sum (rtol 1e-5)test_built_property_wealth_grows_with_its_componentsIMPUTE_VARIABLESordertest_property_wealth_is_imputed_before_its_componentstest_saved_property_wealth_follows_its_components, 25 examples:store_wealth_predictions, thenuprate_property_by_region, then policyengine-uk.make datain CI and skip when the file is absent. Their failure messages give counts and sums only, never individual records.test_regional_property_uprating.py: the proportionalproperty_wealthtest is replaced bytest_only_main_residence_value_is_rescaled.Commands run and results
All commands ran in a fresh clone of
b45c373on Python 3.13.uv sync --frozen --all-extras --python 3.13: OK.uv lock --python 3.13addedhypothesis6.168.5, plussortedcontainersas its dependency.uv run --frozen pytest policyengine_uk_data/tests/test_property_wealth_components.py policyengine_uk_data/tests/test_regional_property_uprating.py policyengine_uk_data/tests/test_student_loan_balance.py: 15 passed, 8 skipped (the skips are the built-dataset tests, since no local build exists).CONDITIONING_ONLY_VARIABLES = (), so that the column is saved again, the Hypothesis property andtest_property_wealth_is_imputed_before_its_componentsboth fail.property_wealthcolumn dropped, plustest_land_value_targets.py: 18 passed.uv run --frozen ruff format --check .(ruff 0.15.5): 217 files already formatted.uv run --frozen pytest policyengine_uk_data/tests(full local suite, no local build): 524 passed, 121 skipped, 1 xfailed. The skips are the tests that need built or private data. CI builds the dataset and runs them; see the checks on this PR.1.57.4_enhanced_frs_2024_25.h5; the base-year comparison also ran on1.56.14_enhanced_frs_2024_25.h5.Not done
TESTINGbuild is the only rebuild.DVPropertyR8is unverified (see above).property_wealthdefensively; that is not done here.main_residence_valueonly. Before this PR it also scaled the WAS total for owners.other_residential_property_valueandnon_residential_property_valueare not rescaled, and whether they should be is a separate methodological question.hypothesisto the dev extras: whichever merges second needsuv lockrerun.🤖 Generated with Claude Code