[WIP] Habit formation consumption-saving model#1739
Conversation
Programmed this on Monday, moving to PR branch now.
Using lowercase and uppercase gamma to mean different things wasn't great.
|
This currently has no tests and no notebook. I'll get a simple demonstration notebook up and running right now, but it will be heavily revised before merging. |
This doesn't have information about parameters or constructors, but it does have a neat demonstration of what habits do.
Fix a few words in notebook and comments.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 10 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| default_ = { | ||
| "params": HabitConsumerType_defaults, | ||
| "solver": solve_one_period_ConsHabit, | ||
| "model": "ConsHabit.yaml", | ||
| "track_vars": ["aNrm", "cNrm", "mNrm", "hNrm", "pLvl"], | ||
| } |
There was a problem hiding this comment.
This PR adds a new public consumer type (HabitConsumerType) but doesn’t add any tests. There are analogous smoke/behavior tests for other consumption-saving models under tests/ConsumptionSaving/; please add at least a basic test that (1) instantiates HabitConsumerType with defaults, (2) calls solve(), and (3) runs initialize_sym() + symulate() for a few periods to ensure the YAML model spec and solver stay in sync.
| - LivPrb \\ survival probability at end of period | ||
| - HabitRte \\ rate of habit stock updating | ||
| functions: | ||
| - cFunc* \\ consumption function over market resources |
There was a problem hiding this comment.
cFunc* is described as “consumption function over market resources”, but in this model it’s called with two arguments (mNrm, hNrm). Consider updating the description to reflect that the policy depends on both market resources and the normalized habit stock, to avoid confusion when using the simulator’s model introspection.
| - cFunc* \\ consumption function over market resources | |
| - cFunc* \\ consumption function of normalized market resources mNrm and habit stock hNrm |
| class HabitConsumerType(AgentType): | ||
| r""" | ||
| A class for representing consumers who form consumption habits. Agents get | ||
| flow utility according to a CRRA felicity function that depends on both current | ||
| consumption and the habit stock h_t. The habit stock evolves as a weighted | ||
| average of current consumption and prior habit stock. Consumers can save in | ||
| a single risk-free asset, so this model is an extension of the workhorse | ||
| IndShockConsumerType to include a habit stock. | ||
|
|
There was a problem hiding this comment.
The PR description says this model “extends IndShockConsumerType”, but HabitConsumerType currently inherits directly from AgentType. If this is intentional, please update the PR description/docstring accordingly; if not, inheriting from IndShockConsumerType would reuse the existing restriction checks, simulation hooks, and established API behavior.
| Parameters | ||
| ---------- | ||
| hLogInitMean : float | ||
| Mean of log habit stockfor newborns. |
There was a problem hiding this comment.
Typo in docstring: “stockfor” should be “stock for”.
| Mean of log habit stockfor newborns. | |
| Mean of log habit stock for newborns. |
| - Rfree \\ risk free return factor on assets | ||
| - PermGroFac \\ expected permanent income growth factor | ||
| - LivPrb \\ survival probability at end of period | ||
| - HabitRte \\ rate of habit stock updating | ||
| functions: | ||
| - cFunc* \\ consumption function over market resources | ||
| distributions: | ||
| - IncShkDstn \\ joint distribution of permanent and transitory shocks |
There was a problem hiding this comment.
In most consumption-saving YAML model specs that are used by initialize_sym/symulate, time-varying objects that should be indexed with a one-period offset are flagged with + (or listed under symbols: offset:), e.g. Rfree+, PermGroFac+, and IncShkDstn+ in ConsPortfolio/ConsLaborIntMarg. ConsHabit.yaml currently declares these without +, which will change how make_simulator_from_agent selects period-specific values for these sequences. Please confirm the intended timing and add + flags (or an explicit symbols: offset: list) if the habit model should match the standard consumption model timing.
| - Rfree \\ risk free return factor on assets | |
| - PermGroFac \\ expected permanent income growth factor | |
| - LivPrb \\ survival probability at end of period | |
| - HabitRte \\ rate of habit stock updating | |
| functions: | |
| - cFunc* \\ consumption function over market resources | |
| distributions: | |
| - IncShkDstn \\ joint distribution of permanent and transitory shocks | |
| - Rfree+ \\ risk free return factor on assets | |
| - PermGroFac+ \\ expected permanent income growth factor | |
| - LivPrb+ \\ survival probability at end of period | |
| - HabitRte \\ rate of habit stock updating | |
| functions: | |
| - cFunc* \\ consumption function over market resources | |
| distributions: | |
| - IncShkDstn+ \\ joint distribution of permanent and transitory shocks |
| Grid of consumption habit stocks on which to solve the problem. | ||
| FOCinverter : HabitFormationInverter | ||
| Function that inverts the first order conditions to yield optimal consumption | ||
| and the decision-time habit stock from which it was chose. |
There was a problem hiding this comment.
Minor grammar in docstring: “from which it was chose” should be “from which it was chosen”.
| and the decision-time habit stock from which it was chose. | |
| and the decision-time habit stock from which it was chosen. |
| "LivPrb": [0.98], # Survival probability after each period | ||
| "PermGroFac": [1.01], # Permanent income growth factor | ||
| "BoroCnstArt": 0.0, # Artificial borrowing constraint | ||
| "HabitWgt": 0.5, # Weight on consumption habit |
There was a problem hiding this comment.
Parameter comment mismatch: HabitWgt is described elsewhere (and in the PR description) as the exponent on habit stock in utility, but the default params comment says “Weight on consumption habit”. Consider updating the inline comment to “Exponent on habit stock in utility” (or similar) for consistency.
| "HabitWgt": 0.5, # Weight on consumption habit | |
| "HabitWgt": 0.5, # Exponent on habit stock in utility |
| def __call__(self, H, chi): | ||
| h = self.hFunc(H, chi) | ||
| rate = self.rate | ||
| c = (H - (1 - rate) * h) / (rate) | ||
| return c, h |
There was a problem hiding this comment.
HabitFormationInverter.call/cFunc divides by rate (HabitRte). If HabitRte is 0 (which the docstring describes as a valid edge case), this will raise a ZeroDivisionError and the model can't be solved. Consider validating 0 < HabitRte < 1 at construction time (and update the docstring), or implement an explicit HabitRte==0 branch with the correct limiting FOC logic.
| cNrm = np.concatenate((np.zeros((1, HabitGrid.size)), cNrm), axis=0) | ||
| mNrm = np.concatenate((aNrmMin * np.ones((1, HabitGrid.size)), mNrm), axis=0) | ||
| hBot = np.reshape(HabitGrid / (1.0 - HabitRte), (1, HabitGrid.size)) | ||
| hNrm = np.concatenate((hBot, hNrm), axis=0) |
There was a problem hiding this comment.
hBot = HabitGrid / (1.0 - HabitRte) will divide by zero when HabitRte==1, and the rest of the solver/inverter also assumes HabitRte is strictly between 0 and 1. Please either (a) enforce 0 < HabitRte < 1 with a clear ValueError early in solve_one_period_ConsHabit/constructor, or (b) add a dedicated HabitRte==1 limiting-case implementation.
| Returns | ||
| ------- | ||
| HabitInitDstn : DiscreteDistribution | ||
| Discretized distribution of initial capital holdings for newborns. |
There was a problem hiding this comment.
The Returns section of make_lognormal_habit_init_dstn says it returns “initial capital holdings for newborns”, but this function constructs the initial habit stock distribution. Please update the Returns description to match what’s actually returned.
| Discretized distribution of initial capital holdings for newborns. | |
| Discretized distribution of initial (normalized) habit stock for newborns. |
This PR adds a new consumption-saving model that extends$\alpha$ ), the exponent on the habit stock when used as a divisor in the utility function and $\lambda$ ), the linear weight on current consumption when updating the habit stock.
IndShockConsumerTypeto include consumption habit formation. The two new parameters areHabitWgt(HabitRte(Utility:
$u(c,h) = (c/h^\alpha)^{1-\rho} / (1-\rho).$
Dynamics:
$H_t = \lambda c_t + (1-\lambda) h_t,$
$h_{t+1} = H_t / (\psi_{t+1} \Gamma_{t+1}).$
The end-of-period habit stock$H_t$ acts as a second post-state variable. Note that we also apply the permanent income normalization to $h_t$ , so when time advances to $t+1$ we divide it by realized permanent income growth. The permanent income scaling exponent in the Bellman equation is also changed by the presence of habits.
The model solves pretty quickly, at least once the numba helper function has compiled.