99 ContinuousAction ,
1010 ContinuousState ,
1111 FloatND ,
12+ ScalarFloat ,
1213)
1314
1415
1516def capital_income (
1617 assets : ContinuousState ,
17- rate_of_return : float ,
18+ rate_of_return : ScalarFloat ,
1819) -> FloatND :
1920 """Compute capital income from assets."""
2021 return assets * rate_of_return
@@ -35,41 +36,74 @@ def cash_on_hand(
3536 return assets + after_tax_income + ssi_benefit - hic_premium
3637
3738
38- def transfers (
39- cash_on_hand : FloatND ,
40- consumption_floor : float ,
39+ def consumption_dollars_floor (
40+ consumption_equiv_floor : ScalarFloat ,
4141 equivalence_scale : FloatND ,
4242) -> FloatND :
43- """Government transfers to enforce consumption floor.
43+ """Per-household $-floor on consumption."""
44+ return consumption_equiv_floor * equivalence_scale
4445
45- tr = max{0, C_min * equivalence_scale - cash_on_hand}
46- """
47- floor = consumption_floor * equivalence_scale
48- return jnp .maximum (0.0 , floor - cash_on_hand )
46+
47+ def transfers (
48+ cash_on_hand : FloatND ,
49+ consumption_dollars_floor : FloatND ,
50+ ) -> FloatND :
51+ """Government transfers to enforce the consumption floor."""
52+ return jnp .maximum (0.0 , consumption_dollars_floor - cash_on_hand )
4953
5054
5155def next_assets (
5256 cash_on_hand : FloatND ,
5357 transfers : FloatND ,
5458 pension_assets_adjustment : FloatND ,
55- consumption : ContinuousAction ,
59+ consumption_dollars : ContinuousAction ,
5660 oop_costs : FloatND ,
5761) -> ContinuousState :
58- """Compute beginning-of-next-period assets.
62+ """Compute beginning-of-next-period assets for non-terminal targets .
5963
6064 OOP health costs are deducted here (not from cash_on_hand) so that the
6165 consumption choice does not condition on the HCC shock realization.
6266 """
6367 return (
64- cash_on_hand + transfers + pension_assets_adjustment - consumption - oop_costs
68+ cash_on_hand
69+ + transfers
70+ + pension_assets_adjustment
71+ - consumption_dollars
72+ - oop_costs
6573 )
6674
6775
68- def borrowing_constraint (
69- consumption : ContinuousAction ,
76+ def next_assets_when_dead (
7077 cash_on_hand : FloatND ,
7178 transfers : FloatND ,
72- pension_assets_adjustment : FloatND ,
79+ consumption_dollars : ContinuousAction ,
80+ oop_costs : FloatND ,
81+ ) -> ContinuousState :
82+ """Compute beginning-of-next-period assets for the dead/terminal target.
83+
84+ No `pension_assets_adjustment` term: with no future, there is no
85+ next-period pension wealth to impute against. Avoiding the dependency
86+ also keeps the `dead` per-target transition's DAG free of `next_aime`
87+ (which would otherwise need to come from a transition `dead` does not
88+ have, since `aime` is not a state in the terminal regime).
89+ """
90+ return cash_on_hand + transfers - consumption_dollars - oop_costs
91+
92+
93+ def borrowing_constraint (
94+ consumption_dollars : ContinuousAction ,
95+ cash_on_hand : FloatND ,
96+ consumption_dollars_floor : FloatND ,
7397) -> BoolND :
74- """Consumption cannot exceed available resources (no borrowing)."""
75- return consumption <= cash_on_hand + transfers + pension_assets_adjustment
98+ """Consumption cannot exceed post-transfer resources.
99+
100+ Post-transfer resources are `max(cash_on_hand, consumption_dollars_floor)`:
101+ the transfer system tops `cash_on_hand` to the floor when below,
102+ otherwise resources are unchanged. The algebraic identity is
103+ `cash_on_hand + transfers == max(cash_on_hand, floor)`; the `max`
104+ form is preferred because the additive form rounds to `floor + ε`
105+ (with `|ε| ~ ULP(|cash_on_hand|)`) at extreme cash, which flips
106+ the kink-boundary comparison at large negative values of `assets`.
107+ The `max` form returns `floor` exactly.
108+ """
109+ return consumption_dollars <= jnp .maximum (cash_on_hand , consumption_dollars_floor )
0 commit comments