Coverage for HARK / ConsumptionSaving / ConsWealthUtilityModel.py: 99%
296 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-25 05:22 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-25 05:22 +0000
1"""
2This module has consumption-saving models with wealth directly in the utility function.
3Unlike the model in ConsWealthPortfolioModel.py, these models do not have portfolio
4allocation between a risky and riskless asset. Two AgentType subclasses will be covered:
61) WealthUtilityConsumerType: Agents who face transitory and permanent shocks to labor
7 income and who can save in a riskless asset, and have CRRA preferences over a Cobb-
8 Douglas composition of consumption and retained assets. This is WealthPortfolioConsumerType
9 with no portfolio allocation.
112) CapitalistSpiritConsumerType: Agents who face transitory and permanent shocks to labor
12 income and who can save in a riskless asset, and have *additive* CRRA preferences over
13 consumption and assets, with a *lower* CRRA for assets than consumption.
14"""
16import numpy as np
17from copy import deepcopy
19from HARK.distributions import expected
20from HARK.interpolation import (
21 ConstantFunction,
22 LinearInterp,
23 CubicInterp,
24 LowerEnvelope,
25 LowerEnvelope2D,
26 ValueFuncCRRA,
27 MargValueFuncCRRA,
28 MargMargValueFuncCRRA,
29 UpperEnvelope,
30 BilinearInterp,
31 VariableLowerBoundFunc2D,
32 LinearInterpOnInterp1D,
33)
34from HARK.Calibration.Income.IncomeProcesses import (
35 construct_lognormal_income_process_unemployment,
36 get_PermShkDstn_from_IncShkDstn,
37 get_TranShkDstn_from_IncShkDstn,
38 make_AR1_style_pLvlNextFunc,
39 make_pLvlGrid_by_simulation,
40 make_basic_pLvlPctiles,
41)
42from HARK.ConsumptionSaving.ConsIndShockModel import (
43 calc_boro_const_nat,
44 calc_m_nrm_min,
45 make_lognormal_kNrm_init_dstn,
46 make_lognormal_pLvl_init_dstn,
47 IndShockConsumerType,
48 ConsumerSolution,
49)
50from HARK.ConsumptionSaving.ConsGenIncProcessModel import (
51 GenIncProcessConsumerType,
52)
53from HARK.rewards import UtilityFuncCRRA, CRRAutility
54from HARK.utilities import NullFunc, make_assets_grid
57def make_terminal_solution_for_wealth_in_utility(CRRA, WealthShare, WealthShift):
58 """
59 Construct the terminal period solution for a consumption-saving model with
60 CRRA utility over a composite of wealth and consumption.
62 Parameters
63 ----------
64 CRRA : float
65 Coefficient of relative risk aversion.
66 WealthShare : float
67 Wealth's share in the Cobb-Douglas aggregator.
68 WealthShift : float
69 Additive shifter for wealth in the Cobb-Douglas aggregator.
71 Returns
72 -------
73 solution_terminal : ConsumerSolution
74 Terminal period solution for someone with the given CRRA.
75 """
76 if (WealthShift > 0.0) and (WealthShare > 0.0):
77 m_cusp = (1 - WealthShare) / WealthShare * WealthShift
78 m_terminal = np.array([0.0, m_cusp, m_cusp + 1.0])
79 c_terminal = np.array([0.0, m_cusp, m_cusp + (1.0 - WealthShare)])
80 else:
81 m_terminal = np.array([0.0, 1.0])
82 c_terminal = np.array([0.0, 1.0 - WealthShare])
84 cFunc_terminal = LinearInterp(m_terminal, c_terminal)
85 vFunc_terminal = ValueFuncCRRA(cFunc_terminal, CRRA)
86 vPfunc_terminal = MargValueFuncCRRA(cFunc_terminal, CRRA)
87 vPPfunc_terminal = MargMargValueFuncCRRA(cFunc_terminal, CRRA)
88 solution_terminal = ConsumerSolution(
89 cFunc=cFunc_terminal,
90 vFunc=vFunc_terminal,
91 vPfunc=vPfunc_terminal,
92 vPPfunc=vPPfunc_terminal,
93 mNrmMin=0.0,
94 hNrm=0.0,
95 MPCmin=1.0 - WealthShare,
96 MPCmax=1.0,
97 )
98 return solution_terminal
101def make_2D_CRRA_solution_empty(CRRA):
102 """
103 Construct the pseudo-terminal period solution for a consumption-saving model with CRRA
104 utility and two state variables: levels of market resources and permanent income.
105 All functions return zero everywhere.
107 Parameters
108 ----------
109 CRRA : float
110 Coefficient of relative risk aversion. This is the only relevant parameter.
112 Returns
113 -------
114 solution_terminal : ConsumerSolution
115 Terminal period solution for someone with the given CRRA.
116 """
117 cFunc_terminal = ConstantFunction(0.0)
118 vFunc_terminal = ConstantFunction(0.0)
119 vPfunc_terminal = ConstantFunction(0.0)
120 vPPfunc_terminal = ConstantFunction(0.0)
121 solution_terminal = ConsumerSolution(
122 cFunc=cFunc_terminal,
123 vFunc=vFunc_terminal,
124 vPfunc=vPfunc_terminal,
125 vPPfunc=vPPfunc_terminal,
126 mNrmMin=ConstantFunction(0.0),
127 hNrm=ConstantFunction(0.0),
128 MPCmin=1.0,
129 MPCmax=1.0,
130 )
131 solution_terminal.hLvl = solution_terminal.hNrm
132 solution_terminal.mLvlMin = solution_terminal.mNrmMin
133 return solution_terminal
136class ChiFromOmegaFunction:
137 """
138 A class for representing a function that takes in values of omega = EndOfPrdvPnvrs / aNrm
139 and returns the corresponding optimal chi = cNrm / aNrm. The only parameters
140 that matter for this transformation are the coefficient of relative risk
141 aversion (rho) and the share of wealth in the Cobb-Douglas aggregator (delta).
143 Parameters
144 ----------
145 CRRA : float
146 Coefficient of relative risk aversion.
147 WealthShare : float
148 Share for wealth in the Cobb-Douglas aggregator in CRRA utility function.
149 N : int, optional
150 Number of interpolating gridpoints to use (default 501).
151 z_bound : float, optional
152 Absolute value on the auxiliary variable z's boundary (default 15).
153 z represents values that are input into a logit transformation
154 scaled by the upper bound of chi, which yields chi values.
155 """
157 def __init__(self, CRRA, WealthShare, N=501, z_bound=15):
158 self.CRRA = CRRA
159 self.WealthShare = WealthShare
160 self.N = N
161 self.z_bound = z_bound
162 self.update()
164 def f(self, x):
165 """
166 Define the relationship between chi and omega, and evaluate on the vector
167 """
168 r = self.CRRA
169 d = self.WealthShare
170 return x ** (1 - d) * ((1 - d) * x ** (-d) - d * x ** (1 - d)) ** (-1 / r)
172 def update(self):
173 """
174 Construct the underlying interpolation of log(omega) on z.
175 """
176 # Make vectors of chi and z
177 chi_limit = (1.0 - self.WealthShare) / self.WealthShare
178 z_vec = np.linspace(-self.z_bound, self.z_bound, self.N)
179 exp_z = np.exp(z_vec)
180 chi_vec = chi_limit * exp_z / (1 + exp_z)
182 omega_vec = self.f(chi_vec)
183 log_omega_vec = np.log(omega_vec)
185 # Construct the interpolant
186 zFromLogOmegaFunc = LinearInterp(log_omega_vec, z_vec, lower_extrap=True)
188 # Store the function and limit as attributes
189 self.func = zFromLogOmegaFunc
190 self.limit = chi_limit
192 def __call__(self, omega):
193 """
194 Calculate optimal values of chi = cNrm / aNrm from values of omega.
196 Parameters
197 ----------
198 omega : np.array
199 One or more values of omega = EndOfPrdvP / aNrm.
201 Returns
202 -------
203 chi : np.array
204 Identically shaped array with optimal chi values.
205 """
206 z = self.func(np.log(omega))
207 exp_z = np.exp(z)
208 chi = self.limit * exp_z / (1 + exp_z)
209 return chi
212# Trivial constructor function
213def make_ChiFromOmega_function(CRRA, WealthShare, ChiFromOmega_N, ChiFromOmega_bound):
214 if WealthShare == 0.0:
215 return NullFunc()
216 return ChiFromOmegaFunction(
217 CRRA, WealthShare, N=ChiFromOmega_N, z_bound=ChiFromOmega_bound
218 )
221def utility(c, a, CRRA, share=0.0, intercept=0.0):
222 w = a + intercept
223 return (c ** (1 - share) * w**share) ** (1 - CRRA) / (1 - CRRA)
226def dudc(c, a, CRRA, share=0.0, intercept=0.0):
227 u = utility(c, a, CRRA, share, intercept)
228 return u * (1 - CRRA) * (1 - share) / c
231def calc_m_nrm_next(shocks, a_nrm, G, R):
232 """
233 Calculate future realizations of market resources mNrm from the income
234 shock distribution S and end-of-period assets a_nrm.
235 """
236 return R * a_nrm / (shocks["PermShk"] * G) + shocks["TranShk"]
239def calc_dvdm_next(shocks, a_nrm, G, R, rho, vp_func):
240 """
241 Evaluate realizations of marginal value of market resources next period,
242 based on the income distribution S and values of end-of-period assets a_nrm
243 """
244 m_nrm = calc_m_nrm_next(shocks, a_nrm, G, R)
245 perm_shk_fac = shocks["PermShk"] * G
246 return perm_shk_fac ** (-rho) * vp_func(m_nrm)
249def calc_v_next(shocks, a_nrm, G, R, rho, v_func):
250 """
251 Evaluate realizations of value of market resources next period, based on the
252 income distribution S and values of end-of-period assets a_nrm.
253 """
254 m_nrm = calc_m_nrm_next(shocks, a_nrm, G, R)
255 v_next = v_func(m_nrm)
256 return (shocks["PermShk"] * G) ** (1.0 - rho) * v_next
259def solve_one_period_WealthUtility(
260 solution_next,
261 IncShkDstn,
262 LivPrb,
263 DiscFac,
264 CRRA,
265 Rfree,
266 PermGroFac,
267 BoroCnstArt,
268 aXtraGrid,
269 vFuncBool,
270 CubicBool,
271 WealthShare,
272 WealthShift,
273 ChiFunc,
274):
275 """
276 Solve one period of the wealth-in-utility consumption-saving problem, conditional
277 on the solution to the succeeding period.
279 Parameters
280 ----------
281 solution_next : ConsumerSolution
282 Solution to the succeeding period's problem, which must include a vPfunc,
283 among other attributes.
284 IncShkDstn : Distribution
285 Distribution of next period's permanent and transitory income shocks, discretized.
286 LivPrb : float
287 Survival probability at the end of this period.
288 DiscFac : float
289 Intertemporal discount factor.
290 CRRA : float
291 Coefficient of relative risk aversion on composite of consumption and wealth.
292 Rfree : float
293 Risk-free return factor on retained assets.
294 PermGroFac : float
295 Expected growth rate of permanent income from this period to the next.
296 BoroCnstArt : float or None
297 Artificial borrowing constraint on retained assets.
298 aXtraGrid : np.array
299 Grid of end-of-period assets values.
300 vFuncBool : bool
301 Indicator for whether the value function should be constructed.
302 CubicBool : bool
303 Indicator for whether the consumption function should use cubic spline
304 interpolation (True) or linear splines (False).
305 WealthShare : float
306 Share of wealth in the Cobb-Douglas composition of consumption and wealth,
307 which should be between 0 and 1.
308 WealthShift : float
309 Shifter on wealth in the Cobb-Douglas composition, which should be non-negative.
310 ChiFunc : function
311 Mapping from omega = EndOfPrdvPnvrs / aNrm to the optimal chi = cNrm / aNrm.
313 Returns
314 -------
315 solution_now : ConsumerSolution
316 Solution to this period's problem, including the consumption function cFunc.
318 """
319 # Raise an error if cubic interpolation was requested
320 if CubicBool:
321 raise NotImplementedError(
322 "Cubic interpolation hasn't been programmed for the wealth in utility model yet."
323 )
325 # Define the current period utility function and effective discount factor
326 uFunc = UtilityFuncCRRA(CRRA)
327 DiscFacEff = DiscFac * LivPrb # "effective" discount factor
329 # Unpack next period's solution for easier access
330 vPfuncNext = solution_next.vPfunc
331 vFuncNext = solution_next.vFunc
333 # Calculate the minimum allowable value of money resources in this period
334 BoroCnstNat = calc_boro_const_nat(
335 solution_next.mNrmMin, IncShkDstn, Rfree, PermGroFac
336 )
337 BoroCnstNat = np.maximum(-WealthShift, BoroCnstNat)
339 # Set the minimum allowable (normalized) market resources based on the natural
340 # and artificial borrowing constraints
341 mNrmMinNow = calc_m_nrm_min(BoroCnstArt, BoroCnstNat)
343 # Make a grid of end-of-period assets
344 aNrmGrid = aXtraGrid + BoroCnstNat
346 # Calculate marginal value of end-of-period assets by taking expectations over income shocks
347 exp_dvdm = expected(
348 calc_dvdm_next,
349 IncShkDstn,
350 args=(aNrmGrid, PermGroFac, Rfree, CRRA, vPfuncNext),
351 )
352 dvda_end_of_prd = DiscFacEff * Rfree * exp_dvdm
353 dvda_nvrs = uFunc.derinv(dvda_end_of_prd, order=(1, 0))
355 # Calculate optimal consumption for each end-of-period assets value
356 if WealthShare == 0.0:
357 cNrm_now = dvda_nvrs
358 else:
359 wealth_temp = aXtraGrid + WealthShift
360 omega = dvda_nvrs / wealth_temp
361 cNrm_now = ChiFunc(omega) * wealth_temp
363 # Calculate the endogenous mNrm gridpoints, then construct the consumption function
364 mNrm_now = np.insert(aNrmGrid + cNrm_now, 0, BoroCnstNat)
365 cNrm_now = np.insert(cNrm_now, 0, 0.0)
366 cFuncUnc = LinearInterp(mNrm_now, cNrm_now)
367 cFuncCnst = LinearInterp([mNrmMinNow, mNrmMinNow + 1.0], [0.0, 1.0])
368 cFuncNow = LowerEnvelope(cFuncUnc, cFuncCnst)
370 # Calculate marginal value of market resources as the marginal utility of consumption
371 m_temp = aXtraGrid.copy() + mNrmMinNow
372 c_temp = cFuncNow(m_temp)
373 a_temp = m_temp - c_temp
374 dudc_now = dudc(c_temp, a_temp, CRRA, WealthShare, WealthShift)
375 dudc_nvrs_now = np.insert(uFunc.derinv(dudc_now, order=(1, 0)), 0, 0.0)
376 dudc_nvrs_func_now = LinearInterp(np.insert(m_temp, 0, mNrmMinNow), dudc_nvrs_now)
378 # Construct the marginal value (of mNrm) function
379 vPfuncNow = MargValueFuncCRRA(dudc_nvrs_func_now, CRRA)
381 # Add the value function if requested
382 if vFuncBool:
383 EndOfPrd_v = expected(
384 calc_v_next, IncShkDstn, args=(a_temp, PermGroFac, Rfree, CRRA, vFuncNext)
385 )
386 EndOfPrd_v *= DiscFacEff
387 u_now = utility(c_temp, a_temp, CRRA, WealthShare, WealthShift)
388 v_now = u_now + EndOfPrd_v
389 vNvrs_now = np.insert(uFunc.inverse(v_now), 0, 0.0)
390 vNvrsFunc = LinearInterp(np.insert(m_temp, 0, mNrmMinNow), vNvrs_now)
391 vFuncNow = ValueFuncCRRA(vNvrsFunc, CRRA)
392 else:
393 vFuncNow = NullFunc()
395 # Package and return the solution
396 solution_now = ConsumerSolution(
397 cFunc=cFuncNow,
398 vPfunc=vPfuncNow,
399 vFunc=vFuncNow,
400 mNrmMin=mNrmMinNow,
401 )
402 return solution_now
405###############################################################################
407# Make a dictionary of constructors for the wealth-in-utility portfolio choice consumer type
408WealthUtility_constructors_default = {
409 "IncShkDstn": construct_lognormal_income_process_unemployment,
410 "PermShkDstn": get_PermShkDstn_from_IncShkDstn,
411 "TranShkDstn": get_TranShkDstn_from_IncShkDstn,
412 "aXtraGrid": make_assets_grid,
413 "ChiFunc": make_ChiFromOmega_function,
414 "kNrmInitDstn": make_lognormal_kNrm_init_dstn,
415 "pLvlInitDstn": make_lognormal_pLvl_init_dstn,
416 "solution_terminal": make_terminal_solution_for_wealth_in_utility,
417}
419# Default parameters to make IncShkDstn using construct_lognormal_income_process_unemployment
420WealthUtility_IncShkDstn_default = {
421 "PermShkStd": [0.1], # Standard deviation of log permanent income shocks
422 "PermShkCount": 7, # Number of points in discrete approximation to permanent income shocks
423 "TranShkStd": [0.1], # Standard deviation of log transitory income shocks
424 "TranShkCount": 7, # Number of points in discrete approximation to transitory income shocks
425 "UnempPrb": 0.05, # Probability of unemployment while working
426 "IncUnemp": 0.3, # Unemployment benefits replacement rate while working
427 "T_retire": 0, # Period of retirement (0 --> no retirement)
428 "UnempPrbRet": 0.005, # Probability of "unemployment" while retired
429 "IncUnempRet": 0.0, # "Unemployment" benefits when retired
430}
432# Default parameters to make aXtraGrid using make_assets_grid
433WealthUtility_aXtraGrid_default = {
434 "aXtraMin": 0.001, # Minimum end-of-period "assets above minimum" value
435 "aXtraMax": 20, # Maximum end-of-period "assets above minimum" value
436 "aXtraNestFac": 2, # Exponential nesting factor for aXtraGrid
437 "aXtraCount": 48, # Number of points in the grid of "assets above minimum"
438 "aXtraExtra": None, # Additional other values to add in grid (optional)
439}
441# Default parameters to make ChiFunc with make_ChiFromOmega_function
442WealthUtility_ChiFunc_default = {
443 "ChiFromOmega_N": 501, # Number of gridpoints in chi-from-omega function
444 "ChiFromOmega_bound": 15, # Highest gridpoint to use for it
445}
447# Make a dictionary with parameters for the default constructor for kNrmInitDstn
448WealthUtility_kNrmInitDstn_default = {
449 "kLogInitMean": -12.0, # Mean of log initial capital
450 "kLogInitStd": 0.0, # Stdev of log initial capital
451 "kNrmInitCount": 15, # Number of points in initial capital discretization
452}
454# Make a dictionary with parameters for the default constructor for pLvlInitDstn
455WealthUtility_pLvlInitDstn_default = {
456 "pLogInitMean": 0.0, # Mean of log permanent income
457 "pLogInitStd": 0.0, # Stdev of log permanent income
458 "pLvlInitCount": 15, # Number of points in initial capital discretization
459}
461# Make a dictionary to specify a risky asset consumer type
462WealthUtility_solving_default = {
463 # BASIC HARK PARAMETERS REQUIRED TO SOLVE THE MODEL
464 "cycles": 1, # Finite, non-cyclic model
465 "T_cycle": 1, # Number of periods in the cycle for this agent type
466 "constructors": WealthUtility_constructors_default, # See dictionary above
467 "pseudo_terminal": False, # solution_terminal really is part of solution
468 # PRIMITIVE RAW PARAMETERS REQUIRED TO SOLVE THE MODEL
469 "CRRA": 2.0, # Coefficient of relative risk aversion
470 "Rfree": [1.03], # Return factor on risk free asset
471 "DiscFac": 0.96, # Intertemporal discount factor
472 "LivPrb": [0.98], # Survival probability after each period
473 "PermGroFac": [1.01], # Permanent income growth factor
474 "BoroCnstArt": 0.0, # Artificial borrowing constraint
475 "WealthShare": 0.2, # Share of wealth in Cobb-Douglas aggregator in utility function
476 "WealthShift": 0.0, # Shifter for wealth in utility function
477 "vFuncBool": False, # Whether to calculate the value function during solution
478 "CubicBool": False, # Whether to use cubic spline interpolation when True
479}
480WealthUtility_simulation_default = {
481 # PARAMETERS REQUIRED TO SIMULATE THE MODEL
482 "AgentCount": 10000, # Number of agents of this type
483 "T_age": None, # Age after which simulated agents are automatically killed
484 "PermGroFacAgg": 1.0, # Aggregate permanent income growth factor
485 # (The portion of PermGroFac attributable to aggregate productivity growth)
486 "NewbornTransShk": False, # Whether Newborns have transitory shock
487 # ADDITIONAL OPTIONAL PARAMETERS
488 "PerfMITShk": False, # Do Perfect Foresight MIT Shock
489 # (Forces Newborns to follow solution path of the agent they replaced if True)
490 "neutral_measure": False, # Whether to use permanent income neutral measure (see Harmenberg 2021)
491}
493WealthUtilityConsumerType_default = {}
494WealthUtilityConsumerType_default.update(WealthUtility_solving_default)
495WealthUtilityConsumerType_default.update(WealthUtility_simulation_default)
496WealthUtilityConsumerType_default.update(WealthUtility_kNrmInitDstn_default)
497WealthUtilityConsumerType_default.update(WealthUtility_pLvlInitDstn_default)
498WealthUtilityConsumerType_default.update(WealthUtility_aXtraGrid_default)
499WealthUtilityConsumerType_default.update(WealthUtility_IncShkDstn_default)
500WealthUtilityConsumerType_default.update(WealthUtility_ChiFunc_default)
501init_wealth_utility = WealthUtilityConsumerType_default
504class WealthUtilityConsumerType(IndShockConsumerType):
505 r"""
506 Class for representing consumers who face idiosyncratic income risk and can save in
507 a risk-free asset, and have CRRA preferences over a Cobb-Douglas composite of assets
508 and consumption.
510 .. math::
511 \newcommand{\CRRA}{\rho}
512 \newcommand{\LivPrb}{\mathsf{S}}
513 \newcommand{\PermGroFac}{\Gamma}
514 \newcommand{\Rfree}{\mathsf{R}}
515 \newcommand{\DiscFac}{\beta}
516 \newcommand{\WealthShare}{\alpha}
517 \newcommand{\WealthShift}{\xi}
519 \begin{equation*}
520 v_t(m_t) = \max_{c_t}u(x_t) + \DiscFac \LivPrb_{t} \mathbb{E}_{t} \left[ (\PermGroFac_{t+1} \psi_{t+1})^{1-\CRRA} v_{t+1}(m_{t+1}) \right] ~~\text{s.t.}
521 \end{equation*}
522 \begin{align*}
523 x_t &=& (a_t + \WealthShift)^\WealthShare c_t^{1-\WealthShare}, \\
524 a_t &=& m_t - c_t, \\
525 a_t &\geq& \underline{a}, \\
526 m_{t+1} &=& a_t \Rfree_{t+1}/(\PermGroFac_{t+1} \psi_{t+1}) + \theta_{t+1}, \\
527 (\psi_{t+1},\theta_{t+1}) &\sim& F_{t+1}, \\
528 \mathbb{E}[\psi] &=& 1, \\
529 u(x) &=& \frac{x^{1-\CRRA}}{1-\CRRA}.
530 \end{align*}
532 Constructors
533 ------------
534 IncShkDstn: Constructor, :math:`\psi`, :math:`\theta`
535 The agent's income shock distributions.
537 Its default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.construct_lognormal_income_process_unemployment`
538 aXtraGrid: Constructor
539 The agent's asset grid.
541 Its default constructor is :func:`HARK.utilities.make_assets_grid`
543 Solving Parameters
544 ------------------
545 cycles: int
546 0 specifies an infinite horizon model, 1 specifies a finite model.
547 T_cycle: int
548 Number of periods in the cycle for this agent type.
549 CRRA: float, :math:`\rho`
550 Coefficient of Relative Risk Aversion.
551 WealthShift : float, :math:`\xi`
552 Additive shifter for wealth in the utility function.
553 WealthShare : float, :math:`\alpha`
554 Cobb-Douglas share for wealth in the utility function.
555 Rfree: float or list[float], time varying, :math:`\mathsf{R}`
556 Risk Free interest rate. Pass a list of floats to make Rfree time varying.
557 DiscFac: float, :math:`\beta`
558 Intertemporal discount factor.
559 LivPrb: list[float], time varying, :math:`1-\mathsf{D}`
560 Survival probability after each period.
561 PermGroFac: list[float], time varying, :math:`\Gamma`
562 Permanent income growth factor.
563 BoroCnstArt: float, :math:`\underline{a}`
564 The minimum Asset/Permanent Income ratio, None to ignore.
565 vFuncBool: bool
566 Whether to calculate the value function during solution.
567 CubicBool: bool
568 Whether to use cubic spline interpolation.
570 Simulation Parameters
571 ---------------------
572 AgentCount: int
573 Number of agents of this kind that are created during simulations.
574 T_age: int
575 Age after which to automatically kill agents, None to ignore.
576 T_sim: int, required for simulation
577 Number of periods to simulate.
578 track_vars: list[strings]
579 List of variables that should be tracked when running the simulation.
580 For this agent, the options are 'PermShk', 'TranShk', 'aLvl', 'aNrm', 'bNrm', 'cNrm', 'mNrm', 'pLvl', and 'who_dies'.
582 PermShk is the agent's permanent income shock
584 TranShk is the agent's transitory income shock
586 aLvl is the nominal asset level
588 aNrm is the normalized assets
590 bNrm is the normalized resources without this period's labor income
592 cNrm is the normalized consumption
594 mNrm is the normalized market resources
596 pLvl is the permanent income level
598 who_dies is the array of which agents died
599 aNrmInitMean: float
600 Mean of Log initial Normalized Assets.
601 aNrmInitStd: float
602 Std of Log initial Normalized Assets.
603 pLvlInitMean: float
604 Mean of Log initial permanent income.
605 pLvlInitStd: float
606 Std of Log initial permanent income.
607 PermGroFacAgg: float
608 Aggregate permanent income growth factor (The portion of PermGroFac attributable to aggregate productivity growth).
609 PerfMITShk: boolean
610 Do Perfect Foresight MIT Shock (Forces Newborns to follow solution path of the agent they replaced if True).
611 NewbornTransShk: boolean
612 Whether Newborns have transitory shock.
614 Attributes
615 ----------
616 solution: list[Consumer solution object]
617 Created by the :func:`.solve` method. Finite horizon models create a list with T_cycle+1 elements, for each period in the solution.
618 Infinite horizon solutions return a list with T_cycle elements for each period in the cycle.
620 Visit :class:`HARK.ConsumptionSaving.ConsIndShockModel.ConsumerSolution` for more information about the solution.
621 history: Dict[Array]
622 Created by running the :func:`.simulate()` method.
623 Contains the variables in track_vars. Each item in the dictionary is an array with the shape (T_sim,AgentCount).
624 Visit :class:`HARK.core.AgentType.simulate` for more information.
625 """
627 time_inv_ = deepcopy(IndShockConsumerType.time_inv_)
628 time_inv_ = time_inv_ + [
629 "WealthShare",
630 "WealthShift",
631 "ChiFunc",
632 ]
633 default_ = {
634 "params": init_wealth_utility,
635 "solver": solve_one_period_WealthUtility,
636 "model": "ConsIndShock.yaml",
637 "track_vars": ["aNrm", "cNrm", "mNrm", "pLvl"],
638 }
640 def pre_solve(self):
641 self.construct("solution_terminal")
643 def make_euler_error_func(self, mMax=100, approx_inc_dstn=True): # pragma: nocover
644 raise NotImplementedError()
646 def check_conditions(self, verbose): # pragma: nocover
647 raise NotImplementedError()
649 def calc_limiting_values(self): # pragma: nocover
650 raise NotImplementedError()
653###############################################################################
656def solve_one_period_CapitalistSpirit(
657 solution_next,
658 IncShkDstn,
659 LivPrb,
660 DiscFac,
661 CRRA,
662 WealthCurve,
663 WealthFac,
664 WealthShift,
665 Rfree,
666 pLvlNextFunc,
667 BoroCnstArt,
668 aXtraGrid,
669 pLvlGrid,
670 vFuncBool,
671 CubicBool,
672):
673 """
674 Solves one one period problem of a consumer who experiences persistent and
675 transitory shocks to his income. Unlike in ConsIndShock, consumers do not
676 necessarily have the same predicted level of p next period as this period
677 (after controlling for growth). Instead, they have a function that translates
678 current persistent income into expected next period persistent income (subject
679 to shocks).
681 Moreover, the agent's preferences follow the "capitalist spirit" model, so that
682 end-of-period assets yield additively separable CRRA utility with a *lower*
683 coefficient than for consumption. This causes the saving rate to increase as
684 wealth increases, eventually approaching 100%.
686 Parameters
687 ----------
688 solution_next : ConsumerSolution
689 The solution to next period's one period problem.
690 IncShkDstn : distribution.Distribution
691 A discrete approximation to the income shocks between the period being
692 solved and the one immediately following (in solution_next).
693 LivPrb : float
694 Survival probability; likelihood of being alive at the beginning of
695 the succeeding period.
696 DiscFac : float
697 Intertemporal discount factor for future utility.
698 CRRA : float
699 Coefficient of relative risk aversion for consumption.
700 WealthCurve : float
701 Ratio of CRRA for consumption to CRRA for wealth. Must be strictly between
702 zero and one.
703 WealthFac : float
704 Weighting factor on utility of wealth relative to utility of consumption.
705 Should be non-negative.
706 WealthShift : float
707 Shifter for wealth when calculating utility. Should be non-negative.
708 Rfree : float
709 Risk free interest factor on end-of-period assets.
710 pLvlNextFunc : float
711 Expected persistent income next period as a function of current pLvl.
712 BoroCnstArt: float or None
713 Borrowing constraint for the minimum allowable assets to end the
714 period with.
715 aXtraGrid: np.array
716 Array of "extra" end-of-period (normalized) asset values-- assets
717 above the absolute minimum acceptable level.
718 pLvlGrid: np.array
719 Array of persistent income levels at which to solve the problem.
720 vFuncBool: boolean
721 An indicator for whether the value function should be computed and
722 included in the reported solution.
723 CubicBool: boolean
724 An indicator for whether the solver should use cubic or linear interpolation.
726 Returns
727 -------
728 solution_now : ConsumerSolution
729 Solution to this period's consumption-saving problem.
730 """
731 if WealthCurve >= 1.0:
732 raise ValueError("WealthCurve must be less than 1!")
733 if WealthCurve <= 0.0:
734 raise ValueError("WealthCurve must be greater than 0!")
735 if WealthFac < 0.0:
736 raise ValueError("WealthFac cannot be negative!")
737 if WealthShift < 0.0:
738 raise ValueError("WealthShift cannot be negative!")
740 # Define the utility functions for this period
741 uFuncCon = UtilityFuncCRRA(CRRA)
742 DiscFacEff = DiscFac * LivPrb # "effective" discount factor
743 CRRAwealth = CRRA * WealthCurve
744 uFuncWealth = lambda a: WealthFac * CRRAutility(a + WealthShift, rho=CRRAwealth)
746 if vFuncBool and (CRRA >= 1.0) and (CRRAwealth < 1.0):
747 raise ValueError(
748 "Can't construct a good representation of value function when rho > 1 > nu!"
749 )
751 # Unpack next period's income shock distribution
752 ShkPrbsNext = IncShkDstn.pmv
753 PermShkValsNext = IncShkDstn.atoms[0]
754 TranShkValsNext = IncShkDstn.atoms[1]
755 PermShkMinNext = np.min(PermShkValsNext)
756 TranShkMinNext = np.min(TranShkValsNext)
758 # Calculate the probability that we get the worst possible income draw
759 IncNext = PermShkValsNext * TranShkValsNext
760 WorstIncNext = PermShkMinNext * TranShkMinNext
761 WorstIncPrb = np.sum(ShkPrbsNext[IncNext == WorstIncNext])
762 # WorstIncPrb is the "Weierstrass p" concept: the odds we get the WORST thing
764 # Unpack next period's (marginal) value function
765 vFuncNext = solution_next.vFunc # This is None when vFuncBool is False
766 vPfuncNext = solution_next.vPfunc
767 mLvlMinNext = solution_next.mLvlMin
768 hLvlNext = solution_next.hLvl
770 # Define some functions for calculating future expectations
771 def calc_pLvl_next(S, p):
772 return pLvlNextFunc(p) * S["PermShk"]
774 def calc_mLvl_next(S, a, p_next):
775 return Rfree * a + p_next * S["TranShk"]
777 def calc_hLvl(S, p):
778 pLvl_next = pLvlNextFunc(p) * S["PermShk"]
779 hLvl = S["TranShk"] * pLvl_next + hLvlNext(pLvl_next)
780 return hLvl
782 def calc_v_next(S, a, p):
783 pLvl_next = calc_pLvl_next(S, p)
784 mLvl_next = calc_mLvl_next(S, a, pLvl_next)
785 v_next = vFuncNext(mLvl_next, pLvl_next)
786 return v_next
788 def calc_vP_next(S, a, p):
789 pLvl_next = pLvlNextFunc(p) * S["PermShk"]
790 mLvl_next = calc_mLvl_next(S, a, pLvl_next)
791 vP_next = vPfuncNext(mLvl_next, pLvl_next)
792 return vP_next
794 # Construct human wealth level as a function of productivity pLvl
795 hLvlGrid = 1.0 / Rfree * expected(calc_hLvl, IncShkDstn, args=(pLvlGrid))
796 hLvlNow = LinearInterp(np.insert(pLvlGrid, 0, 0.0), np.insert(hLvlGrid, 0, 0.0))
798 # Make temporary grids of income shocks and next period income values
799 ShkCount = TranShkValsNext.size
800 pLvlCount = pLvlGrid.size
801 PermShkVals_temp = np.tile(
802 np.reshape(PermShkValsNext, (1, ShkCount)), (pLvlCount, 1)
803 )
804 TranShkVals_temp = np.tile(
805 np.reshape(TranShkValsNext, (1, ShkCount)), (pLvlCount, 1)
806 )
807 pLvlNext_temp = (
808 np.tile(
809 np.reshape(pLvlNextFunc(pLvlGrid), (pLvlCount, 1)),
810 (1, ShkCount),
811 )
812 * PermShkVals_temp
813 )
815 # Find the natural borrowing constraint for each persistent income level
816 aLvlMin_candidates = (
817 mLvlMinNext(pLvlNext_temp) - TranShkVals_temp * pLvlNext_temp
818 ) / Rfree
819 aLvlMinNow = np.max(aLvlMin_candidates, axis=1)
820 aLvlMinNow = np.maximum(aLvlMinNow, -WealthShift)
821 BoroCnstNat = LinearInterp(
822 np.insert(pLvlGrid, 0, 0.0), np.insert(aLvlMinNow, 0, 0.0)
823 )
825 # Define the minimum allowable mLvl by pLvl as the greater of the natural and artificial borrowing constraints
826 if BoroCnstArt is not None:
827 BoroCnstArt = LinearInterp(np.array([0.0, 1.0]), np.array([0.0, BoroCnstArt]))
828 mLvlMinNow = UpperEnvelope(BoroCnstArt, BoroCnstNat)
829 else:
830 mLvlMinNow = BoroCnstNat
832 # Define the constrained consumption function as "consume all" shifted by mLvlMin
833 cFuncNowCnstBase = BilinearInterp(
834 np.array([[0.0, 0.0], [1.0, 1.0]]),
835 np.array([0.0, 1.0]),
836 np.array([0.0, 1.0]),
837 )
838 cFuncNowCnst = VariableLowerBoundFunc2D(cFuncNowCnstBase, mLvlMinNow)
840 # Define grids of pLvl and aLvl on which to compute future expectations
841 pLvlCount = pLvlGrid.size
842 aNrmCount = aXtraGrid.size
843 pLvlNow = np.tile(pLvlGrid, (aNrmCount, 1)).transpose()
844 aLvlNow = np.tile(aXtraGrid, (pLvlCount, 1)) * pLvlNow + BoroCnstNat(pLvlNow)
845 # shape = (pLvlCount,aNrmCount)
846 if pLvlGrid[0] == 0.0: # aLvl turns out badly if pLvl is 0 at bottom
847 aLvlNow[0, :] = aXtraGrid
849 # Calculate end-of-period marginal value of assets
850 EndOfPrd_vP = (
851 DiscFacEff * Rfree * expected(calc_vP_next, IncShkDstn, args=(aLvlNow, pLvlNow))
852 )
854 # Add in marginal utility of assets through the capitalist spirit function
855 dvda = EndOfPrd_vP + WealthFac * (aLvlNow + WealthShift) ** (-CRRAwealth)
857 # If the value function has been requested, construct the end-of-period vFunc
858 if vFuncBool:
859 # Compute expected value from end-of-period states
860 EndOfPrd_v = expected(calc_v_next, IncShkDstn, args=(aLvlNow, pLvlNow))
861 EndOfPrd_v *= DiscFacEff
863 # Transformed value through inverse utility function to "decurve" it
864 EndOfPrd_vNvrs = uFuncCon.inv(EndOfPrd_v)
865 EndOfPrd_vNvrsP = EndOfPrd_vP * uFuncCon.derinv(EndOfPrd_v, order=(0, 1))
867 # Add points at mLvl=zero
868 EndOfPrd_vNvrs = np.concatenate(
869 (np.zeros((pLvlCount, 1)), EndOfPrd_vNvrs), axis=1
870 )
871 EndOfPrd_vNvrsP = np.concatenate(
872 (
873 np.reshape(EndOfPrd_vNvrsP[:, 0], (pLvlCount, 1)),
874 EndOfPrd_vNvrsP,
875 ),
876 axis=1,
877 )
878 # This is a very good approximation, vNvrsPP = 0 at the asset minimum
880 # Make a temporary aLvl grid for interpolating the end-of-period value function
881 aLvl_temp = np.concatenate(
882 (
883 np.reshape(BoroCnstNat(pLvlGrid), (pLvlGrid.size, 1)),
884 aLvlNow,
885 ),
886 axis=1,
887 )
889 # Make an end-of-period value function for each persistent income level in the grid
890 EndOfPrd_vNvrsFunc_list = []
891 for p in range(pLvlCount):
892 EndOfPrd_vNvrsFunc_list.append(
893 CubicInterp(
894 aLvl_temp[p, :] - BoroCnstNat(pLvlGrid[p]),
895 EndOfPrd_vNvrs[p, :],
896 EndOfPrd_vNvrsP[p, :],
897 )
898 )
899 EndOfPrd_vNvrsFuncBase = LinearInterpOnInterp1D(
900 EndOfPrd_vNvrsFunc_list, pLvlGrid
901 )
903 # Re-adjust the combined end-of-period value function to account for the
904 # natural borrowing constraint shifter and "re-curve" it
905 EndOfPrd_vNvrsFunc = VariableLowerBoundFunc2D(
906 EndOfPrd_vNvrsFuncBase, BoroCnstNat
907 )
908 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA)
909 if isinstance(vFuncNext, ConstantFunction):
910 EndOfPrd_vFunc = ConstantFunction(vFuncNext.value)
912 # Solve the first order condition to get optimal consumption, then find the
913 # endogenous gridpoints
914 cLvlNow = uFuncCon.derinv(dvda, order=(1, 0))
915 mLvlNow = cLvlNow + aLvlNow
917 # Limiting consumption is zero as m approaches mNrmMin
918 c_for_interpolation = np.concatenate((np.zeros((pLvlCount, 1)), cLvlNow), axis=-1)
919 m_for_interpolation = np.concatenate(
920 (
921 BoroCnstNat(np.reshape(pLvlGrid, (pLvlCount, 1))),
922 mLvlNow,
923 ),
924 axis=-1,
925 )
927 # Make an array of corresponding pLvl values, adding an additional column for
928 # the mLvl points at the lower boundary
929 p_for_interpolation = np.concatenate(
930 (np.reshape(pLvlGrid, (pLvlCount, 1)), pLvlNow), axis=-1
931 )
933 # Build the set of cFuncs by pLvl, gathered in a list
934 cFunc_by_pLvl_list = [] # list of consumption functions for each pLvl
936 # Loop over pLvl values and make an mLvl for each one
937 for j in range(p_for_interpolation.shape[0]):
938 pLvl_j = p_for_interpolation[j, 0]
939 m_temp = m_for_interpolation[j, :] - BoroCnstNat(pLvl_j)
941 # Make a linear consumption function for this pLvl
942 c_temp = c_for_interpolation[j, :]
943 if pLvl_j > 0:
944 cFunc_by_pLvl_list.append(
945 LinearInterp(
946 m_temp,
947 c_temp,
948 lower_extrap=True,
949 )
950 )
951 else:
952 cFunc_by_pLvl_list.append(LinearInterp(m_temp, c_temp, lower_extrap=True))
954 # Combine all linear cFuncs into one function
955 pLvl_list = p_for_interpolation[:, 0]
956 cFuncUncBase = LinearInterpOnInterp1D(cFunc_by_pLvl_list, pLvl_list)
957 cFuncNowUnc = VariableLowerBoundFunc2D(cFuncUncBase, BoroCnstNat)
958 # Re-adjust for lower bound of natural borrowing constraint
960 # Combine the constrained and unconstrained functions into the true consumption function
961 cFuncNow = LowerEnvelope2D(cFuncNowUnc, cFuncNowCnst)
963 # Make the marginal value function
964 vPfuncNow = MargValueFuncCRRA(cFuncNow, CRRA)
966 # If the value function has been requested, construct it now
967 if vFuncBool:
968 # Compute expected value and marginal value on a grid of market resources
969 # Tile pLvl across m values
970 pLvl_temp = np.tile(pLvlGrid, (aNrmCount, 1))
971 mLvl_temp = (
972 np.tile(mLvlMinNow(pLvlGrid), (aNrmCount, 1))
973 + np.tile(np.reshape(aXtraGrid, (aNrmCount, 1)), (1, pLvlCount)) * pLvl_temp
974 )
975 cLvl_temp = cFuncNow(mLvl_temp, pLvl_temp)
976 aLvl_temp = mLvl_temp - cLvl_temp
977 u_now = uFuncCon(cLvl_temp) + uFuncWealth(aLvl_temp)
978 v_temp = u_now + EndOfPrd_vFunc(aLvl_temp, pLvl_temp)
979 vP_temp = uFuncCon.der(cLvl_temp)
981 # Calculate pseudo-inverse value and its first derivative (wrt mLvl)
982 vNvrs_temp = uFuncCon.inv(v_temp) # value transformed through inverse utility
983 vNvrsP_temp = vP_temp * uFuncCon.derinv(v_temp, order=(0, 1))
985 # Add data at the lower bound of m
986 mLvl_temp = np.concatenate(
987 (np.reshape(mLvlMinNow(pLvlGrid), (1, pLvlCount)), mLvl_temp), axis=0
988 )
989 vNvrs_temp = np.concatenate((np.zeros((1, pLvlCount)), vNvrs_temp), axis=0)
990 vNvrsP_temp = np.concatenate(
991 (np.reshape(vNvrsP_temp[0, :], (1, vNvrsP_temp.shape[1])), vNvrsP_temp),
992 axis=0,
993 )
995 # Construct the pseudo-inverse value function
996 vNvrsFunc_list = []
997 for j in range(pLvlCount):
998 pLvl = pLvlGrid[j]
999 vNvrsFunc_list.append(
1000 CubicInterp(
1001 mLvl_temp[:, j] - mLvlMinNow(pLvl),
1002 vNvrs_temp[:, j],
1003 vNvrsP_temp[:, j],
1004 )
1005 )
1006 # Value function "shifted"
1007 vNvrsFuncBase = LinearInterpOnInterp1D(vNvrsFunc_list, pLvlGrid)
1008 vNvrsFuncNow = VariableLowerBoundFunc2D(vNvrsFuncBase, mLvlMinNow)
1010 # "Re-curve" the pseudo-inverse value function into the value function
1011 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA)
1013 else:
1014 vFuncNow = NullFunc()
1016 # Dummy out the marginal marginal value function
1017 vPPfuncNow = NullFunc()
1019 # Package and return the solution object
1020 solution_now = ConsumerSolution(
1021 cFunc=cFuncNow,
1022 vFunc=vFuncNow,
1023 vPfunc=vPfuncNow,
1024 vPPfunc=vPPfuncNow,
1025 mNrmMin=0.0, # Not a normalized model, mLvlMin will be added below
1026 hNrm=0.0, # Not a normalized model, hLvl will be added below
1027 MPCmax=0.0, # This should be a function, need to make it
1028 )
1029 solution_now.hLvl = hLvlNow
1030 solution_now.mLvlMin = mLvlMinNow
1031 return solution_now
1034###############################################################################
1037# Make a constructor dictionary for the capitalist spirit consumer type
1038CapitalistSpirit_constructors_default = {
1039 "IncShkDstn": construct_lognormal_income_process_unemployment,
1040 "PermShkDstn": get_PermShkDstn_from_IncShkDstn,
1041 "TranShkDstn": get_TranShkDstn_from_IncShkDstn,
1042 "aXtraGrid": make_assets_grid,
1043 "pLvlPctiles": make_basic_pLvlPctiles,
1044 "pLvlGrid": make_pLvlGrid_by_simulation,
1045 "pLvlNextFunc": make_AR1_style_pLvlNextFunc,
1046 "solution_terminal": make_2D_CRRA_solution_empty,
1047 "kNrmInitDstn": make_lognormal_kNrm_init_dstn,
1048 "pLvlInitDstn": make_lognormal_pLvl_init_dstn,
1049}
1051# Make a dictionary with parameters for the default constructor for kNrmInitDstn
1052CapitalistSpirit_kNrmInitDstn_default = {
1053 "kLogInitMean": -12.0, # Mean of log initial capital
1054 "kLogInitStd": 0.0, # Stdev of log initial capital
1055 "kNrmInitCount": 15, # Number of points in initial capital discretization
1056}
1058# Make a dictionary with parameters for the default constructor for pLvlInitDstn
1059CapitalistSpirit_pLvlInitDstn_default = {
1060 "pLogInitMean": 0.0, # Mean of log permanent income
1061 "pLogInitStd": 0.4, # Stdev of log permanent income
1062 "pLvlInitCount": 15, # Number of points in initial capital discretization
1063}
1065# Default parameters to make IncShkDstn using construct_lognormal_income_process_unemployment
1066CapitalistSpirit_IncShkDstn_default = {
1067 "PermShkStd": [0.1], # Standard deviation of log permanent income shocks
1068 "PermShkCount": 7, # Number of points in discrete approximation to permanent income shocks
1069 "TranShkStd": [0.1], # Standard deviation of log transitory income shocks
1070 "TranShkCount": 7, # Number of points in discrete approximation to transitory income shocks
1071 "UnempPrb": 0.05, # Probability of unemployment while working
1072 "IncUnemp": 0.3, # Unemployment benefits replacement rate while working
1073 "T_retire": 0, # Period of retirement (0 --> no retirement)
1074 "UnempPrbRet": 0.005, # Probability of "unemployment" while retired
1075 "IncUnempRet": 0.0, # "Unemployment" benefits when retired
1076}
1078# Default parameters to make aXtraGrid using make_assets_grid
1079CapitalistSpirit_aXtraGrid_default = {
1080 "aXtraMin": 0.001, # Minimum end-of-period "assets above minimum" value
1081 "aXtraMax": 50.0, # Maximum end-of-period "assets above minimum" value
1082 "aXtraNestFac": 2, # Exponential nesting factor for aXtraGrid
1083 "aXtraCount": 72, # Number of points in the grid of "assets above minimum"
1084 "aXtraExtra": [0.005, 0.01], # Additional other values to add in grid (optional)
1085}
1087# Default parameters to make pLvlGrid using make_basic_pLvlPctiles
1088CapitalistSpirit_pLvlPctiles_default = {
1089 "pLvlPctiles_count": 19, # Number of points in the "body" of the grid
1090 "pLvlPctiles_bound": [0.05, 0.95], # Percentile bounds of the "body"
1091 "pLvlPctiles_tail_count": 4, # Number of points in each tail of the grid
1092 "pLvlPctiles_tail_order": np.e, # Scaling factor for points in each tail
1093}
1095# Default parameters to make pLvlGrid using make_pLvlGrid_by_simulation
1096CapitalistSpirit_pLvlGrid_default = {
1097 "pLvlExtra": None, # Additional permanent income points to automatically add to the grid, optional
1098}
1100CapitalistSpirit_pLvlNextFunc_default = {
1101 "PrstIncCorr": 0.98, # Persistence factor for "permanent" shocks
1102 "PermGroFac": [1.00], # Expected permanent income growth factor
1103}
1105# Make a dictionary to specify a general income process consumer type
1106CapitalistSpirit_solving_default = {
1107 # BASIC HARK PARAMETERS REQUIRED TO SOLVE THE MODEL
1108 "cycles": 1, # Finite, non-cyclic model
1109 "T_cycle": 1, # Number of periods in the cycle for this agent type
1110 "pseudo_terminal": True, # solution_terminal is not actually part of solution
1111 "constructors": CapitalistSpirit_constructors_default, # See dictionary above
1112 # PRIMITIVE RAW PARAMETERS REQUIRED TO SOLVE THE MODEL
1113 "CRRA": 2.0, # Coefficient of relative risk aversion
1114 "Rfree": [1.03], # Interest factor on retained assets
1115 "DiscFac": 0.96, # Intertemporal discount factor
1116 "LivPrb": [0.98], # Survival probability after each period
1117 "WealthFac": 1.0, # Relative weight on utility of wealth
1118 "WealthShift": 0.0, # Additive shifter for wealth in utility function
1119 "WealthCurve": 0.8, # Ratio of CRRA for wealth to CRRA for consumption
1120 "BoroCnstArt": 0.0, # Artificial borrowing constraint
1121 "vFuncBool": False, # Whether to calculate the value function during solution
1122 "CubicBool": False, # Whether to use cubic spline interpolation when True
1123}
1125CapitalistSpirit_simulation_default = {
1126 # PARAMETERS REQUIRED TO SIMULATE THE MODEL
1127 "AgentCount": 10000, # Number of agents of this type
1128 "T_age": None, # Age after which simulated agents are automatically killed
1129 "PermGroFacAgg": 1.0, # Aggregate permanent income growth factor
1130 # (The portion of PermGroFac attributable to aggregate productivity growth)
1131 "NewbornTransShk": False, # Whether Newborns have transitory shock
1132 # ADDITIONAL OPTIONAL PARAMETERS
1133 "PerfMITShk": False, # Do Perfect Foresight MIT Shock
1134 # (Forces Newborns to follow solution path of the agent they replaced if True)
1135 "neutral_measure": False, # Whether to use permanent income neutral measure (see Harmenberg 2021)
1136}
1137CapitalistSpirit_default = {}
1138CapitalistSpirit_default.update(CapitalistSpirit_kNrmInitDstn_default)
1139CapitalistSpirit_default.update(CapitalistSpirit_pLvlInitDstn_default)
1140CapitalistSpirit_default.update(CapitalistSpirit_IncShkDstn_default)
1141CapitalistSpirit_default.update(CapitalistSpirit_aXtraGrid_default)
1142CapitalistSpirit_default.update(CapitalistSpirit_pLvlNextFunc_default)
1143CapitalistSpirit_default.update(CapitalistSpirit_pLvlGrid_default)
1144CapitalistSpirit_default.update(CapitalistSpirit_pLvlPctiles_default)
1145CapitalistSpirit_default.update(CapitalistSpirit_solving_default)
1146CapitalistSpirit_default.update(CapitalistSpirit_simulation_default)
1147init_capitalist_spirit = CapitalistSpirit_default
1150class CapitalistSpiritConsumerType(GenIncProcessConsumerType):
1151 r"""
1152 Class for representing consumers who have "capitalist spirit" preferences,
1153 yielding CRRA utility from consumption and wealth, additively. Importantly,
1154 the risk aversion coefficient for wealth is *lower* than for consumption, so
1155 the agent's saving rate approaches 100% as they become arbitrarily rich.
1157 .. math::
1158 \begin{eqnarray*}
1159 V_t(M_t,P_t) &=& \max_{C_t} U(C_t, A_t) + \beta (1-\mathsf{D}_{t+1}) \mathbb{E} [V_{t+1}(M_{t+1}, P_{t+1}) ], \\
1160 A_t &=& M_t - C_t, \\
1161 A_t/P_t &\geq& \underline{a}, \\
1162 M_{t+1} &=& R A_t + \theta_{t+1}, \\
1163 p_{t+1} &=& G_{t+1}(P_t)\psi_{t+1}, \\
1164 (\psi_{t+1},\theta_{t+1}) &\sim& F_{t+1}, \\
1165 \mathbb{E} [F_{t+1}] &=& 1, \\
1166 U(C) &=& \frac{C^{1-\rho}}{1-\rho} + \alpha \frac{(A + \xi)^{1-\nu}}{1-\nu}, \\
1167 log(G_{t+1} (x)) &=&\varphi log(x) + (1-\varphi) log(\overline{P}_{t})+log(\Gamma_{t+1}) + log(\psi_{t+1}), \\
1168 \overline{P}_{t+1} &=& \overline{P}_{t} \Gamma_{t+1} \\
1169 \end{eqnarray*}
1171 Constructors
1172 ------------
1173 IncShkDstn: Constructor, :math:`\psi`, :math:`\theta`
1174 The agent's income shock distributions.
1175 Its default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.construct_lognormal_income_process_unemployment`
1176 aXtraGrid: Constructor
1177 The agent's asset grid.
1178 Its default constructor is :func:`HARK.utilities.make_assets_grid`
1179 pLvlNextFunc: Constructor, (:math:`\Gamma`, :math:`\varphi`)
1180 An arbitrary function used to evolve the GenIncShockConsumerType's permanent income
1181 Its default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.make_AR1_style_pLvlNextFunc`
1182 pLvlGrid: Constructor
1183 The agent's pLvl grid
1184 Its default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.make_pLvlGrid_by_simulation`
1185 pLvlPctiles: Constructor
1186 The agents income level percentile grid
1187 Its default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.make_basic_pLvlPctiles`
1189 Solving Parameters
1190 ------------------
1191 cycles: int
1192 0 specifies an infinite horizon model, 1 specifies a finite model.
1193 T_cycle: int
1194 Number of periods in the cycle for this agent type.
1195 CRRA: float, :math:`\rho`
1196 Coefficient of Relative Risk Aversion.
1197 WealthFac : float, :math:`\alpha`
1198 Weighting factor on utility of wealth.
1199 WealthShift : float :math:`\xi`
1200 Additive shifter for wealth in utility function.
1201 WealthCurve : float
1202 CRRA for wealth as a proportion of ordinary CRRA; must be in (0,1): CRRAwealth = WealthCurve *CRRA.
1203 Rfree: list[float], time varying, :math:`\mathsf{R}`
1204 Risk Free interest rate. Pass a list of floats to make Rfree time varying.
1205 DiscFac: float, :math:`\beta`
1206 Intertemporal discount factor.
1207 LivPrb: list[float], time varying, :math:`1-\mathsf{D}`
1208 Survival probability after each period.
1209 PermGroFac: list[float], time varying, :math:`\Gamma`
1210 Permanent income growth factor.
1211 BoroCnstArt: float, :math:`\underline{a}`
1212 The minimum Asset/Permanent Income ratio, None to ignore.
1213 vFuncBool: bool
1214 Whether to calculate the value function during solution.
1215 CubicBool: bool
1216 Whether to use cubic spline interpolation.
1218 Simulation Parameters
1219 ---------------------
1220 AgentCount: int
1221 Number of agents of this kind that are created during simulations.
1222 T_age: int
1223 Age after which to automatically kill agents, None to ignore.
1224 T_sim: int, required for simulation
1225 Number of periods to simulate.
1226 track_vars: list[strings]
1227 List of variables that should be tracked when running the simulation.
1228 For this agent, the options are 'PermShk', 'TranShk', 'aLvl', 'cLvl', 'mLvl', 'pLvl', and 'who_dies'.
1230 PermShk is the agent's permanent income shock
1232 TranShk is the agent's transitory income shock
1234 aLvl is the nominal asset level
1236 cLvl is the nominal consumption level
1238 mLvl is the nominal market resources
1240 pLvl is the permanent income level
1242 who_dies is the array of which agents died
1243 kLogInitMean: float
1244 Mean of Log initial Normalized Assets.
1245 kLogInitStd: float
1246 Std of Log initial Normalized Assets.
1247 pLogInitMean: float
1248 Mean of Log initial permanent income.
1249 pLogInitStd: float
1250 Std of Log initial permanent income.
1251 PermGroFacAgg: float
1252 Aggregate permanent income growth factor (The portion of PermGroFac attributable to aggregate productivity growth).
1253 PerfMITShk: boolean
1254 Do Perfect Foresight MIT Shock (Forces Newborns to follow solution path of the agent they replaced if True).
1255 NewbornTransShk: boolean
1256 Whether Newborns have transitory shock.
1258 Attributes
1259 ----------
1260 solution: list[Consumer solution object]
1261 Created by the :func:`.solve` method. Finite horizon models create a list with T_cycle+1 elements, for each period in the solution.
1262 Infinite horizon solutions return a list with T_cycle elements for each period in the cycle.
1264 Unlike other models with this solution type, this model's variables are NOT normalized.
1265 The solution functions also depend on the permanent income level. For example, :math:`C=\text{cFunc}(M,P)`.
1266 hNrm has been replaced by hLvl which is a function of permanent income.
1267 MPC max has not yet been implemented for this class. It will be a function of permanent income.
1269 Visit :class:`HARK.ConsumptionSaving.ConsIndShockModel.ConsumerSolution` for more information about the solution.
1271 history: Dict[Array]
1272 Created by running the :func:`.simulate()` method.
1273 Contains the variables in track_vars. Each item in the dictionary is an array with the shape (T_sim,AgentCount).
1274 Visit :class:`HARK.core.AgentType.simulate` for more information.
1275 """
1277 time_inv_ = GenIncProcessConsumerType.time_inv_ + [
1278 "WealthCurve",
1279 "WealthFac",
1280 "WealthShift",
1281 ]
1283 default_ = {
1284 "params": init_capitalist_spirit,
1285 "solver": solve_one_period_CapitalistSpirit,
1286 "model": "ConsGenIncProcess.yaml",
1287 "track_vars": ["aLvl", "cLvl", "mLvl", "pLvl"],
1288 }