Coverage for HARK / ConsumptionSaving / ConsPrefShockModel.py: 98%

322 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-25 05:22 +0000

1""" 

2Extensions to ConsIndShockModel concerning models with preference shocks. 

3It currently only two models: 

4 

51) An extension of ConsIndShock, but with an iid lognormal multiplicative shock each period. 

62) A combination of (1) and ConsKinkedR, demonstrating how to construct a new model 

7 by inheriting from multiple classes. 

8""" 

9 

10import numpy as np 

11 

12from HARK import NullFunc 

13from HARK.ConsumptionSaving.ConsIndShockModel import ( 

14 ConsumerSolution, 

15 IndShockConsumerType, 

16 KinkedRconsumerType, 

17 make_assets_grid, 

18 make_lognormal_kNrm_init_dstn, 

19 make_lognormal_pLvl_init_dstn, 

20) 

21from HARK.Calibration.Income.IncomeProcesses import ( 

22 construct_lognormal_income_process_unemployment, 

23 get_PermShkDstn_from_IncShkDstn, 

24 get_TranShkDstn_from_IncShkDstn, 

25) 

26from HARK.distributions import MeanOneLogNormal, expected 

27from HARK.interpolation import ( 

28 IdentityFunction, 

29 CubicInterp, 

30 LinearInterp, 

31 LinearInterpOnInterp1D, 

32 BilinearInterp, 

33 LowerEnvelope, 

34 LowerEnvelope2D, 

35 MargValueFuncCRRA, 

36 MargMargValueFuncCRRA, 

37 ValueFuncCRRA, 

38) 

39from HARK.rewards import UtilityFuncCRRA 

40 

41__all__ = [ 

42 "PrefShockConsumerType", 

43 "KinkyPrefConsumerType", 

44 "make_lognormal_PrefShkDstn", 

45] 

46 

47 

48def make_pref_shock_solution_terminal(CRRA): 

49 """ 

50 Construct the terminal period solution for a consumption-saving model with 

51 CRRA utility and two state variables. The consumption function depends *only* 

52 on the first dimension, representing market resources. 

53 

54 Parameters 

55 ---------- 

56 CRRA : float 

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

58 

59 Returns 

60 ------- 

61 solution_terminal : ConsumerSolution 

62 Terminal period solution for someone with the given CRRA. 

63 """ 

64 cFunc_terminal = IdentityFunction(i_dim=0, n_dims=2) # c=m at t=T 

65 vFunc_terminal = ValueFuncCRRA(cFunc_terminal, CRRA) 

66 vPfunc_terminal = MargValueFuncCRRA(cFunc_terminal, CRRA) 

67 vPPfunc_terminal = MargMargValueFuncCRRA(cFunc_terminal, CRRA) 

68 solution_terminal = ConsumerSolution( 

69 cFunc=cFunc_terminal, 

70 vFunc=vFunc_terminal, 

71 vPfunc=vPfunc_terminal, 

72 vPPfunc=vPPfunc_terminal, 

73 mNrmMin=0.0, 

74 hNrm=0.0, 

75 MPCmin=1.0, 

76 MPCmax=1.0, 

77 ) 

78 return solution_terminal 

79 

80 

81def make_lognormal_PrefShkDstn( 

82 T_cycle, 

83 PrefShkStd, 

84 PrefShkCount, 

85 RNG, 

86 PrefShk_tail_N=0, 

87 PrefShk_tail_order=np.e, 

88 PrefShk_tail_bound=[0.02, 0.98], 

89): 

90 r""" 

91 Make a discretized mean one lognormal preference shock distribution for each 

92 period of the agent's problem. 

93 

94 .. math:: 

95 \eta_t \sim \mathcal{N}(-\textbf{PrefShkStd}_{t}^{2}/2,\textbf{PrefShkStd}_{t}^2) 

96 

97 Parameters 

98 ---------- 

99 T_cycle : int 

100 Number of non-terminal periods in the agent's cycle. 

101 PrefShkStd : [float] 

102 Standard deviation of log preference shocks in each period. 

103 PrefShkCount : int 

104 Number of equiprobable preference shock nodes in the "body" of the distribution. 

105 RNG : RandomState 

106 The AgentType's internal random number generator. 

107 PrefShk_tail_N : int 

108 Number of shock nodes in each "tail" of the distribution (optional). 

109 PrefShk_tail_order : float 

110 Scaling factor for tail nodes (optional). 

111 PrefShk_tail_bound : [float,float] 

112 CDF bounds for tail nodes (optional). 

113 

114 Returns 

115 ------- 

116 PrefShkDstn : [DiscreteDistribution] 

117 List of discretized lognormal distributions for shocks. 

118 """ 

119 PrefShkDstn = [] # discrete distributions of preference shocks 

120 for t in range(T_cycle): 

121 PrefShkStd = PrefShkStd[t] 

122 new_dstn = MeanOneLogNormal( 

123 sigma=PrefShkStd, seed=RNG.integers(0, 2**31 - 1) 

124 ).discretize( 

125 N=PrefShkCount, 

126 method="equiprobable", 

127 tail_N=PrefShk_tail_N, 

128 tail_order=PrefShk_tail_order, 

129 tail_bound=PrefShk_tail_bound, 

130 ) 

131 PrefShkDstn.append(new_dstn) 

132 return PrefShkDstn 

133 

134 

135############################################################################### 

136 

137 

138def solve_one_period_ConsPrefShock( 

139 solution_next, 

140 IncShkDstn, 

141 PrefShkDstn, 

142 LivPrb, 

143 DiscFac, 

144 CRRA, 

145 Rfree, 

146 PermGroFac, 

147 BoroCnstArt, 

148 aXtraGrid, 

149 vFuncBool, 

150 CubicBool, 

151): 

152 """ 

153 Solves one period of a consumption-saving model with idiosyncratic shocks to 

154 permanent and transitory income, with one risk free asset and CRRA utility. 

155 The consumer also faces iid preference shocks as a multiplicative shifter to 

156 their marginal utility of consumption. 

157 

158 Parameters 

159 ---------- 

160 solution_next : ConsumerSolution 

161 The solution to the succeeding one period problem. 

162 IncShkDstn : distribution.Distribution 

163 A discrete approximation to the income process between the period being 

164 solved and the one immediately following (in solution_next). Order: 

165 permanent shocks, transitory shocks. 

166 PrefShkDstn : distribution.Distribution 

167 Discrete distribution of the multiplicative utility shifter. Order: 

168 probabilities, preference shocks. 

169 LivPrb : float 

170 Survival probability; likelihood of being alive at the beginning of 

171 the succeeding period. 

172 DiscFac : float 

173 Intertemporal discount factor for future utility. 

174 CRRA : float 

175 Coefficient of relative risk aversion. 

176 Rfree : float 

177 Risk free interest factor on end-of-period assets. 

178 PermGroGac : float 

179 Expected permanent income growth factor at the end of this period. 

180 BoroCnstArt: float or None 

181 Borrowing constraint for the minimum allowable assets to end the 

182 period with. If it is less than the natural borrowing constraint, 

183 then it is irrelevant; BoroCnstArt=None indicates no artificial bor- 

184 rowing constraint. 

185 aXtraGrid: np.array 

186 Array of "extra" end-of-period asset values-- assets above the 

187 absolute minimum acceptable level. 

188 vFuncBool: boolean 

189 An indicator for whether the value function should be computed and 

190 included in the reported solution. 

191 CubicBool: boolean 

192 An indicator for whether the solver should use cubic or linear inter- 

193 polation. 

194 

195 Returns 

196 ------- 

197 solution: ConsumerSolution 

198 The solution to the single period consumption-saving problem. Includes 

199 a consumption function cFunc (using linear splines), a marginal value 

200 function vPfunc, a minimum acceptable level of normalized market re- 

201 sources mNrmMin, normalized human wealth hNrm, and bounding MPCs MPCmin 

202 and MPCmax. It might also have a value function vFunc. The consumption 

203 function is defined over normalized market resources and the preference 

204 shock, c = cFunc(m,PrefShk), but the (marginal) value function is defined 

205 unconditionally on the shock, just before it is revealed. 

206 """ 

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

208 uFunc = UtilityFuncCRRA(CRRA) 

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

210 

211 # Unpack next period's income and preference shock distributions 

212 ShkPrbsNext = IncShkDstn.pmv 

213 PermShkValsNext = IncShkDstn.atoms[0] 

214 TranShkValsNext = IncShkDstn.atoms[1] 

215 PermShkMinNext = np.min(PermShkValsNext) 

216 TranShkMinNext = np.min(TranShkValsNext) 

217 PrefShkPrbs = PrefShkDstn.pmv 

218 PrefShkVals = PrefShkDstn.atoms.flatten() 

219 

220 # Calculate the probability that we get the worst possible income draw 

221 IncNext = PermShkValsNext * TranShkValsNext 

222 WorstIncNext = PermShkMinNext * TranShkMinNext 

223 WorstIncPrb = np.sum(ShkPrbsNext[IncNext == WorstIncNext]) 

224 # WorstIncPrb is the "Weierstrass p" concept: the odds we get the WORST thing 

225 

226 # Unpack next period's (marginal) value function 

227 vFuncNext = solution_next.vFunc # This is None when vFuncBool is False 

228 vPfuncNext = solution_next.vPfunc 

229 vPPfuncNext = solution_next.vPPfunc # This is None when CubicBool is False 

230 

231 # Update the bounding MPCs and PDV of human wealth: 

232 PatFac = ((Rfree * DiscFacEff) ** (1.0 / CRRA)) / Rfree 

233 try: 

234 MPCminNow = 1.0 / (1.0 + PatFac / solution_next.MPCmin) 

235 except: 

236 MPCminNow = 0.0 

237 Ex_IncNext = np.dot(ShkPrbsNext, TranShkValsNext * PermShkValsNext) 

238 hNrmNow = PermGroFac / Rfree * (Ex_IncNext + solution_next.hNrm) 

239 temp_fac = (WorstIncPrb ** (1.0 / CRRA)) * PatFac 

240 MPCmaxNow = 1.0 / (1.0 + temp_fac / solution_next.MPCmax) 

241 

242 # Calculate the minimum allowable value of money resources in this period 

243 PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rfree 

244 BoroCnstNat = (solution_next.mNrmMin - TranShkMinNext) * PermGroFacEffMin 

245 

246 # Set the minimum allowable (normalized) market resources based on the natural 

247 # and artificial borrowing constraints 

248 if BoroCnstArt is None: 

249 mNrmMinNow = BoroCnstNat 

250 else: 

251 mNrmMinNow = np.max([BoroCnstNat, BoroCnstArt]) 

252 

253 # Set the upper limit of the MPC (at mNrmMinNow) based on whether the natural 

254 # or artificial borrowing constraint actually binds 

255 if BoroCnstNat < mNrmMinNow: 

256 MPCmaxEff = 1.0 # If actually constrained, MPC near limit is 1 

257 else: 

258 MPCmaxEff = MPCmaxNow # Otherwise, it's the MPC calculated above 

259 

260 # Define the borrowing-constrained consumption function 

261 cFuncCnst = BilinearInterp( 

262 np.array([[0.0, 0.0], [1.0, 1.0]]), 

263 np.array([mNrmMinNow, mNrmMinNow + 1.0]), 

264 np.array([0.0, 1.0]), 

265 ) 

266 

267 # Construct the assets grid by adjusting aXtra by the natural borrowing constraint 

268 aNrmNow = np.asarray(aXtraGrid) + BoroCnstNat 

269 

270 # Define local functions for taking future expectations 

271 def calc_mNrmNext(S, a, R): 

272 return R / (PermGroFac * S["PermShk"]) * a + S["TranShk"] 

273 

274 def calc_vNext(S, a, R): 

275 return (S["PermShk"] ** (1.0 - CRRA) * PermGroFac ** (1.0 - CRRA)) * vFuncNext( 

276 calc_mNrmNext(S, a, R) 

277 ) 

278 

279 def calc_vPnext(S, a, R): 

280 return S["PermShk"] ** (-CRRA) * vPfuncNext(calc_mNrmNext(S, a, R)) 

281 

282 def calc_vPPnext(S, a, R): 

283 return S["PermShk"] ** (-CRRA - 1.0) * vPPfuncNext(calc_mNrmNext(S, a, R)) 

284 

285 # Calculate end-of-period marginal value of assets at each gridpoint 

286 vPfacEff = DiscFacEff * Rfree * PermGroFac ** (-CRRA) 

287 EndOfPrdvP = vPfacEff * expected(calc_vPnext, IncShkDstn, args=(aNrmNow, Rfree)) 

288 

289 # Find optimal consumption corresponding to each aNrm, PrefShk combination 

290 cNrm_base = uFunc.derinv(EndOfPrdvP, order=(1, 0)) 

291 PrefShkCount = PrefShkVals.size 

292 PrefShk_temp = np.tile( 

293 np.reshape(PrefShkVals ** (1.0 - 1.0 / CRRA), (PrefShkCount, 1)), 

294 (1, cNrm_base.size), 

295 ) 

296 cNrmNow = np.tile(cNrm_base, (PrefShkCount, 1)) * PrefShk_temp 

297 mNrmNow = cNrmNow + np.tile(aNrmNow, (PrefShkCount, 1)) 

298 # These are the endogenous gridpoints, as usual 

299 

300 # Add the bottom point to the c and m arrays 

301 m_for_interpolation = np.concatenate( 

302 (BoroCnstNat * np.ones((PrefShkCount, 1)), mNrmNow), axis=1 

303 ) 

304 c_for_interpolation = np.concatenate((np.zeros((PrefShkCount, 1)), cNrmNow), axis=1) 

305 

306 # Construct the consumption function as a cubic or linear spline interpolation 

307 # for each value of PrefShk, interpolated across those values. 

308 if CubicBool: 

309 # This is not yet supported, not sure why we never got to it 

310 raise ValueError( 

311 "Cubic interpolation is not yet supported by the preference shock model!" 

312 ) 

313 

314 # Make the preference-shock specific consumption functions 

315 cFuncs_by_PrefShk = [] 

316 for j in range(PrefShkCount): 

317 MPCmin_j = MPCminNow * PrefShkVals[j] ** (1.0 - 1.0 / CRRA) 

318 cFunc_this_shk = LinearInterp( 

319 m_for_interpolation[j, :], 

320 c_for_interpolation[j, :], 

321 intercept_limit=hNrmNow * MPCmin_j, 

322 slope_limit=MPCmin_j, 

323 ) 

324 cFuncs_by_PrefShk.append(cFunc_this_shk) 

325 

326 # Combine the list of consumption functions into a single interpolation 

327 cFuncUnc = LinearInterpOnInterp1D(cFuncs_by_PrefShk, PrefShkVals) 

328 cFuncNow = LowerEnvelope2D(cFuncUnc, cFuncCnst) 

329 

330 # Make the ex ante marginal value function (before the preference shock) 

331 m_grid = aXtraGrid + mNrmMinNow 

332 vP_vec = np.zeros_like(m_grid) 

333 cFuncCnst_1D = LinearInterp([mNrmMinNow, mNrmMinNow + 1.0], [0.0, 1.0]) 

334 for j in range(PrefShkCount): # numeric integration over the preference shock 

335 shk = PrefShkVals[j] ** (CRRA - 1.0) 

336 prb = PrefShkPrbs[j] 

337 cFunc_temp = LowerEnvelope(cFuncUnc.xInterpolators[j], cFuncCnst_1D) 

338 vP_vec += uFunc.der(cFunc_temp(m_grid)) * shk * prb 

339 vPnvrs_vec = uFunc.derinv(vP_vec, order=(1, 0)) 

340 vPfuncNow = MargValueFuncCRRA(LinearInterp(m_grid, vPnvrs_vec), CRRA) 

341 

342 # Define this period's marginal marginal value function 

343 vPPfuncNow = NullFunc() # Dummy object, cubic interpolation not implemented 

344 

345 # Construct this period's value function if requested 

346 if vFuncBool: 

347 # Calculate end-of-period value, its derivative, and their pseudo-inverse 

348 EndOfPrdv = DiscFacEff * expected(calc_vNext, IncShkDstn, args=(aNrmNow, Rfree)) 

349 EndOfPrdvNvrs = uFunc.inv( 

350 EndOfPrdv 

351 ) # value transformed through inverse utility 

352 EndOfPrdvNvrsP = EndOfPrdvP * uFunc.derinv(EndOfPrdv, order=(0, 1)) 

353 EndOfPrdvNvrs = np.insert(EndOfPrdvNvrs, 0, 0.0) 

354 EndOfPrdvNvrsP = np.insert(EndOfPrdvNvrsP, 0, EndOfPrdvNvrsP[0]) 

355 # This is a very good approximation, vNvrsPP = 0 at the asset minimum 

356 

357 # Construct the end-of-period value function 

358 aNrm_temp = np.insert(aNrmNow, 0, BoroCnstNat) 

359 EndOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, EndOfPrdvNvrs, EndOfPrdvNvrsP) 

360 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA) 

361 

362 # Compute expected value and marginal value on a grid of market resources, 

363 # accounting for all of the discrete preference shocks 

364 mNrm_temp = mNrmMinNow + aXtraGrid 

365 v_temp = np.zeros_like(mNrm_temp) 

366 vP_temp = np.zeros_like(mNrm_temp) 

367 for j in range(PrefShkCount): 

368 this_shock = PrefShkVals[j] 

369 this_prob = PrefShkPrbs[j] 

370 cFunc_temp = LowerEnvelope(cFuncUnc.xInterpolators[j], cFuncCnst_1D) 

371 cNrm_temp = cFunc_temp(mNrm_temp) 

372 aNrm_temp = mNrm_temp - cNrm_temp 

373 v_temp += this_prob * ( 

374 uFunc(cNrm_temp / this_shock) + EndOfPrd_vFunc(aNrm_temp) 

375 ) 

376 vP_temp += this_prob * uFunc.der(cNrm_temp) * this_shock ** (CRRA - 1.0) 

377 

378 # Construct the beginning-of-period value function 

379 # value transformed through inverse utility 

380 vNvrs_temp = uFunc.inv(v_temp) 

381 vNvrsP_temp = vP_temp * uFunc.derinv(v_temp, order=(0, 1)) 

382 mNrm_temp = np.insert(mNrm_temp, 0, mNrmMinNow) 

383 vNvrs_temp = np.insert(vNvrs_temp, 0, 0.0) 

384 vNvrsP_temp = np.insert(vNvrsP_temp, 0, MPCmaxEff ** (-CRRA / (1.0 - CRRA))) 

385 MPCminNvrs = MPCminNow ** (-CRRA / (1.0 - CRRA)) 

386 vNvrsFuncNow = CubicInterp( 

387 mNrm_temp, vNvrs_temp, vNvrsP_temp, MPCminNvrs * hNrmNow, MPCminNvrs 

388 ) 

389 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA) 

390 

391 else: 

392 vFuncNow = NullFunc() # Dummy object 

393 

394 # Create and return this period's solution 

395 solution_now = ConsumerSolution( 

396 cFunc=cFuncNow, 

397 vFunc=vFuncNow, 

398 vPfunc=vPfuncNow, 

399 vPPfunc=vPPfuncNow, 

400 mNrmMin=mNrmMinNow, 

401 hNrm=hNrmNow, 

402 MPCmin=MPCminNow, 

403 MPCmax=MPCmaxEff, 

404 ) 

405 return solution_now 

406 

407 

408############################################################################### 

409 

410 

411def solve_one_period_ConsKinkyPref( 

412 solution_next, 

413 IncShkDstn, 

414 PrefShkDstn, 

415 LivPrb, 

416 DiscFac, 

417 CRRA, 

418 Rboro, 

419 Rsave, 

420 PermGroFac, 

421 BoroCnstArt, 

422 aXtraGrid, 

423 vFuncBool, 

424 CubicBool, 

425): 

426 """ 

427 Solves one period of a consumption-saving model with idiosyncratic shocks to 

428 permanent and transitory income, with a risk free asset and CRRA utility. 

429 In this variation, the interest rate on borrowing Rboro exceeds the interest 

430 rate on saving Rsave. The consumer also faces iid preference shocks as a multi- 

431 plicative shifter to their marginal utility of consumption. 

432 

433 Parameters 

434 ---------- 

435 solution_next : ConsumerSolution 

436 The solution to next period's one period problem. 

437 IncShkDstn : distribution.Distribution 

438 A discrete approximation to the income process between the period being 

439 solved and the one immediately following (in solution_next). 

440 PrefShkDstn : distribution.Distribution 

441 Discrete distribution of the multiplicative utility shifter. Order: 

442 probabilities, preference shocks. 

443 LivPrb : float 

444 Survival probability; likelihood of being alive at the beginning of 

445 the succeeding period. 

446 DiscFac : float 

447 Intertemporal discount factor for future utility. 

448 CRRA : float 

449 Coefficient of relative risk aversion. 

450 Rboro: float 

451 Interest factor on assets between this period and the succeeding 

452 period when assets are negative. 

453 Rsave: float 

454 Interest factor on assets between this period and the succeeding 

455 period when assets are positive. 

456 PermGroFac : float 

457 Expected permanent income growth factor at the end of this period. 

458 BoroCnstArt: float or None 

459 Borrowing constraint for the minimum allowable assets to end the 

460 period with. If it is less than the natural borrowing constraint, 

461 then it is irrelevant; BoroCnstArt=None indicates no artificial bor- 

462 rowing constraint. 

463 aXtraGrid: np.array 

464 Array of "extra" end-of-period asset values-- assets above the 

465 absolute minimum acceptable level. 

466 vFuncBool: boolean 

467 An indicator for whether the value function should be computed and 

468 included in the reported solution. 

469 CubicBool: boolean 

470 An indicator for whether the solver should use cubic or linear inter- 

471 polation. 

472 

473 Returns 

474 ------- 

475 solution_now : ConsumerSolution 

476 The solution to the single period consumption-saving problem. Includes 

477 a consumption function cFunc (using linear splines), a marginal value 

478 function vPfunc, a minimum acceptable level of normalized market re- 

479 sources mNrmMin, normalized human wealth hNrm, and bounding MPCs MPCmin 

480 and MPCmax. It might also have a value function vFunc. The consumption 

481 function is defined over normalized market resources and the preference 

482 shock, c = cFunc(m,PrefShk), but the (marginal) value function is defined 

483 unconditionally on the shock, just before it is revealed. 

484 """ 

485 # Verifiy that there is actually a kink in the interest factor 

486 assert Rboro >= Rsave, ( 

487 "Interest factor on debt less than interest factor on savings!" 

488 ) 

489 # If the kink is in the wrong direction, code should break here. If there's 

490 # no kink at all, then just use the ConsIndShockModel solver. 

491 if Rboro == Rsave: 

492 solution_now = solve_one_period_ConsPrefShock( 

493 solution_next, 

494 IncShkDstn, 

495 PrefShkDstn, 

496 LivPrb, 

497 DiscFac, 

498 CRRA, 

499 Rboro, 

500 PermGroFac, 

501 BoroCnstArt, 

502 aXtraGrid, 

503 vFuncBool, 

504 CubicBool, 

505 ) 

506 return solution_now 

507 

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

509 uFunc = UtilityFuncCRRA(CRRA) 

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

511 

512 # Unpack next period's income and preference shock distributions 

513 ShkPrbsNext = IncShkDstn.pmv 

514 PermShkValsNext = IncShkDstn.atoms[0] 

515 TranShkValsNext = IncShkDstn.atoms[1] 

516 PermShkMinNext = np.min(PermShkValsNext) 

517 TranShkMinNext = np.min(TranShkValsNext) 

518 PrefShkPrbs = PrefShkDstn.pmv 

519 PrefShkVals = PrefShkDstn.atoms.flatten() 

520 

521 # Calculate the probability that we get the worst possible income draw 

522 IncNext = PermShkValsNext * TranShkValsNext 

523 WorstIncNext = PermShkMinNext * TranShkMinNext 

524 WorstIncPrb = np.sum(ShkPrbsNext[IncNext == WorstIncNext]) 

525 # WorstIncPrb is the "Weierstrass p" concept: the odds we get the WORST thing 

526 

527 # Unpack next period's (marginal) value function 

528 vFuncNext = solution_next.vFunc # This is None when vFuncBool is False 

529 vPfuncNext = solution_next.vPfunc 

530 vPPfuncNext = solution_next.vPPfunc # This is None when CubicBool is False 

531 

532 # Update the bounding MPCs and PDV of human wealth: 

533 PatFac = ((Rsave * DiscFacEff) ** (1.0 / CRRA)) / Rsave 

534 PatFacAlt = ((Rboro * DiscFacEff) ** (1.0 / CRRA)) / Rboro 

535 try: 

536 MPCminNow = 1.0 / (1.0 + PatFac / solution_next.MPCmin) 

537 except: 

538 MPCminNow = 0.0 

539 Ex_IncNext = np.dot(ShkPrbsNext, TranShkValsNext * PermShkValsNext) 

540 hNrmNow = (PermGroFac / Rsave) * (Ex_IncNext + solution_next.hNrm) 

541 temp_fac = (WorstIncPrb ** (1.0 / CRRA)) * PatFacAlt 

542 MPCmaxNow = 1.0 / (1.0 + temp_fac / solution_next.MPCmax) 

543 

544 # Calculate the minimum allowable value of money resources in this period 

545 PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rboro 

546 BoroCnstNat = (solution_next.mNrmMin - TranShkMinNext) * PermGroFacEffMin 

547 

548 # Set the minimum allowable (normalized) market resources based on the natural 

549 # and artificial borrowing constraints 

550 if BoroCnstArt is None: 

551 mNrmMinNow = BoroCnstNat 

552 else: 

553 mNrmMinNow = np.max([BoroCnstNat, BoroCnstArt]) 

554 

555 # Set the upper limit of the MPC (at mNrmMinNow) based on whether the natural 

556 # or artificial borrowing constraint actually binds 

557 if BoroCnstNat < mNrmMinNow: 

558 MPCmaxEff = 1.0 # If actually constrained, MPC near limit is 1 

559 else: 

560 MPCmaxEff = MPCmaxNow # Otherwise, it's the MPC calculated above 

561 

562 # Define the borrowing-constrained consumption function 

563 cFuncCnst = BilinearInterp( 

564 np.array([[0.0, 0.0], [1.0, 1.0]]), 

565 np.array([mNrmMinNow, mNrmMinNow + 1.0]), 

566 np.array([0.0, 1.0]), 

567 ) 

568 

569 # Construct the assets grid by adjusting aXtra by the natural borrowing constraint 

570 aNrmNow = np.sort( 

571 np.hstack((np.asarray(aXtraGrid) + mNrmMinNow, np.array([0.0, 0.0]))) 

572 ) 

573 

574 # Make a 1D array of the interest factor at each asset gridpoint 

575 Rfree = Rsave * np.ones_like(aNrmNow) 

576 Rfree[aNrmNow < 0] = Rboro 

577 i_kink = np.argwhere(aNrmNow == 0.0)[0][0] 

578 Rfree[i_kink] = Rboro 

579 

580 # Define local functions for taking future expectations 

581 def calc_mNrmNext(S, a, R): 

582 return R / (PermGroFac * S["PermShk"]) * a + S["TranShk"] 

583 

584 def calc_vNext(S, a, R): 

585 return (S["PermShk"] ** (1.0 - CRRA) * PermGroFac ** (1.0 - CRRA)) * vFuncNext( 

586 calc_mNrmNext(S, a, R) 

587 ) 

588 

589 def calc_vPnext(S, a, R): 

590 return S["PermShk"] ** (-CRRA) * vPfuncNext(calc_mNrmNext(S, a, R)) 

591 

592 def calc_vPPnext(S, a, R): 

593 return S["PermShk"] ** (-CRRA - 1.0) * vPPfuncNext(calc_mNrmNext(S, a, R)) 

594 

595 # Calculate end-of-period marginal value of assets at each gridpoint 

596 vPfacEff = DiscFacEff * Rfree * PermGroFac ** (-CRRA) 

597 EndOfPrdvP = vPfacEff * expected(calc_vPnext, IncShkDstn, args=(aNrmNow, Rfree)) 

598 

599 # Find optimal consumption corresponding to each aNrm, PrefShk combination 

600 cNrm_base = uFunc.derinv(EndOfPrdvP, order=(1, 0)) 

601 PrefShkCount = PrefShkVals.size 

602 PrefShk_temp = np.tile( 

603 np.reshape(PrefShkVals ** (1.0 - 1.0 / CRRA), (PrefShkCount, 1)), 

604 (1, cNrm_base.size), 

605 ) 

606 cNrmNow = np.tile(cNrm_base, (PrefShkCount, 1)) * PrefShk_temp 

607 mNrmNow = cNrmNow + np.tile(aNrmNow, (PrefShkCount, 1)) 

608 # These are the endogenous gridpoints, as usual 

609 

610 # Add the bottom point to the c and m arrays 

611 m_for_interpolation = np.concatenate( 

612 (BoroCnstNat * np.ones((PrefShkCount, 1)), mNrmNow), axis=1 

613 ) 

614 c_for_interpolation = np.concatenate((np.zeros((PrefShkCount, 1)), cNrmNow), axis=1) 

615 

616 # Construct the consumption function as a cubic or linear spline interpolation 

617 # for each value of PrefShk, interpolated across those values. 

618 if CubicBool: 

619 # This is not yet supported, not sure why we never got to it 

620 raise ValueError( 

621 "Cubic interpolation is not yet supported by the preference shock model!" 

622 ) 

623 

624 # Make the preference-shock specific consumption functions 

625 cFuncs_by_PrefShk = [] 

626 for j in range(PrefShkCount): 

627 MPCmin_j = MPCminNow * PrefShkVals[j] ** (1.0 - 1.0 / CRRA) 

628 cFunc_this_shk = LinearInterp( 

629 m_for_interpolation[j, :], 

630 c_for_interpolation[j, :], 

631 intercept_limit=hNrmNow * MPCmin_j, 

632 slope_limit=MPCmin_j, 

633 ) 

634 cFuncs_by_PrefShk.append(cFunc_this_shk) 

635 

636 # Combine the list of consumption functions into a single interpolation 

637 cFuncUnc = LinearInterpOnInterp1D(cFuncs_by_PrefShk, PrefShkVals) 

638 cFuncNow = LowerEnvelope2D(cFuncUnc, cFuncCnst) 

639 

640 # Make the ex ante marginal value function (before the preference shock) 

641 m_grid = aXtraGrid + mNrmMinNow 

642 vP_vec = np.zeros_like(m_grid) 

643 cFuncCnst_1D = LinearInterp([mNrmMinNow, mNrmMinNow + 1.0], [0.0, 1.0]) 

644 for j in range(PrefShkCount): # numeric integration over the preference shock 

645 shk = PrefShkVals[j] ** (CRRA - 1.0) 

646 prb = PrefShkPrbs[j] 

647 cFunc_temp = LowerEnvelope(cFuncUnc.xInterpolators[j], cFuncCnst_1D) 

648 vP_vec += uFunc.der(cFunc_temp(m_grid)) * shk * prb 

649 vPnvrs_vec = uFunc.derinv(vP_vec, order=(1, 0)) 

650 vPfuncNow = MargValueFuncCRRA(LinearInterp(m_grid, vPnvrs_vec), CRRA) 

651 

652 # Define this period's marginal marginal value function 

653 vPPfuncNow = NullFunc() # Dummy object, cubic interpolation not implemented 

654 

655 # Construct this period's value function if requested 

656 if vFuncBool: 

657 # Calculate end-of-period value, its derivative, and their pseudo-inverse 

658 EndOfPrdv = DiscFacEff * expected(calc_vNext, IncShkDstn, args=(aNrmNow, Rfree)) 

659 EndOfPrdvNvrs = uFunc.inv( 

660 EndOfPrdv 

661 ) # value transformed through inverse utility 

662 EndOfPrdvNvrsP = EndOfPrdvP * uFunc.derinv(EndOfPrdv, order=(0, 1)) 

663 EndOfPrdvNvrs = np.insert(EndOfPrdvNvrs, 0, 0.0) 

664 EndOfPrdvNvrsP = np.insert(EndOfPrdvNvrsP, 0, EndOfPrdvNvrsP[0]) 

665 # This is a very good approximation, vNvrsPP = 0 at the asset minimum 

666 

667 # Construct the end-of-period value function 

668 aNrm_temp = np.insert(aNrmNow, 0, BoroCnstNat) 

669 EndOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, EndOfPrdvNvrs, EndOfPrdvNvrsP) 

670 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA) 

671 

672 # Compute expected value and marginal value on a grid of market resources, 

673 # accounting for all of the discrete preference shocks 

674 mNrm_temp = mNrmMinNow + aXtraGrid 

675 v_temp = np.zeros_like(mNrm_temp) 

676 vP_temp = np.zeros_like(mNrm_temp) 

677 for j in range(PrefShkCount): 

678 this_shock = PrefShkVals[j] 

679 this_prob = PrefShkPrbs[j] 

680 cFunc_temp = LowerEnvelope(cFuncUnc.xInterpolators[j], cFuncCnst_1D) 

681 cNrm_temp = cFunc_temp(mNrm_temp) 

682 aNrm_temp = mNrm_temp - cNrm_temp 

683 v_temp += this_prob * ( 

684 uFunc(cNrm_temp / this_shock) + EndOfPrd_vFunc(aNrm_temp) 

685 ) 

686 vP_temp += this_prob * uFunc.der(cNrm_temp) * this_shock ** (CRRA - 1.0) 

687 

688 # Construct the beginning-of-period value function 

689 # value transformed through inverse utility 

690 vNvrs_temp = uFunc.inv(v_temp) 

691 vNvrsP_temp = vP_temp * uFunc.derinv(v_temp, order=(0, 1)) 

692 mNrm_temp = np.insert(mNrm_temp, 0, mNrmMinNow) 

693 vNvrs_temp = np.insert(vNvrs_temp, 0, 0.0) 

694 vNvrsP_temp = np.insert(vNvrsP_temp, 0, MPCmaxEff ** (-CRRA / (1.0 - CRRA))) 

695 MPCminNvrs = MPCminNow ** (-CRRA / (1.0 - CRRA)) 

696 vNvrsFuncNow = CubicInterp( 

697 mNrm_temp, vNvrs_temp, vNvrsP_temp, MPCminNvrs * hNrmNow, MPCminNvrs 

698 ) 

699 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA) 

700 

701 else: 

702 vFuncNow = NullFunc() # Dummy object 

703 

704 # Create and return this period's solution 

705 solution_now = ConsumerSolution( 

706 cFunc=cFuncNow, 

707 vFunc=vFuncNow, 

708 vPfunc=vPfuncNow, 

709 vPPfunc=vPPfuncNow, 

710 mNrmMin=mNrmMinNow, 

711 hNrm=hNrmNow, 

712 MPCmin=MPCminNow, 

713 MPCmax=MPCmaxEff, 

714 ) 

715 return solution_now 

716 

717 

718############################################################################### 

719 

720# Make a dictionary of constructors for the preference shock model 

721PrefShockConsumerType_constructors_default = { 

722 "IncShkDstn": construct_lognormal_income_process_unemployment, 

723 "PermShkDstn": get_PermShkDstn_from_IncShkDstn, 

724 "TranShkDstn": get_TranShkDstn_from_IncShkDstn, 

725 "aXtraGrid": make_assets_grid, 

726 "PrefShkDstn": make_lognormal_PrefShkDstn, 

727 "solution_terminal": make_pref_shock_solution_terminal, 

728 "kNrmInitDstn": make_lognormal_kNrm_init_dstn, 

729 "pLvlInitDstn": make_lognormal_pLvl_init_dstn, 

730} 

731 

732# Make a dictionary with parameters for the default constructor for kNrmInitDstn 

733PrefShockConsumerType_kNrmInitDstn_default = { 

734 "kLogInitMean": -12.0, # Mean of log initial capital 

735 "kLogInitStd": 0.0, # Stdev of log initial capital 

736 "kNrmInitCount": 15, # Number of points in initial capital discretization 

737} 

738 

739# Make a dictionary with parameters for the default constructor for pLvlInitDstn 

740PrefShockConsumerType_pLvlInitDstn_default = { 

741 "pLogInitMean": 0.0, # Mean of log permanent income 

742 "pLogInitStd": 0.0, # Stdev of log permanent income 

743 "pLvlInitCount": 15, # Number of points in initial capital discretization 

744} 

745 

746# Default parameters to make IncShkDstn using construct_lognormal_income_process_unemployment 

747PrefShockConsumerType_IncShkDstn_default = { 

748 "PermShkStd": [0.1], # Standard deviation of log permanent income shocks 

749 "PermShkCount": 7, # Number of points in discrete approximation to permanent income shocks 

750 "TranShkStd": [0.1], # Standard deviation of log transitory income shocks 

751 "TranShkCount": 7, # Number of points in discrete approximation to transitory income shocks 

752 "UnempPrb": 0.05, # Probability of unemployment while working 

753 "IncUnemp": 0.3, # Unemployment benefits replacement rate while working 

754 "T_retire": 0, # Period of retirement (0 --> no retirement) 

755 "UnempPrbRet": 0.005, # Probability of "unemployment" while retired 

756 "IncUnempRet": 0.0, # "Unemployment" benefits when retired 

757} 

758 

759# Default parameters to make aXtraGrid using construct_assets_grid 

760 

761PrefShockConsumerType_aXtraGrid_default = { 

762 "aXtraMin": 0.001, # Minimum end-of-period "assets above minimum" value 

763 "aXtraMax": 20, # Maximum end-of-period "assets above minimum" value 

764 "aXtraNestFac": 3, # Exponential nesting factor for aXtraGrid 

765 "aXtraCount": 48, # Number of points in the grid of "assets above minimum" 

766 "aXtraExtra": None, # Additional other values to add in grid (optional) 

767} 

768 

769# Default parameters to make PrefShkDstn using make_lognormal_PrefShkDstn 

770 

771PrefShockConsumerType_PrefShkDstn_default = { 

772 "PrefShkCount": 12, # Number of points in discrete approximation to preference shock dist 

773 "PrefShk_tail_N": 4, # Number of "tail points" on each end of pref shock dist 

774 "PrefShkStd": [0.30], # Standard deviation of utility shocks 

775} 

776 

777# Make a dictionary to specify an preference shocks consumer type 

778PrefShockConsumerType_solving_default = { 

779 # BASIC HARK PARAMETERS REQUIRED TO SOLVE THE MODEL 

780 "cycles": 1, # Finite, non-cyclic model 

781 "T_cycle": 1, # Number of periods in the cycle for this agent type 

782 "pseudo_terminal": False, # Terminal period really does exist 

783 "constructors": PrefShockConsumerType_constructors_default, # See dictionary above 

784 # PRIMITIVE RAW PARAMETERS REQUIRED TO SOLVE THE MODEL 

785 "CRRA": 2.0, # Coefficient of relative risk aversion 

786 "Rfree": [1.03], # Interest factor on retained assets 

787 "DiscFac": 0.96, # Intertemporal discount factor 

788 "LivPrb": [0.98], # Survival probability after each period 

789 "PermGroFac": [1.01], # Permanent income growth factor 

790 "BoroCnstArt": 0.0, # Artificial borrowing constraint 

791 "vFuncBool": False, # Whether to calculate the value function during solution 

792 "CubicBool": False, # Whether to use cubic spline interpolation when True 

793 # (Uses linear spline interpolation for cFunc when False) 

794} 

795PrefShockConsumerType_simulation_default = { 

796 # PARAMETERS REQUIRED TO SIMULATE THE MODEL 

797 "AgentCount": 10000, # Number of agents of this type 

798 "T_age": None, # Age after which simulated agents are automatically killed 

799 "PermGroFacAgg": 1.0, # Aggregate permanent income growth factor 

800 # (The portion of PermGroFac attributable to aggregate productivity growth) 

801 "NewbornTransShk": False, # Whether Newborns have transitory shock 

802 # ADDITIONAL OPTIONAL PARAMETERS 

803 "PerfMITShk": False, # Do Perfect Foresight MIT Shock 

804 # (Forces Newborns to follow solution path of the agent they replaced if True) 

805 "neutral_measure": False, # Whether to use permanent income neutral measure (see Harmenberg 2021) 

806} 

807 

808PrefShockConsumerType_default = {} 

809PrefShockConsumerType_default.update(PrefShockConsumerType_IncShkDstn_default) 

810PrefShockConsumerType_default.update(PrefShockConsumerType_aXtraGrid_default) 

811PrefShockConsumerType_default.update(PrefShockConsumerType_PrefShkDstn_default) 

812PrefShockConsumerType_default.update(PrefShockConsumerType_kNrmInitDstn_default) 

813PrefShockConsumerType_default.update(PrefShockConsumerType_pLvlInitDstn_default) 

814PrefShockConsumerType_default.update(PrefShockConsumerType_solving_default) 

815PrefShockConsumerType_default.update(PrefShockConsumerType_simulation_default) 

816init_preference_shocks = ( 

817 PrefShockConsumerType_default # So models that aren't updated don't break 

818) 

819 

820 

821class PrefShockConsumerType(IndShockConsumerType): 

822 r""" 

823 A consumer type based on IndShockConsumerType, with multiplicative shocks to utility each period. 

824 

825 .. math:: 

826 \newcommand{\CRRA}{\rho} 

827 \newcommand{\DiePrb}{\mathsf{D}} 

828 \newcommand{\PermGroFac}{\Gamma} 

829 \newcommand{\Rfree}{\mathsf{R}} 

830 \newcommand{\DiscFac}{\beta} 

831 \begin{align*} 

832 v_t(m_t,\eta_t) &=\max_{c_t} \eta_{t} u(c_t) + \DiscFac (1 - \DiePrb_{t+1}) \mathbb{E}_{t} \left[ (\PermGroFac_{t+1} \psi_{t+1})^{1-\CRRA} v_{t+1}(m_{t+1},\eta_{t+1}) \right], \\ 

833 & \text{s.t.} \\ 

834 a_t &= m_t - c_t, \\ 

835 a_t &\geq \underline{a}, \\ 

836 m_{t+1} &= a_t \Rfree_{t+1}/(\PermGroFac_{t+1} \psi_{t+1}) + \theta_{t+1}, \\ 

837 (\psi_{t+1},\theta_{t+1},\eta_{t+1}) &\sim F_{t+1}, \\ 

838 \mathbb{E}[\psi]=\mathbb{E}[\eta] &= 1, \\ 

839 u(c,\eta) &= \frac{(c/\eta)^{1-\CRRA}}{1-\CRRA} \\ 

840 \end{align*} 

841 

842 

843 Constructors 

844 ------------ 

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

846 The agent's income shock distributions. 

847 

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

849 aXtraGrid: Constructor 

850 The agent's asset grid. 

851 

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

853 PrefShkDstn: Constructor, :math:`\eta` 

854 The agent's preference shock distributions. 

855 

856 Its default constuctor is :func:`HARK.ConsumptionSaving.ConsPrefShockModel.make_lognormal_PrefShkDstn` 

857 

858 Solving Parameters 

859 ------------------ 

860 cycles: int 

861 0 specifies an infinite horizon model, 1 specifies a finite model. 

862 T_cycle: int 

863 Number of periods in the cycle for this agent type. 

864 CRRA: float, :math:`\rho` 

865 Coefficient of Relative Risk Aversion. 

866 Rfree: float or list[float], time varying, :math:`\mathsf{R}` 

867 Risk Free interest rate. Pass a list of floats to make Rfree time varying. 

868 DiscFac: float, :math:`\beta` 

869 Intertemporal discount factor. 

870 LivPrb: list[float], time varying, :math:`1-\mathsf{D}` 

871 Survival probability after each period. 

872 PermGroFac: list[float], time varying, :math:`\Gamma` 

873 Permanent income growth factor. 

874 BoroCnstArt: float, :math:`\underline{a}` 

875 The minimum Asset/Perminant Income ratio, None to ignore. 

876 vFuncBool: bool 

877 Whether to calculate the value function during solution. 

878 CubicBool: bool 

879 Whether to use cubic spline interpoliation. 

880 

881 Simulation Parameters 

882 --------------------- 

883 AgentCount: int 

884 Number of agents of this kind that are created during simulations. 

885 T_age: int 

886 Age after which to automatically kill agents, None to ignore. 

887 T_sim: int, required for simulation 

888 Number of periods to simulate. 

889 track_vars: list[strings] 

890 List of variables that should be tracked when running the simulation. 

891 For this agent, the options are 'PermShk', 'PrefShk', 'TranShk', 'aLvl', 'aNrm', 'bNrm', 'cNrm', 'mNrm', 'pLvl', and 'who_dies'. 

892 

893 PermShk is the agent's permanent income shock 

894 

895 PrefShk is the agent's preference shock 

896 

897 TranShk is the agent's transitory income shock 

898 

899 aLvl is the nominal asset level 

900 

901 aNrm is the normalized assets 

902 

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

904 

905 cNrm is the normalized consumption 

906 

907 mNrm is the normalized market resources 

908 

909 pLvl is the permanent income level 

910 

911 who_dies is the array of which agents died 

912 aNrmInitMean: float 

913 Mean of Log initial Normalized Assets. 

914 aNrmInitStd: float 

915 Std of Log initial Normalized Assets. 

916 pLvlInitMean: float 

917 Mean of Log initial permanent income. 

918 pLvlInitStd: float 

919 Std of Log initial permanent income. 

920 PermGroFacAgg: float 

921 Aggregate permanent income growth factor (The portion of PermGroFac attributable to aggregate productivity growth). 

922 PerfMITShk: boolean 

923 Do Perfect Foresight MIT Shock (Forces Newborns to follow solution path of the agent they replaced if True). 

924 NewbornTransShk: boolean 

925 Whether Newborns have transitory shock. 

926 

927 Attributes 

928 ---------- 

929 solution: list[Consumer solution object] 

930 Created by the :func:`.solve` method. Finite horizon models create a list with T_cycle+1 elements, for each period in the solution. 

931 Infinite horizon solutions return a list with T_cycle elements for each period in the cycle. 

932 

933 For this model, cFunc is defined over normalized market resources and :math:`\eta`, cNrm = cFunc(mNrm, :math:`\eta`). 

934 

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

936 history: Dict[Array] 

937 Created by running the :func:`.simulate()` method. 

938 Contains the variables in track_vars. Each item in the dictionary is an array with the shape (T_sim,AgentCount). 

939 Visit :class:`HARK.core.AgentType.simulate` for more information. 

940 """ 

941 

942 IncShkDstn_defaults = PrefShockConsumerType_IncShkDstn_default 

943 aXtraGrid_defaults = PrefShockConsumerType_aXtraGrid_default 

944 PrefShkDstn_defaults = PrefShockConsumerType_PrefShkDstn_default 

945 solving_defaults = PrefShockConsumerType_solving_default 

946 simulation_defaults = PrefShockConsumerType_simulation_default 

947 default_ = { 

948 "params": PrefShockConsumerType_default, 

949 "solver": solve_one_period_ConsPrefShock, 

950 "model": "ConsPrefShock.yaml", 

951 "track_vars": ["aNrm", "cNrm", "mNrm", "pLvl"], 

952 } 

953 

954 shock_vars_ = IndShockConsumerType.shock_vars_ + ["PrefShk"] 

955 time_vary_ = IndShockConsumerType.time_vary_ + ["PrefShkDstn"] 

956 distributions = [ 

957 "IncShkDstn", 

958 "PermShkDstn", 

959 "TranShkDstn", 

960 "kNrmInitDstn", 

961 "pLvlInitDstn", 

962 "PrefShkDstn", 

963 ] 

964 

965 def pre_solve(self): 

966 self.construct("solution_terminal") 

967 

968 def reset_rng(self): 

969 """ 

970 Reset the RNG behavior of this type. This method is called automatically 

971 by initialize_sim(), ensuring that each simulation run uses the same sequence 

972 of random shocks; this is necessary for structural estimation to work. 

973 This method extends IndShockConsumerType.reset_rng() to also reset elements 

974 of PrefShkDstn. 

975 

976 Parameters 

977 ---------- 

978 None 

979 

980 Returns 

981 ------- 

982 None 

983 """ 

984 IndShockConsumerType.reset_rng(self) 

985 

986 # Reset PrefShkDstn if it exists (it might not because reset_rng is called at init) 

987 if hasattr(self, "PrefShkDstn"): 

988 for dstn in self.PrefShkDstn: 

989 dstn.reset() 

990 

991 def get_shocks(self): 

992 """ 

993 Gets permanent and transitory income shocks for this period as well as preference shocks. 

994 

995 Parameters 

996 ---------- 

997 None 

998 

999 Returns 

1000 ------- 

1001 None 

1002 """ 

1003 IndShockConsumerType.get_shocks( 

1004 self 

1005 ) # Get permanent and transitory income shocks 

1006 PrefShkNow = np.zeros(self.AgentCount) # Initialize shock array 

1007 for t in range(self.T_cycle): 

1008 these = t == self.t_cycle 

1009 N = np.sum(these) 

1010 if N > 0: 

1011 PrefShkNow[these] = self.PrefShkDstn[t].draw(N) 

1012 self.shocks["PrefShk"] = PrefShkNow 

1013 

1014 def get_controls(self): 

1015 """ 

1016 Calculates consumption for each consumer of this type using the consumption functions. 

1017 

1018 Parameters 

1019 ---------- 

1020 None 

1021 

1022 Returns 

1023 ------- 

1024 None 

1025 """ 

1026 cNrmNow = np.zeros(self.AgentCount) + np.nan 

1027 for t in range(self.T_cycle): 

1028 these = t == self.t_cycle 

1029 cNrmNow[these] = self.solution[t].cFunc( 

1030 self.state_now["mNrm"][these], self.shocks["PrefShk"][these] 

1031 ) 

1032 self.controls["cNrm"] = cNrmNow 

1033 return None 

1034 

1035 def calc_bounding_values(self): # pragma: nocover 

1036 """ 

1037 NOT YET IMPLEMENTED FOR THIS CLASS 

1038 """ 

1039 raise NotImplementedError() 

1040 

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

1042 """ 

1043 NOT YET IMPLEMENTED FOR THIS CLASS 

1044 """ 

1045 raise NotImplementedError() 

1046 

1047 def check_conditions(self, verbose=None): 

1048 raise NotImplementedError() # pragma: nocover 

1049 

1050 def calc_limiting_values(self): 

1051 raise NotImplementedError() # pragma: nocover 

1052 

1053 

1054############################################################################### 

1055 

1056# Specify default parameters that differ in "kinky preference" model compared to base PrefShockConsumerType 

1057kinky_pref_different_params = { 

1058 "Rboro": 1.20, # Interest factor on assets when borrowing, a < 0 

1059 "Rsave": 1.02, # Interest factor on assets when saving, a > 0 

1060 "BoroCnstArt": None, # Kinked R only matters if borrowing is allowed 

1061} 

1062KinkyPrefConsumerType_constructors_default = ( 

1063 PrefShockConsumerType_constructors_default.copy() 

1064) 

1065KinkyPrefConsumerType_IncShkDstn_default = ( 

1066 PrefShockConsumerType_IncShkDstn_default.copy() 

1067) 

1068KinkyPrefConsumerType_pLvlInitDstn_default = ( 

1069 PrefShockConsumerType_pLvlInitDstn_default.copy() 

1070) 

1071KinkyPrefConsumerType_kNrmInitDstn_default = ( 

1072 PrefShockConsumerType_kNrmInitDstn_default.copy() 

1073) 

1074KinkyPrefConsumerType_aXtraGrid_default = PrefShockConsumerType_aXtraGrid_default.copy() 

1075KinkyPrefConsumerType_PrefShkDstn_default = ( 

1076 PrefShockConsumerType_PrefShkDstn_default.copy() 

1077) 

1078KinkyPrefConsumerType_solving_default = PrefShockConsumerType_solving_default.copy() 

1079KinkyPrefConsumerType_solving_default["constructors"] = ( 

1080 KinkyPrefConsumerType_constructors_default 

1081) 

1082KinkyPrefConsumerType_simulation_default = ( 

1083 PrefShockConsumerType_simulation_default.copy() 

1084) 

1085KinkyPrefConsumerType_solving_default.update(kinky_pref_different_params) 

1086 

1087# Make a dictionary to specify a "kinky preference" consumer 

1088KinkyPrefConsumerType_default = {} 

1089KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_IncShkDstn_default) 

1090KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_aXtraGrid_default) 

1091KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_PrefShkDstn_default) 

1092KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_kNrmInitDstn_default) 

1093KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_pLvlInitDstn_default) 

1094KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_solving_default) 

1095KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_simulation_default) 

1096init_kinky_pref = KinkyPrefConsumerType_default 

1097 

1098 

1099class KinkyPrefConsumerType(PrefShockConsumerType, KinkedRconsumerType): 

1100 r""" 

1101 A consumer type based on PrefShockConsumerType, with different 

1102 interest rates for saving (:math:`\mathsf{R}_{save}`) and borrowing 

1103 (:math:`\mathsf{R}_{boro}`). 

1104 

1105 Solver for this class is currently only compatible with linear spline interpolation. 

1106 

1107 .. math:: 

1108 \newcommand{\CRRA}{\rho} 

1109 \newcommand{\DiePrb}{\mathsf{D}} 

1110 \newcommand{\PermGroFac}{\Gamma} 

1111 \newcommand{\Rfree}{\mathsf{R}} 

1112 \newcommand{\DiscFac}{\beta} 

1113 \begin{align*} 

1114 v_t(m_t,\eta_t) &= \max_{c_t} \eta_{t} u(c_t) + \DiscFac (1-\DiePrb_{t+1}) \mathbb{E}_{t} \left[(\PermGroFac_{t+1}\psi_{t+1})^{1-\CRRA} v_{t+1}(m_{t+1},\eta_{t+1}) \right], \\ 

1115 a_t &= m_t - c_t, \\ 

1116 a_t &\geq \underline{a}, \\ 

1117 m_{t+1} &= \Rfree_t/(\PermGroFac_{t+1} \psi_{t+1}) a_t + \theta_{t+1}, \\ 

1118 \Rfree_t &= \begin{cases} 

1119 \Rfree_{boro} & \text{if } a_t < 0\\ 

1120 \Rfree_{save} & \text{if } a_t \geq 0, 

1121 \end{cases}\\ 

1122 \Rfree_{boro} &> \Rfree_{save}, \\ 

1123 (\psi_{t+1},\theta_{t+1},\eta_{t+1}) &\sim F_{t+1}, \\ 

1124 \mathbb{E}[\psi]=\mathbb{E}[\eta] &= 1. \\ 

1125 u(c,\eta) &= \frac{(c/\eta)^{1-\CRRA}}{1-\CRRA} \\ 

1126 \end{align*} 

1127 

1128 

1129 Constructors 

1130 ------------ 

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

1132 The agent's income shock distributions. 

1133 

1134 It's default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.construct_lognormal_income_process_unemployment` 

1135 aXtraGrid: Constructor 

1136 The agent's asset grid. 

1137 

1138 It's default constructor is :func:`HARK.utilities.make_assets_grid` 

1139 PrefShkDstn: Constructor, :math:`\eta` 

1140 The agent's preference shock distributions. 

1141 

1142 It's default constuctor is :func:`HARK.ConsumptionSaving.ConsPrefShockModel.make_lognormal_PrefShkDstn` 

1143 

1144 Solving Parameters 

1145 ------------------ 

1146 cycles: int 

1147 0 specifies an infinite horizon model, 1 specifies a finite model. 

1148 T_cycle: int 

1149 Number of periods in the cycle for this agent type. 

1150 CRRA: float, :math:`\rho` 

1151 Coefficient of Relative Risk Aversion. 

1152 Rfree: float or list[float], time varying, :math:`\mathsf{R}` 

1153 Risk Free interest rate. Pass a list of floats to make Rfree time varying. 

1154 Rboro: float, :math:`\mathsf{R}_{boro}` 

1155 Risk Free interest rate when assets are negative. 

1156 Rsave: float, :math:`\mathsf{R}_{save}` 

1157 Risk Free interest rate when assets are positive. 

1158 DiscFac: float, :math:`\beta` 

1159 Intertemporal discount factor. 

1160 LivPrb: list[float], time varying, :math:`1-\mathsf{D}` 

1161 Survival probability after each period. 

1162 PermGroFac: list[float], time varying, :math:`\Gamma` 

1163 Permanent income growth factor. 

1164 BoroCnstArt: float, :math:`\underline{a}` 

1165 The minimum Asset/Perminant Income ratio, None to ignore. 

1166 vFuncBool: bool 

1167 Whether to calculate the value function during solution. 

1168 CubicBool: bool 

1169 Whether to use cubic spline interpoliation. 

1170 

1171 Simulation Parameters 

1172 --------------------- 

1173 AgentCount: int 

1174 Number of agents of this kind that are created during simulations. 

1175 T_age: int 

1176 Age after which to automatically kill agents, None to ignore. 

1177 T_sim: int, required for simulation 

1178 Number of periods to simulate. 

1179 track_vars: list[strings] 

1180 List of variables that should be tracked when running the simulation. 

1181 For this agent, the options are 'PermShk', 'PrefShk', 'TranShk', 'aLvl', 'aNrm', 'bNrm', 'cNrm', 'mNrm', 'pLvl', and 'who_dies'. 

1182 

1183 PermShk is the agent's permanent income shock 

1184 

1185 PrefShk is the agent's preference shock 

1186 

1187 TranShk is the agent's transitory income shock 

1188 

1189 aLvl is the nominal asset level 

1190 

1191 aNrm is the normalized assets 

1192 

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

1194 

1195 cNrm is the normalized consumption 

1196 

1197 mNrm is the normalized market resources 

1198 

1199 pLvl is the permanent income level 

1200 

1201 who_dies is the array of which agents died 

1202 aNrmInitMean: float 

1203 Mean of Log initial Normalized Assets. 

1204 aNrmInitStd: float 

1205 Std of Log initial Normalized Assets. 

1206 pLvlInitMean: float 

1207 Mean of Log initial permanent income. 

1208 pLvlInitStd: float 

1209 Std of Log initial permanent income. 

1210 PermGroFacAgg: float 

1211 Aggregate permanent income growth factor (The portion of PermGroFac attributable to aggregate productivity growth). 

1212 PerfMITShk: boolean 

1213 Do Perfect Foresight MIT Shock (Forces Newborns to follow solution path of the agent they replaced if True). 

1214 NewbornTransShk: boolean 

1215 Whether Newborns have transitory shock. 

1216 

1217 Attributes 

1218 ---------- 

1219 solution: list[Consumer solution object] 

1220 Created by the :func:`.solve` method. Finite horizon models create a list with T_cycle+1 elements, for each period in the solution. 

1221 Infinite horizon solutions return a list with T_cycle elements for each period in the cycle. 

1222 

1223 For this model, cFunc is defined over normalized market resources and :math:`\eta`, cNrm = cFunc(mNrm, :math:`\eta`). 

1224 

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

1226 history: Dict[Array] 

1227 Created by running the :func:`.simulate()` method. 

1228 Contains the variables in track_vars. Each item in the dictionary is an array with the shape (T_sim,AgentCount). 

1229 Visit :class:`HARK.core.AgentType.simulate` for more information. 

1230 """ 

1231 

1232 IncShkDstn_defaults = KinkyPrefConsumerType_IncShkDstn_default 

1233 aXtraGrid_defaults = KinkyPrefConsumerType_aXtraGrid_default 

1234 PrefShkDstn_defaults = KinkyPrefConsumerType_PrefShkDstn_default 

1235 solving_defaults = KinkyPrefConsumerType_solving_default 

1236 simulation_defaults = KinkyPrefConsumerType_simulation_default 

1237 default_ = { 

1238 "params": KinkyPrefConsumerType_default, 

1239 "solver": solve_one_period_ConsKinkyPref, 

1240 "model": "ConsPrefShock.yaml", 

1241 "track_vars": ["aNrm", "cNrm", "mNrm", "pLvl"], 

1242 } 

1243 

1244 time_inv_ = IndShockConsumerType.time_inv_ + ["Rboro", "Rsave"] 

1245 distributions = [ 

1246 "IncShkDstn", 

1247 "PermShkDstn", 

1248 "TranShkDstn", 

1249 "kNrmInitDstn", 

1250 "pLvlInitDstn", 

1251 "PrefShkDstn", 

1252 ] 

1253 

1254 def pre_solve(self): 

1255 self.construct("solution_terminal") 

1256 

1257 def get_Rport(self): # Specify which get_Rport to use 

1258 return KinkedRconsumerType.get_Rport(self)