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

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: 

5 

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. 

10 

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""" 

15 

16import numpy as np 

17from copy import deepcopy 

18 

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 

55 

56 

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. 

61 

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. 

70 

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]) 

83 

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 

99 

100 

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. 

106 

107 Parameters 

108 ---------- 

109 CRRA : float 

110 Coefficient of relative risk aversion. This is the only relevant parameter. 

111 

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 

134 

135 

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). 

142 

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 """ 

156 

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() 

163 

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) 

171 

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) 

181 

182 omega_vec = self.f(chi_vec) 

183 log_omega_vec = np.log(omega_vec) 

184 

185 # Construct the interpolant 

186 zFromLogOmegaFunc = LinearInterp(log_omega_vec, z_vec, lower_extrap=True) 

187 

188 # Store the function and limit as attributes 

189 self.func = zFromLogOmegaFunc 

190 self.limit = chi_limit 

191 

192 def __call__(self, omega): 

193 """ 

194 Calculate optimal values of chi = cNrm / aNrm from values of omega. 

195 

196 Parameters 

197 ---------- 

198 omega : np.array 

199 One or more values of omega = EndOfPrdvP / aNrm. 

200 

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 

210 

211 

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 ) 

219 

220 

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) 

224 

225 

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 

229 

230 

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"] 

237 

238 

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) 

247 

248 

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 

257 

258 

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. 

278 

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. 

312 

313 Returns 

314 ------- 

315 solution_now : ConsumerSolution 

316 Solution to this period's problem, including the consumption function cFunc. 

317 

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 ) 

324 

325 # Define the current period utility function and effective discount factor 

326 uFunc = UtilityFuncCRRA(CRRA) 

327 DiscFacEff = DiscFac * LivPrb # "effective" discount factor 

328 

329 # Unpack next period's solution for easier access 

330 vPfuncNext = solution_next.vPfunc 

331 vFuncNext = solution_next.vFunc 

332 

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) 

338 

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) 

342 

343 # Make a grid of end-of-period assets 

344 aNrmGrid = aXtraGrid + BoroCnstNat 

345 

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)) 

354 

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 

362 

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) 

369 

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) 

377 

378 # Construct the marginal value (of mNrm) function 

379 vPfuncNow = MargValueFuncCRRA(dudc_nvrs_func_now, CRRA) 

380 

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() 

394 

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 

403 

404 

405############################################################################### 

406 

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} 

418 

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} 

431 

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} 

440 

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} 

446 

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} 

453 

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} 

460 

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} 

492 

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 

502 

503 

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. 

509 

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} 

518 

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*} 

531 

532 Constructors 

533 ------------ 

534 IncShkDstn: Constructor, :math:`\psi`, :math:`\theta` 

535 The agent's income shock distributions. 

536 

537 Its default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.construct_lognormal_income_process_unemployment` 

538 aXtraGrid: Constructor 

539 The agent's asset grid. 

540 

541 Its default constructor is :func:`HARK.utilities.make_assets_grid` 

542 

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. 

569 

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'. 

581 

582 PermShk is the agent's permanent income shock 

583 

584 TranShk is the agent's transitory income shock 

585 

586 aLvl is the nominal asset level 

587 

588 aNrm is the normalized assets 

589 

590 bNrm is the normalized resources without this period's labor income 

591 

592 cNrm is the normalized consumption 

593 

594 mNrm is the normalized market resources 

595 

596 pLvl is the permanent income level 

597 

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. 

613 

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. 

619 

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 """ 

626 

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 } 

639 

640 def pre_solve(self): 

641 self.construct("solution_terminal") 

642 

643 def make_euler_error_func(self, mMax=100, approx_inc_dstn=True): # pragma: nocover 

644 raise NotImplementedError() 

645 

646 def check_conditions(self, verbose): # pragma: nocover 

647 raise NotImplementedError() 

648 

649 def calc_limiting_values(self): # pragma: nocover 

650 raise NotImplementedError() 

651 

652 

653############################################################################### 

654 

655 

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). 

680 

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%. 

685 

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. 

725 

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!") 

739 

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) 

745 

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 ) 

750 

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) 

757 

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 

763 

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 

769 

770 # Define some functions for calculating future expectations 

771 def calc_pLvl_next(S, p): 

772 return pLvlNextFunc(p) * S["PermShk"] 

773 

774 def calc_mLvl_next(S, a, p_next): 

775 return Rfree * a + p_next * S["TranShk"] 

776 

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 

781 

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 

787 

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 

793 

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)) 

797 

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 ) 

814 

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 ) 

824 

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 

831 

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) 

839 

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 

848 

849 # Calculate end-of-period marginal value of assets 

850 EndOfPrd_vP = ( 

851 DiscFacEff * Rfree * expected(calc_vP_next, IncShkDstn, args=(aLvlNow, pLvlNow)) 

852 ) 

853 

854 # Add in marginal utility of assets through the capitalist spirit function 

855 dvda = EndOfPrd_vP + WealthFac * (aLvlNow + WealthShift) ** (-CRRAwealth) 

856 

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 

862 

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)) 

866 

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 

879 

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 ) 

888 

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 ) 

902 

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) 

911 

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 

916 

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 ) 

926 

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 ) 

932 

933 # Build the set of cFuncs by pLvl, gathered in a list 

934 cFunc_by_pLvl_list = [] # list of consumption functions for each pLvl 

935 

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) 

940 

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)) 

953 

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 

959 

960 # Combine the constrained and unconstrained functions into the true consumption function 

961 cFuncNow = LowerEnvelope2D(cFuncNowUnc, cFuncNowCnst) 

962 

963 # Make the marginal value function 

964 vPfuncNow = MargValueFuncCRRA(cFuncNow, CRRA) 

965 

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) 

980 

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)) 

984 

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 ) 

994 

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) 

1009 

1010 # "Re-curve" the pseudo-inverse value function into the value function 

1011 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA) 

1012 

1013 else: 

1014 vFuncNow = NullFunc() 

1015 

1016 # Dummy out the marginal marginal value function 

1017 vPPfuncNow = NullFunc() 

1018 

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 

1032 

1033 

1034############################################################################### 

1035 

1036 

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} 

1050 

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} 

1057 

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} 

1064 

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} 

1077 

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} 

1086 

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} 

1094 

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} 

1099 

1100CapitalistSpirit_pLvlNextFunc_default = { 

1101 "PrstIncCorr": 0.98, # Persistence factor for "permanent" shocks 

1102 "PermGroFac": [1.00], # Expected permanent income growth factor 

1103} 

1104 

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} 

1124 

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 

1148 

1149 

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. 

1156 

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*} 

1170 

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` 

1188 

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. 

1217 

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'. 

1229 

1230 PermShk is the agent's permanent income shock 

1231 

1232 TranShk is the agent's transitory income shock 

1233 

1234 aLvl is the nominal asset level 

1235 

1236 cLvl is the nominal consumption level 

1237 

1238 mLvl is the nominal market resources 

1239 

1240 pLvl is the permanent income level 

1241 

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. 

1257 

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. 

1263 

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. 

1268 

1269 Visit :class:`HARK.ConsumptionSaving.ConsIndShockModel.ConsumerSolution` for more information about the solution. 

1270 

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 """ 

1276 

1277 time_inv_ = GenIncProcessConsumerType.time_inv_ + [ 

1278 "WealthCurve", 

1279 "WealthFac", 

1280 "WealthShift", 

1281 ] 

1282 

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 }