Skip to content

GDP income-identity assert is a no-op, masking a rental-tax double-count #101

Description

@nuochan

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions