Skip to content

Bequest receipts don't sum to bequests left when demographics vary by income group #1186

Description

@marcelolafleur

With the new income-group demographics (PR #1165), a model calibrated with a mortality gradient fails the steady-state resource-constraint check. In an OG-ZAF calibration with South African mortality gradients, the violation is about 1% of GDP: RuntimeError: Steady state aggregate resource constraint not satisfied, resource constraint ≈ −0.0105.

The cause is in household.get_bq, in the use_zeta = False branch. Per-capita bequests are computed as BQ[j] / lambdas[j], which treats each group's population share as its birth share. That was exact before #1165. With a mortality gradient, groups no longer have λ-proportional populations — in our calibration the poorest group's actual population share is 0.226 against λ = 0.25, and the top group's is 0.011 against λ = 0.010 — so each group's receipts total BQ[j] × (actual share / λ_j) instead of BQ[j]. Bequests received no longer equal bequests left: with savings concentrated in the (longer-lived) top groups, households collectively receive about 6.5% more bequests than exist, and the phantom resources show up in the resource constraint.

Reproducer against ogcore 0.18.0 (any Specifications with a mortality gradient):

omega_SS = np.asarray(p.omega_SS)          # (S, J), non-separable under gradients
b = np.outer(np.linspace(0.5, 2.0, p.S),   # savings tilted toward top groups
             np.array([0.2, 0.4, 0.7, 1.2, 2.0, 4.0, 10.0]))
BQ = np.asarray(aggregates.get_BQ(0.04, b, None, p, "SS", False)).ravel()
received = sum((household.get_bq(BQ, j, p, "SS") * omega_SS[:, j]).sum()
               for j in range(p.J))
# BQ.sum() = 0.031880, received = 0.033957  ->  +6.5% over-distribution

The use_zeta = True branch was already updated in #1165 — it divides by the actual omega_SS[:, j] / omega — so the fix is to give the non-zeta branch the same treatment: divide each group's pool by its actual population share (omega_SS[:, j].sum() in the SS, and the per-period group shares along the time path) instead of lambdas[j]. With demographics common across groups the two coincide, so existing results are unchanged.

draft PR to follow. cc: @rickecon @jdebacker

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