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

312 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-07 05:16 +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 LowerEnvelope, 

33 MargValueFuncCRRA, 

34 MargMargValueFuncCRRA, 

35 ValueFuncCRRA, 

36) 

37from HARK.rewards import UtilityFuncCRRA 

38 

39__all__ = [ 

40 "PrefShockConsumerType", 

41 "KinkyPrefConsumerType", 

42 "make_lognormal_PrefShkDstn", 

43] 

44 

45 

46def make_pref_shock_solution_terminal(CRRA): 

47 """ 

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

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

50 on the first dimension, representing market resources. 

51 

52 Parameters 

53 ---------- 

54 CRRA : float 

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

56 

57 Returns 

58 ------- 

59 solution_terminal : ConsumerSolution 

60 Terminal period solution for someone with the given CRRA. 

61 """ 

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

63 vFunc_terminal = ValueFuncCRRA(cFunc_terminal, CRRA) 

64 vPfunc_terminal = MargValueFuncCRRA(cFunc_terminal, CRRA) 

65 vPPfunc_terminal = MargMargValueFuncCRRA(cFunc_terminal, CRRA) 

66 solution_terminal = ConsumerSolution( 

67 cFunc=cFunc_terminal, 

68 vFunc=vFunc_terminal, 

69 vPfunc=vPfunc_terminal, 

70 vPPfunc=vPPfunc_terminal, 

71 mNrmMin=0.0, 

72 hNrm=0.0, 

73 MPCmin=1.0, 

74 MPCmax=1.0, 

75 ) 

76 return solution_terminal 

77 

78 

79def make_lognormal_PrefShkDstn( 

80 T_cycle, 

81 PrefShkStd, 

82 PrefShkCount, 

83 RNG, 

84 PrefShk_tail_N=0, 

85 PrefShk_tail_order=np.e, 

86 PrefShk_tail_bound=[0.02, 0.98], 

87): 

88 r""" 

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

90 period of the agent's problem. 

91 

92 .. math:: 

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

94 

95 Parameters 

96 ---------- 

97 T_cycle : int 

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

99 PrefShkStd : [float] 

100 Standard deviation of log preference shocks in each period. 

101 PrefShkCount : int 

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

103 RNG : RandomState 

104 The AgentType's internal random number generator. 

105 PrefShk_tail_N : int 

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

107 PrefShk_tail_order : float 

108 Scaling factor for tail nodes (optional). 

109 PrefShk_tail_bound : [float,float] 

110 CDF bounds for tail nodes (optional). 

111 

112 Returns 

113 ------- 

114 PrefShkDstn : [DiscreteDistribution] 

115 List of discretized lognormal distributions for shocks. 

116 """ 

117 PrefShkDstn = [] # discrete distributions of preference shocks 

118 for t in range(T_cycle): 

119 PrefShkStd = PrefShkStd[t] 

120 new_dstn = MeanOneLogNormal( 

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

122 ).discretize( 

123 N=PrefShkCount, 

124 method="equiprobable", 

125 tail_N=PrefShk_tail_N, 

126 tail_order=PrefShk_tail_order, 

127 tail_bound=PrefShk_tail_bound, 

128 ) 

129 PrefShkDstn.append(new_dstn) 

130 return PrefShkDstn 

131 

132 

133############################################################################### 

134 

135 

136def solve_one_period_ConsPrefShock( 

137 solution_next, 

138 IncShkDstn, 

139 PrefShkDstn, 

140 LivPrb, 

141 DiscFac, 

142 CRRA, 

143 Rfree, 

144 PermGroFac, 

145 BoroCnstArt, 

146 aXtraGrid, 

147 vFuncBool, 

148 CubicBool, 

149): 

150 """ 

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

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

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

154 their marginal utility of consumption. 

155 

156 Parameters 

157 ---------- 

158 solution_next : ConsumerSolution 

159 The solution to the succeeding one period problem. 

160 IncShkDstn : distribution.Distribution 

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

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

163 permanent shocks, transitory shocks. 

164 PrefShkDstn : distribution.Distribution 

165 Discrete distribution of the multiplicative utility shifter. Order: 

166 probabilities, preference shocks. 

167 LivPrb : float 

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

169 the succeeding period. 

170 DiscFac : float 

171 Intertemporal discount factor for future utility. 

172 CRRA : float 

173 Coefficient of relative risk aversion. 

174 Rfree : float 

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

176 PermGroGac : float 

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

178 BoroCnstArt: float or None 

179 Borrowing constraint for the minimum allowable assets to end the 

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

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

182 rowing constraint. 

183 aXtraGrid: np.array 

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

185 absolute minimum acceptable level. 

186 vFuncBool: boolean 

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

188 included in the reported solution. 

189 CubicBool: boolean 

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

191 polation. 

192 

193 Returns 

194 ------- 

195 solution: ConsumerSolution 

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

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

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

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

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

201 function is defined over normalized market resources and the preference 

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

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

204 """ 

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

206 uFunc = UtilityFuncCRRA(CRRA) 

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

208 

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

210 ShkPrbsNext = IncShkDstn.pmv 

211 PermShkValsNext = IncShkDstn.atoms[0] 

212 TranShkValsNext = IncShkDstn.atoms[1] 

213 PermShkMinNext = np.min(PermShkValsNext) 

214 TranShkMinNext = np.min(TranShkValsNext) 

215 PrefShkPrbs = PrefShkDstn.pmv 

216 PrefShkVals = PrefShkDstn.atoms.flatten() 

217 

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

219 IncNext = PermShkValsNext * TranShkValsNext 

220 WorstIncNext = PermShkMinNext * TranShkMinNext 

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

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

223 

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

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

226 vPfuncNext = solution_next.vPfunc 

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

228 

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

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

231 try: 

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

233 except: 

234 MPCminNow = 0.0 

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

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

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

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

239 

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

241 PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rfree 

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

243 

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

245 # and artificial borrowing constraints 

246 if BoroCnstArt is None: 

247 mNrmMinNow = BoroCnstNat 

248 else: 

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

250 

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

252 # or artificial borrowing constraint actually binds 

253 if BoroCnstNat < mNrmMinNow: 

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

255 else: 

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

257 

258 # Define the borrowing-constrained consumption function 

259 cFuncNowCnst = LinearInterp( 

260 np.array([mNrmMinNow, mNrmMinNow + 1.0]), np.array([0.0, 1.0]) 

261 ) 

262 

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

264 aNrmNow = np.asarray(aXtraGrid) + BoroCnstNat 

265 

266 # Define local functions for taking future expectations 

267 def calc_mNrmNext(S, a, R): 

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

269 

270 def calc_vNext(S, a, R): 

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

272 calc_mNrmNext(S, a, R) 

273 ) 

274 

275 def calc_vPnext(S, a, R): 

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

277 

278 def calc_vPPnext(S, a, R): 

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

280 

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

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

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

284 

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

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

287 PrefShkCount = PrefShkVals.size 

288 PrefShk_temp = np.tile( 

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

290 (1, cNrm_base.size), 

291 ) 

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

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

294 # These are the endogenous gridpoints, as usual 

295 

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

297 m_for_interpolation = np.concatenate( 

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

299 ) 

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

301 

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

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

304 if CubicBool: 

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

306 raise ValueError( 

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

308 ) 

309 

310 # Make the preference-shock specific consumption functions 

311 cFuncs_by_PrefShk = [] 

312 for j in range(PrefShkCount): 

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

314 cFunc_this_shk = LowerEnvelope( 

315 LinearInterp( 

316 m_for_interpolation[j, :], 

317 c_for_interpolation[j, :], 

318 intercept_limit=hNrmNow * MPCmin_j, 

319 slope_limit=MPCmin_j, 

320 ), 

321 cFuncNowCnst, 

322 ) 

323 cFuncs_by_PrefShk.append(cFunc_this_shk) 

324 

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

326 cFuncNow = LinearInterpOnInterp1D(cFuncs_by_PrefShk, PrefShkVals) 

327 

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

329 m_grid = aXtraGrid + mNrmMinNow 

330 vP_vec = np.zeros_like(m_grid) 

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

332 vP_vec += ( 

333 uFunc.der(cFuncs_by_PrefShk[j](m_grid)) * PrefShkPrbs[j] * PrefShkVals[j] 

334 ) 

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

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

337 

338 # Define this period's marginal marginal value function 

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

340 

341 # Construct this period's value function if requested 

342 if vFuncBool: 

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

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

345 EndOfPrdvNvrs = uFunc.inv( 

346 EndOfPrdv 

347 ) # value transformed through inverse utility 

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

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

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

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

352 

353 # Construct the end-of-period value function 

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

355 EndOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, EndOfPrdvNvrs, EndOfPrdvNvrsP) 

356 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA) 

357 

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

359 # accounting for all of the discrete preference shocks 

360 mNrm_temp = mNrmMinNow + aXtraGrid 

361 v_temp = np.zeros_like(mNrm_temp) 

362 vP_temp = np.zeros_like(mNrm_temp) 

363 for j in range(PrefShkCount): 

364 this_shock = PrefShkVals[j] 

365 this_prob = PrefShkPrbs[j] 

366 cNrm_temp = cFuncNow(mNrm_temp, this_shock * np.ones_like(mNrm_temp)) 

367 aNrm_temp = mNrm_temp - cNrm_temp 

368 v_temp += this_prob * ( 

369 this_shock * uFunc(cNrm_temp) + EndOfPrd_vFunc(aNrm_temp) 

370 ) 

371 vP_temp += this_prob * this_shock * uFunc.der(cNrm_temp) 

372 

373 # Construct the beginning-of-period value function 

374 # value transformed through inverse utility 

375 vNvrs_temp = uFunc.inv(v_temp) 

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

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

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

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

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

381 vNvrsFuncNow = CubicInterp( 

382 mNrm_temp, vNvrs_temp, vNvrsP_temp, MPCminNvrs * hNrmNow, MPCminNvrs 

383 ) 

384 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA) 

385 

386 else: 

387 vFuncNow = NullFunc() # Dummy object 

388 

389 # Create and return this period's solution 

390 solution_now = ConsumerSolution( 

391 cFunc=cFuncNow, 

392 vFunc=vFuncNow, 

393 vPfunc=vPfuncNow, 

394 vPPfunc=vPPfuncNow, 

395 mNrmMin=mNrmMinNow, 

396 hNrm=hNrmNow, 

397 MPCmin=MPCminNow, 

398 MPCmax=MPCmaxEff, 

399 ) 

400 return solution_now 

401 

402 

403############################################################################### 

404 

405 

406def solve_one_period_ConsKinkyPref( 

407 solution_next, 

408 IncShkDstn, 

409 PrefShkDstn, 

410 LivPrb, 

411 DiscFac, 

412 CRRA, 

413 Rboro, 

414 Rsave, 

415 PermGroFac, 

416 BoroCnstArt, 

417 aXtraGrid, 

418 vFuncBool, 

419 CubicBool, 

420): 

421 """ 

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

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

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

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

426 plicative shifter to their marginal utility of consumption. 

427 

428 Parameters 

429 ---------- 

430 solution_next : ConsumerSolution 

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

432 IncShkDstn : distribution.Distribution 

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

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

435 PrefShkDstn : distribution.Distribution 

436 Discrete distribution of the multiplicative utility shifter. Order: 

437 probabilities, preference shocks. 

438 LivPrb : float 

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

440 the succeeding period. 

441 DiscFac : float 

442 Intertemporal discount factor for future utility. 

443 CRRA : float 

444 Coefficient of relative risk aversion. 

445 Rboro: float 

446 Interest factor on assets between this period and the succeeding 

447 period when assets are negative. 

448 Rsave: float 

449 Interest factor on assets between this period and the succeeding 

450 period when assets are positive. 

451 PermGroFac : float 

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

453 BoroCnstArt: float or None 

454 Borrowing constraint for the minimum allowable assets to end the 

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

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

457 rowing constraint. 

458 aXtraGrid: np.array 

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

460 absolute minimum acceptable level. 

461 vFuncBool: boolean 

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

463 included in the reported solution. 

464 CubicBool: boolean 

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

466 polation. 

467 

468 Returns 

469 ------- 

470 solution_now : ConsumerSolution 

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

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

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

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

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

476 function is defined over normalized market resources and the preference 

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

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

479 """ 

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

481 assert Rboro >= Rsave, ( 

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

483 ) 

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

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

486 if Rboro == Rsave: 

487 solution_now = solve_one_period_ConsPrefShock( 

488 solution_next, 

489 IncShkDstn, 

490 PrefShkDstn, 

491 LivPrb, 

492 DiscFac, 

493 CRRA, 

494 Rboro, 

495 PermGroFac, 

496 BoroCnstArt, 

497 aXtraGrid, 

498 vFuncBool, 

499 CubicBool, 

500 ) 

501 return solution_now 

502 

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

504 uFunc = UtilityFuncCRRA(CRRA) 

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

506 

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

508 ShkPrbsNext = IncShkDstn.pmv 

509 PermShkValsNext = IncShkDstn.atoms[0] 

510 TranShkValsNext = IncShkDstn.atoms[1] 

511 PermShkMinNext = np.min(PermShkValsNext) 

512 TranShkMinNext = np.min(TranShkValsNext) 

513 PrefShkPrbs = PrefShkDstn.pmv 

514 PrefShkVals = PrefShkDstn.atoms.flatten() 

515 

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

517 IncNext = PermShkValsNext * TranShkValsNext 

518 WorstIncNext = PermShkMinNext * TranShkMinNext 

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

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

521 

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

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

524 vPfuncNext = solution_next.vPfunc 

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

526 

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

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

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

530 try: 

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

532 except: 

533 MPCminNow = 0.0 

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

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

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

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

538 

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

540 PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rboro 

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

542 

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

544 # and artificial borrowing constraints 

545 if BoroCnstArt is None: 

546 mNrmMinNow = BoroCnstNat 

547 else: 

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

549 

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

551 # or artificial borrowing constraint actually binds 

552 if BoroCnstNat < mNrmMinNow: 

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

554 else: 

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

556 

557 # Define the borrowing-constrained consumption function 

558 cFuncNowCnst = LinearInterp( 

559 np.array([mNrmMinNow, mNrmMinNow + 1.0]), np.array([0.0, 1.0]) 

560 ) 

561 

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

563 aNrmNow = np.sort( 

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

565 ) 

566 

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

568 Rfree = Rsave * np.ones_like(aNrmNow) 

569 Rfree[aNrmNow < 0] = Rboro 

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

571 Rfree[i_kink] = Rboro 

572 

573 # Define local functions for taking future expectations 

574 def calc_mNrmNext(S, a, R): 

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

576 

577 def calc_vNext(S, a, R): 

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

579 calc_mNrmNext(S, a, R) 

580 ) 

581 

582 def calc_vPnext(S, a, R): 

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

584 

585 def calc_vPPnext(S, a, R): 

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

587 

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

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

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

591 

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

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

594 PrefShkCount = PrefShkVals.size 

595 PrefShk_temp = np.tile( 

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

597 (1, cNrm_base.size), 

598 ) 

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

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

601 # These are the endogenous gridpoints, as usual 

602 

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

604 m_for_interpolation = np.concatenate( 

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

606 ) 

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

608 

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

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

611 if CubicBool: 

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

613 raise ValueError( 

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

615 ) 

616 

617 # Make the preference-shock specific consumption functions 

618 cFuncs_by_PrefShk = [] 

619 for j in range(PrefShkCount): 

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

621 cFunc_this_shk = LowerEnvelope( 

622 LinearInterp( 

623 m_for_interpolation[j, :], 

624 c_for_interpolation[j, :], 

625 intercept_limit=hNrmNow * MPCmin_j, 

626 slope_limit=MPCmin_j, 

627 ), 

628 cFuncNowCnst, 

629 ) 

630 cFuncs_by_PrefShk.append(cFunc_this_shk) 

631 

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

633 cFuncNow = LinearInterpOnInterp1D(cFuncs_by_PrefShk, PrefShkVals) 

634 

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

636 m_grid = aXtraGrid + mNrmMinNow 

637 vP_vec = np.zeros_like(m_grid) 

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

639 vP_vec += ( 

640 uFunc.der(cFuncs_by_PrefShk[j](m_grid)) * PrefShkPrbs[j] * PrefShkVals[j] 

641 ) 

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

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

644 

645 # Define this period's marginal marginal value function 

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

647 

648 # Construct this period's value function if requested 

649 if vFuncBool: 

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

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

652 EndOfPrdvNvrs = uFunc.inv( 

653 EndOfPrdv 

654 ) # value transformed through inverse utility 

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

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

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

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

659 

660 # Construct the end-of-period value function 

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

662 EndOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, EndOfPrdvNvrs, EndOfPrdvNvrsP) 

663 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA) 

664 

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

666 # accounting for all of the discrete preference shocks 

667 mNrm_temp = mNrmMinNow + aXtraGrid 

668 v_temp = np.zeros_like(mNrm_temp) 

669 vP_temp = np.zeros_like(mNrm_temp) 

670 for j in range(PrefShkCount): 

671 this_shock = PrefShkVals[j] 

672 this_prob = PrefShkPrbs[j] 

673 cNrm_temp = cFuncNow(mNrm_temp, this_shock * np.ones_like(mNrm_temp)) 

674 aNrm_temp = mNrm_temp - cNrm_temp 

675 v_temp += this_prob * ( 

676 this_shock * uFunc(cNrm_temp) + EndOfPrd_vFunc(aNrm_temp) 

677 ) 

678 vP_temp += this_prob * this_shock * uFunc.der(cNrm_temp) 

679 

680 # Construct the beginning-of-period value function 

681 # value transformed through inverse utility 

682 vNvrs_temp = uFunc.inv(v_temp) 

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

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

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

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

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

688 vNvrsFuncNow = CubicInterp( 

689 mNrm_temp, vNvrs_temp, vNvrsP_temp, MPCminNvrs * hNrmNow, MPCminNvrs 

690 ) 

691 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA) 

692 

693 else: 

694 vFuncNow = NullFunc() # Dummy object 

695 

696 # Create and return this period's solution 

697 solution_now = ConsumerSolution( 

698 cFunc=cFuncNow, 

699 vFunc=vFuncNow, 

700 vPfunc=vPfuncNow, 

701 vPPfunc=vPPfuncNow, 

702 mNrmMin=mNrmMinNow, 

703 hNrm=hNrmNow, 

704 MPCmin=MPCminNow, 

705 MPCmax=MPCmaxEff, 

706 ) 

707 return solution_now 

708 

709 

710############################################################################### 

711 

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

713PrefShockConsumerType_constructors_default = { 

714 "IncShkDstn": construct_lognormal_income_process_unemployment, 

715 "PermShkDstn": get_PermShkDstn_from_IncShkDstn, 

716 "TranShkDstn": get_TranShkDstn_from_IncShkDstn, 

717 "aXtraGrid": make_assets_grid, 

718 "PrefShkDstn": make_lognormal_PrefShkDstn, 

719 "solution_terminal": make_pref_shock_solution_terminal, 

720 "kNrmInitDstn": make_lognormal_kNrm_init_dstn, 

721 "pLvlInitDstn": make_lognormal_pLvl_init_dstn, 

722} 

723 

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

725PrefShockConsumerType_kNrmInitDstn_default = { 

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

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

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

729} 

730 

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

732PrefShockConsumerType_pLvlInitDstn_default = { 

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

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

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

736} 

737 

738# Default parameters to make IncShkDstn using construct_lognormal_income_process_unemployment 

739PrefShockConsumerType_IncShkDstn_default = { 

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

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

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

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

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

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

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

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

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

749} 

750 

751# Default parameters to make aXtraGrid using construct_assets_grid 

752 

753PrefShockConsumerType_aXtraGrid_default = { 

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

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

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

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

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

759} 

760 

761# Default parameters to make PrefShkDstn using make_lognormal_PrefShkDstn 

762 

763PrefShockConsumerType_PrefShkDstn_default = { 

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

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

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

767} 

768 

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

770PrefShockConsumerType_solving_default = { 

771 # BASIC HARK PARAMETERS REQUIRED TO SOLVE THE MODEL 

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

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

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

775 "constructors": PrefShockConsumerType_constructors_default, # See dictionary above 

776 # PRIMITIVE RAW PARAMETERS REQUIRED TO SOLVE THE MODEL 

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

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

779 "DiscFac": 0.96, # Intertemporal discount factor 

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

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

782 "BoroCnstArt": 0.0, # Artificial borrowing constraint 

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

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

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

786} 

787PrefShockConsumerType_simulation_default = { 

788 # PARAMETERS REQUIRED TO SIMULATE THE MODEL 

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

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

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

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

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

794 # ADDITIONAL OPTIONAL PARAMETERS 

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

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

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

798} 

799 

800PrefShockConsumerType_default = {} 

801PrefShockConsumerType_default.update(PrefShockConsumerType_IncShkDstn_default) 

802PrefShockConsumerType_default.update(PrefShockConsumerType_aXtraGrid_default) 

803PrefShockConsumerType_default.update(PrefShockConsumerType_PrefShkDstn_default) 

804PrefShockConsumerType_default.update(PrefShockConsumerType_kNrmInitDstn_default) 

805PrefShockConsumerType_default.update(PrefShockConsumerType_pLvlInitDstn_default) 

806PrefShockConsumerType_default.update(PrefShockConsumerType_solving_default) 

807PrefShockConsumerType_default.update(PrefShockConsumerType_simulation_default) 

808init_preference_shocks = ( 

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

810) 

811 

812 

813class PrefShockConsumerType(IndShockConsumerType): 

814 r""" 

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

816 

817 .. math:: 

818 \newcommand{\CRRA}{\rho} 

819 \newcommand{\DiePrb}{\mathsf{D}} 

820 \newcommand{\PermGroFac}{\Gamma} 

821 \newcommand{\Rfree}{\mathsf{R}} 

822 \newcommand{\DiscFac}{\beta} 

823 \begin{align*} 

824 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], \\ 

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

826 a_t &= m_t - c_t, \\ 

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

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

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

830 \mathbb{E}[\psi]=\mathbb{E}[\theta] &= 1, \\ 

831 u(c) &= \frac{c^{1-\CRRA}}{1-\CRRA} \\ 

832 \end{align*} 

833 

834 

835 Constructors 

836 ------------ 

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

838 The agent's income shock distributions. 

839 

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

841 aXtraGrid: Constructor 

842 The agent's asset grid. 

843 

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

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

846 The agent's preference shock distributions. 

847 

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

849 

850 Solving Parameters 

851 ------------------ 

852 cycles: int 

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

854 T_cycle: int 

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

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

857 Coefficient of Relative Risk Aversion. 

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

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

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

861 Intertemporal discount factor. 

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

863 Survival probability after each period. 

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

865 Permanent income growth factor. 

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

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

868 vFuncBool: bool 

869 Whether to calculate the value function during solution. 

870 CubicBool: bool 

871 Whether to use cubic spline interpoliation. 

872 

873 Simulation Parameters 

874 --------------------- 

875 AgentCount: int 

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

877 T_age: int 

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

879 T_sim: int, required for simulation 

880 Number of periods to simulate. 

881 track_vars: list[strings] 

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

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

884 

885 PermShk is the agent's permanent income shock 

886 

887 PrefShk is the agent's preference shock 

888 

889 TranShk is the agent's transitory income shock 

890 

891 aLvl is the nominal asset level 

892 

893 aNrm is the normalized assets 

894 

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

896 

897 cNrm is the normalized consumption 

898 

899 mNrm is the normalized market resources 

900 

901 pLvl is the permanent income level 

902 

903 who_dies is the array of which agents died 

904 aNrmInitMean: float 

905 Mean of Log initial Normalized Assets. 

906 aNrmInitStd: float 

907 Std of Log initial Normalized Assets. 

908 pLvlInitMean: float 

909 Mean of Log initial permanent income. 

910 pLvlInitStd: float 

911 Std of Log initial permanent income. 

912 PermGroFacAgg: float 

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

914 PerfMITShk: boolean 

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

916 NewbornTransShk: boolean 

917 Whether Newborns have transitory shock. 

918 

919 Attributes 

920 ---------- 

921 solution: list[Consumer solution object] 

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

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

924 

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

926 

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

928 history: Dict[Array] 

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

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

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

932 """ 

933 

934 IncShkDstn_defaults = PrefShockConsumerType_IncShkDstn_default 

935 aXtraGrid_defaults = PrefShockConsumerType_aXtraGrid_default 

936 PrefShkDstn_defaults = PrefShockConsumerType_PrefShkDstn_default 

937 solving_defaults = PrefShockConsumerType_solving_default 

938 simulation_defaults = PrefShockConsumerType_simulation_default 

939 default_ = { 

940 "params": PrefShockConsumerType_default, 

941 "solver": solve_one_period_ConsPrefShock, 

942 "model": "ConsMarkov.yaml", 

943 } 

944 

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

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

947 distributions = [ 

948 "IncShkDstn", 

949 "PermShkDstn", 

950 "TranShkDstn", 

951 "kNrmInitDstn", 

952 "pLvlInitDstn", 

953 "PrefShkDstn", 

954 ] 

955 

956 def pre_solve(self): 

957 self.construct("solution_terminal") 

958 

959 def reset_rng(self): 

960 """ 

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

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

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

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

965 of PrefShkDstn. 

966 

967 Parameters 

968 ---------- 

969 None 

970 

971 Returns 

972 ------- 

973 None 

974 """ 

975 IndShockConsumerType.reset_rng(self) 

976 

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

978 if hasattr(self, "PrefShkDstn"): 

979 for dstn in self.PrefShkDstn: 

980 dstn.reset() 

981 

982 def get_shocks(self): 

983 """ 

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

985 

986 Parameters 

987 ---------- 

988 None 

989 

990 Returns 

991 ------- 

992 None 

993 """ 

994 IndShockConsumerType.get_shocks( 

995 self 

996 ) # Get permanent and transitory income shocks 

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

998 for t in range(self.T_cycle): 

999 these = t == self.t_cycle 

1000 N = np.sum(these) 

1001 if N > 0: 

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

1003 self.shocks["PrefShk"] = PrefShkNow 

1004 

1005 def get_controls(self): 

1006 """ 

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

1008 

1009 Parameters 

1010 ---------- 

1011 None 

1012 

1013 Returns 

1014 ------- 

1015 None 

1016 """ 

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

1018 for t in range(self.T_cycle): 

1019 these = t == self.t_cycle 

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

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

1022 ) 

1023 self.controls["cNrm"] = cNrmNow 

1024 return None 

1025 

1026 def calc_bounding_values(self): # pragma: nocover 

1027 """ 

1028 Calculate human wealth plus minimum and maximum MPC in an infinite 

1029 horizon model with only one period repeated indefinitely. Store results 

1030 as attributes of self. Human wealth is the present discounted value of 

1031 expected future income after receiving income this period, ignoring mort- 

1032 ality. The maximum MPC is the limit of the MPC as m --> mNrmMin. The 

1033 minimum MPC is the limit of the MPC as m --> infty. 

1034 

1035 NOT YET IMPLEMENTED FOR THIS CLASS 

1036 

1037 Parameters 

1038 ---------- 

1039 None 

1040 

1041 Returns 

1042 ------- 

1043 None 

1044 """ 

1045 raise NotImplementedError() 

1046 

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

1048 """ 

1049 Creates a "normalized Euler error" function for this instance, mapping 

1050 from market resources to "consumption error per dollar of consumption." 

1051 Stores result in attribute eulerErrorFunc as an interpolated function. 

1052 Has option to use approximate income distribution stored in self.IncShkDstn 

1053 or to use a (temporary) very dense approximation. 

1054 

1055 NOT YET IMPLEMENTED FOR THIS CLASS 

1056 

1057 Parameters 

1058 ---------- 

1059 mMax : float 

1060 Maximum normalized market resources for the Euler error function. 

1061 approx_inc_dstn : Boolean 

1062 Indicator for whether to use the approximate discrete income distri- 

1063 bution stored in self.IncShkDstn[0], or to use a very accurate 

1064 discrete approximation instead. When True, uses approximation in 

1065 IncShkDstn; when False, makes and uses a very dense approximation. 

1066 

1067 Returns 

1068 ------- 

1069 None 

1070 

1071 """ 

1072 raise NotImplementedError() 

1073 

1074 def check_conditions(self, verbose=None): 

1075 raise NotImplementedError() 

1076 

1077 def calc_limiting_values(self): 

1078 raise NotImplementedError() 

1079 

1080 

1081############################################################################### 

1082 

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

1084kinky_pref_different_params = { 

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

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

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

1088} 

1089KinkyPrefConsumerType_constructors_default = ( 

1090 PrefShockConsumerType_constructors_default.copy() 

1091) 

1092KinkyPrefConsumerType_IncShkDstn_default = ( 

1093 PrefShockConsumerType_IncShkDstn_default.copy() 

1094) 

1095KinkyPrefConsumerType_pLvlInitDstn_default = ( 

1096 PrefShockConsumerType_pLvlInitDstn_default.copy() 

1097) 

1098KinkyPrefConsumerType_kNrmInitDstn_default = ( 

1099 PrefShockConsumerType_kNrmInitDstn_default.copy() 

1100) 

1101KinkyPrefConsumerType_aXtraGrid_default = PrefShockConsumerType_aXtraGrid_default.copy() 

1102KinkyPrefConsumerType_PrefShkDstn_default = ( 

1103 PrefShockConsumerType_PrefShkDstn_default.copy() 

1104) 

1105KinkyPrefConsumerType_solving_default = PrefShockConsumerType_solving_default.copy() 

1106KinkyPrefConsumerType_solving_default["constructors"] = ( 

1107 KinkyPrefConsumerType_constructors_default 

1108) 

1109KinkyPrefConsumerType_simulation_default = ( 

1110 PrefShockConsumerType_simulation_default.copy() 

1111) 

1112KinkyPrefConsumerType_solving_default.update(kinky_pref_different_params) 

1113 

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

1115KinkyPrefConsumerType_default = {} 

1116KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_IncShkDstn_default) 

1117KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_aXtraGrid_default) 

1118KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_PrefShkDstn_default) 

1119KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_kNrmInitDstn_default) 

1120KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_pLvlInitDstn_default) 

1121KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_solving_default) 

1122KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_simulation_default) 

1123init_kinky_pref = KinkyPrefConsumerType_default 

1124 

1125 

1126class KinkyPrefConsumerType(PrefShockConsumerType, KinkedRconsumerType): 

1127 r""" 

1128 A consumer type based on PrefShockConsumerType, with different 

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

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

1131 

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

1133 

1134 .. math:: 

1135 \newcommand{\CRRA}{\rho} 

1136 \newcommand{\DiePrb}{\mathsf{D}} 

1137 \newcommand{\PermGroFac}{\Gamma} 

1138 \newcommand{\Rfree}{\mathsf{R}} 

1139 \newcommand{\DiscFac}{\beta} 

1140 \begin{align*} 

1141 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], \\ 

1142 a_t &= m_t - c_t, \\ 

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

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

1145 \Rfree_t &= \begin{cases} 

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

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

1148 \end{cases}\\ 

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

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

1151 \mathbb{E}[\psi]=\mathbb{E}[\theta] &= 1. \\ 

1152 u(c) &= \frac{c^{1-\CRRA}}{1-\CRRA} \\ 

1153 \end{align*} 

1154 

1155 

1156 Constructors 

1157 ------------ 

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

1159 The agent's income shock distributions. 

1160 

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

1162 aXtraGrid: Constructor 

1163 The agent's asset grid. 

1164 

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

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

1167 The agent's preference shock distributions. 

1168 

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

1170 

1171 Solving Parameters 

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

1173 cycles: int 

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

1175 T_cycle: int 

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

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

1178 Coefficient of Relative Risk Aversion. 

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

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

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

1182 Risk Free interest rate when assets are negative. 

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

1184 Risk Free interest rate when assets are positive. 

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

1186 Intertemporal discount factor. 

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

1188 Survival probability after each period. 

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

1190 Permanent income growth factor. 

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

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

1193 vFuncBool: bool 

1194 Whether to calculate the value function during solution. 

1195 CubicBool: bool 

1196 Whether to use cubic spline interpoliation. 

1197 

1198 Simulation Parameters 

1199 --------------------- 

1200 AgentCount: int 

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

1202 T_age: int 

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

1204 T_sim: int, required for simulation 

1205 Number of periods to simulate. 

1206 track_vars: list[strings] 

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

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

1209 

1210 PermShk is the agent's permanent income shock 

1211 

1212 PrefShk is the agent's preference shock 

1213 

1214 TranShk is the agent's transitory income shock 

1215 

1216 aLvl is the nominal asset level 

1217 

1218 aNrm is the normalized assets 

1219 

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

1221 

1222 cNrm is the normalized consumption 

1223 

1224 mNrm is the normalized market resources 

1225 

1226 pLvl is the permanent income level 

1227 

1228 who_dies is the array of which agents died 

1229 aNrmInitMean: float 

1230 Mean of Log initial Normalized Assets. 

1231 aNrmInitStd: float 

1232 Std of Log initial Normalized Assets. 

1233 pLvlInitMean: float 

1234 Mean of Log initial permanent income. 

1235 pLvlInitStd: float 

1236 Std of Log initial permanent income. 

1237 PermGroFacAgg: float 

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

1239 PerfMITShk: boolean 

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

1241 NewbornTransShk: boolean 

1242 Whether Newborns have transitory shock. 

1243 

1244 Attributes 

1245 ---------- 

1246 solution: list[Consumer solution object] 

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

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

1249 

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

1251 

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

1253 history: Dict[Array] 

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

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

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

1257 """ 

1258 

1259 IncShkDstn_defaults = KinkyPrefConsumerType_IncShkDstn_default 

1260 aXtraGrid_defaults = KinkyPrefConsumerType_aXtraGrid_default 

1261 PrefShkDstn_defaults = KinkyPrefConsumerType_PrefShkDstn_default 

1262 solving_defaults = KinkyPrefConsumerType_solving_default 

1263 simulation_defaults = KinkyPrefConsumerType_simulation_default 

1264 default_ = { 

1265 "params": KinkyPrefConsumerType_default, 

1266 "solver": solve_one_period_ConsKinkyPref, 

1267 } 

1268 

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

1270 distributions = [ 

1271 "IncShkDstn", 

1272 "PermShkDstn", 

1273 "TranShkDstn", 

1274 "kNrmInitDstn", 

1275 "pLvlInitDstn", 

1276 "PrefShkDstn", 

1277 ] 

1278 

1279 def pre_solve(self): 

1280 self.construct("solution_terminal") 

1281 

1282 def get_Rfree(self): # Specify which get_Rfree to use 

1283 return KinkedRconsumerType.get_Rfree(self)