Summary
The construction-time GDP identity check in economy_ts.py never validates the income leg: its second assertion compares output to expenditure a second time rather than to income. This latent no-op hides a genuine income-side accounting error in synthetic_central_government.py, where the rental income tax is levied on total rent paid — including social-housing tenants, whose rent is government revenue rather than private landlord income.
1. Income identity is never enforced — macromodel/economy/economy_ts.py
current_output = ts.current("gdp_output")[0]
current_expenditure = ts.current("gdp_expenditure")[0]
current_income = ts.current("gdp_income")[0]
assert np.isclose(current_output, current_expenditure), (
f"mismatch, output/expenditure GDP: {current_output / current_expenditure}"
)
assert np.isclose(current_output, current_expenditure), ( # compares expenditure again
f"mismatch, output/income GDP: {current_output / current_income}"
)
The second assertion's message references the income identity, but the comparison operand is current_expenditure. The income leg is therefore unchecked.
Fix:
assert np.isclose(current_output, current_income), (
f"mismatch, output/income GDP: {current_output / current_income}"
)
2. Latent defect this exposes: rental income tax on social-housing rent — macro_data/processing/synthetic_central_government/synthetic_central_government.py
total_rent_paid = synthetic_population.household_data["Rent Paid"].sum()
rental_income_tax = tax_data.income_tax * total_rent_paid
total_rent_paid includes social-housing tenants (Tenure Status == -1), whose rent is booked separately as government revenue (Total Social Housing Rent). Taxing it as landlord income double-counts that rent on the income side of the GDP identity. The over-statement is income_tax × total_social_housing_rent, so it is zero for any economy without social-housing renters and grows with the social-housing stock otherwise.
Fix — levy the tax on private rent only, subtracting the social-housing rent already computed in update_fields for the
Total Social Housing Rent line:
rental_income_tax = tax_data.income_tax * (total_rent_paid - total_social_housing_rent)
Impact
With (1) fixed, the income identity is enforced for the first time, and (2) then causes the output == income assertion to fail at construction for any economy containing social-housing tenants — aborting the build before any timestep. The same over-stated rental_income_tax also flows into Income Taxes and total Revenue, which seed the central government agent's initial fiscal state, so the error is not confined to the identity check. Both are one-line fixes.
Summary
The construction-time GDP identity check in
economy_ts.pynever validates the income leg: its second assertion compares output to expenditure a second time rather than to income. This latent no-op hides a genuine income-side accounting error insynthetic_central_government.py, where the rental income tax is levied on total rent paid — including social-housing tenants, whose rent is government revenue rather than private landlord income.1. Income identity is never enforced —
macromodel/economy/economy_ts.pyThe second assertion's message references the income identity, but the comparison operand is
current_expenditure. The income leg is therefore unchecked.Fix:
2. Latent defect this exposes: rental income tax on social-housing rent —
macro_data/processing/synthetic_central_government/synthetic_central_government.pytotal_rent_paidincludes social-housing tenants (Tenure Status == -1), whose rent is booked separately as government revenue (Total Social Housing Rent). Taxing it as landlord income double-counts that rent on the income side of the GDP identity. The over-statement isincome_tax × total_social_housing_rent, so it is zero for any economy without social-housing renters and grows with the social-housing stock otherwise.Fix — levy the tax on private rent only, subtracting the social-housing rent already computed in
update_fieldsfor theTotal Social Housing Rentline:Impact
With (1) fixed, the income identity is enforced for the first time, and (2) then causes the
output == incomeassertion to fail at construction for any economy containing social-housing tenants — aborting the build before any timestep. The same over-statedrental_income_taxalso flows intoIncome Taxesand totalRevenue, which seed the central government agent's initial fiscal state, so the error is not confined to the identity check. Both are one-line fixes.