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

309 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-02 05:14 +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_basic_CRRA_solution_terminal, 

19 make_lognormal_kNrm_init_dstn, 

20 make_lognormal_pLvl_init_dstn, 

21) 

22from HARK.Calibration.Income.IncomeProcesses import ( 

23 construct_lognormal_income_process_unemployment, 

24 get_PermShkDstn_from_IncShkDstn, 

25 get_TranShkDstn_from_IncShkDstn, 

26) 

27from HARK.distributions import MeanOneLogNormal, expected 

28from HARK.interpolation import ( 

29 CubicInterp, 

30 LinearInterp, 

31 LinearInterpOnInterp1D, 

32 LowerEnvelope, 

33 MargValueFuncCRRA, 

34 ValueFuncCRRA, 

35) 

36from HARK.rewards import UtilityFuncCRRA 

37 

38__all__ = [ 

39 "PrefShockConsumerType", 

40 "KinkyPrefConsumerType", 

41 "make_lognormal_PrefShkDstn", 

42] 

43 

44 

45def make_lognormal_PrefShkDstn( 

46 T_cycle, 

47 PrefShkStd, 

48 PrefShkCount, 

49 RNG, 

50 PrefShk_tail_N=0, 

51 PrefShk_tail_order=np.e, 

52 PrefShk_tail_bound=[0.02, 0.98], 

53): 

54 r""" 

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

56 period of the agent's problem. 

57 

58 .. math:: 

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

60 

61 Parameters 

62 ---------- 

63 T_cycle : int 

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

65 PrefShkStd : [float] 

66 Standard deviation of log preference shocks in each period. 

67 PrefShkCount : int 

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

69 RNG : RandomState 

70 The AgentType's internal random number generator. 

71 PrefShk_tail_N : int 

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

73 PrefShk_tail_order : float 

74 Scaling factor for tail nodes (optional). 

75 PrefShk_tail_bound : [float,float] 

76 CDF bounds for tail nodes (optional). 

77 

78 Returns 

79 ------- 

80 PrefShkDstn : [DiscreteDistribution] 

81 List of discretized lognormal distributions for shocks. 

82 """ 

83 PrefShkDstn = [] # discrete distributions of preference shocks 

84 for t in range(T_cycle): 

85 PrefShkStd = PrefShkStd[t] 

86 new_dstn = MeanOneLogNormal( 

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

88 ).discretize( 

89 N=PrefShkCount, 

90 method="equiprobable", 

91 tail_N=PrefShk_tail_N, 

92 tail_order=PrefShk_tail_order, 

93 tail_bound=PrefShk_tail_bound, 

94 ) 

95 PrefShkDstn.append(new_dstn) 

96 return PrefShkDstn 

97 

98 

99############################################################################### 

100 

101 

102def solve_one_period_ConsPrefShock( 

103 solution_next, 

104 IncShkDstn, 

105 PrefShkDstn, 

106 LivPrb, 

107 DiscFac, 

108 CRRA, 

109 Rfree, 

110 PermGroFac, 

111 BoroCnstArt, 

112 aXtraGrid, 

113 vFuncBool, 

114 CubicBool, 

115): 

116 """ 

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

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

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

120 their marginal utility of consumption. 

121 

122 Parameters 

123 ---------- 

124 solution_next : ConsumerSolution 

125 The solution to the succeeding one period problem. 

126 IncShkDstn : distribution.Distribution 

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

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

129 permanent shocks, transitory shocks. 

130 PrefShkDstn : distribution.Distribution 

131 Discrete distribution of the multiplicative utility shifter. Order: 

132 probabilities, preference shocks. 

133 LivPrb : float 

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

135 the succeeding period. 

136 DiscFac : float 

137 Intertemporal discount factor for future utility. 

138 CRRA : float 

139 Coefficient of relative risk aversion. 

140 Rfree : float 

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

142 PermGroGac : float 

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

144 BoroCnstArt: float or None 

145 Borrowing constraint for the minimum allowable assets to end the 

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

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

148 rowing constraint. 

149 aXtraGrid: np.array 

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

151 absolute minimum acceptable level. 

152 vFuncBool: boolean 

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

154 included in the reported solution. 

155 CubicBool: boolean 

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

157 polation. 

158 

159 Returns 

160 ------- 

161 solution: ConsumerSolution 

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

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

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

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

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

167 function is defined over normalized market resources and the preference 

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

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

170 """ 

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

172 uFunc = UtilityFuncCRRA(CRRA) 

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

174 

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

176 ShkPrbsNext = IncShkDstn.pmv 

177 PermShkValsNext = IncShkDstn.atoms[0] 

178 TranShkValsNext = IncShkDstn.atoms[1] 

179 PermShkMinNext = np.min(PermShkValsNext) 

180 TranShkMinNext = np.min(TranShkValsNext) 

181 PrefShkPrbs = PrefShkDstn.pmv 

182 PrefShkVals = PrefShkDstn.atoms.flatten() 

183 

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

185 IncNext = PermShkValsNext * TranShkValsNext 

186 WorstIncNext = PermShkMinNext * TranShkMinNext 

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

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

189 

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

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

192 vPfuncNext = solution_next.vPfunc 

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

194 

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

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

197 try: 

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

199 except: 

200 MPCminNow = 0.0 

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

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

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

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

205 

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

207 PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rfree 

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

209 

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

211 # and artificial borrowing constraints 

212 if BoroCnstArt is None: 

213 mNrmMinNow = BoroCnstNat 

214 else: 

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

216 

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

218 # or artificial borrowing constraint actually binds 

219 if BoroCnstNat < mNrmMinNow: 

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

221 else: 

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

223 

224 # Define the borrowing-constrained consumption function 

225 cFuncNowCnst = LinearInterp( 

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

227 ) 

228 

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

230 aNrmNow = np.asarray(aXtraGrid) + BoroCnstNat 

231 

232 # Define local functions for taking future expectations 

233 def calc_mNrmNext(S, a, R): 

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

235 

236 def calc_vNext(S, a, R): 

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

238 calc_mNrmNext(S, a, R) 

239 ) 

240 

241 def calc_vPnext(S, a, R): 

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

243 

244 def calc_vPPnext(S, a, R): 

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

246 

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

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

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

250 

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

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

253 PrefShkCount = PrefShkVals.size 

254 PrefShk_temp = np.tile( 

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

256 (1, cNrm_base.size), 

257 ) 

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

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

260 # These are the endogenous gridpoints, as usual 

261 

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

263 m_for_interpolation = np.concatenate( 

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

265 ) 

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

267 

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

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

270 if CubicBool: 

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

272 raise ( 

273 ValueError, 

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

275 ) 

276 

277 # Make the preference-shock specific consumption functions 

278 cFuncs_by_PrefShk = [] 

279 for j in range(PrefShkCount): 

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

281 cFunc_this_shk = LowerEnvelope( 

282 LinearInterp( 

283 m_for_interpolation[j, :], 

284 c_for_interpolation[j, :], 

285 intercept_limit=hNrmNow * MPCmin_j, 

286 slope_limit=MPCmin_j, 

287 ), 

288 cFuncNowCnst, 

289 ) 

290 cFuncs_by_PrefShk.append(cFunc_this_shk) 

291 

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

293 cFuncNow = LinearInterpOnInterp1D(cFuncs_by_PrefShk, PrefShkVals) 

294 

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

296 m_grid = aXtraGrid + mNrmMinNow 

297 vP_vec = np.zeros_like(m_grid) 

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

299 vP_vec += ( 

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

301 ) 

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

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

304 

305 # Define this period's marginal marginal value function 

306 if CubicBool: 

307 pass # This is impossible to reach right now 

308 else: 

309 vPPfuncNow = NullFunc() # Dummy object 

310 

311 # Construct this period's value function if requested 

312 if vFuncBool: 

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

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

315 EndOfPrdvNvrs = uFunc.inv( 

316 EndOfPrdv 

317 ) # value transformed through inverse utility 

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

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

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

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

322 

323 # Construct the end-of-period value function 

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

325 EndOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, EndOfPrdvNvrs, EndOfPrdvNvrsP) 

326 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA) 

327 

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

329 # accounting for all of the discrete preference shocks 

330 mNrm_temp = mNrmMinNow + aXtraGrid 

331 v_temp = np.zeros_like(mNrm_temp) 

332 vP_temp = np.zeros_like(mNrm_temp) 

333 for j in range(PrefShkCount): 

334 this_shock = PrefShkVals[j] 

335 this_prob = PrefShkPrbs[j] 

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

337 aNrm_temp = mNrm_temp - cNrm_temp 

338 v_temp += this_prob * ( 

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

340 ) 

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

342 

343 # Construct the beginning-of-period value function 

344 # value transformed through inverse utility 

345 vNvrs_temp = uFunc.inv(v_temp) 

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

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

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

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

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

351 vNvrsFuncNow = CubicInterp( 

352 mNrm_temp, vNvrs_temp, vNvrsP_temp, MPCminNvrs * hNrmNow, MPCminNvrs 

353 ) 

354 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA) 

355 

356 else: 

357 vFuncNow = NullFunc() # Dummy object 

358 

359 # Create and return this period's solution 

360 solution_now = ConsumerSolution( 

361 cFunc=cFuncNow, 

362 vFunc=vFuncNow, 

363 vPfunc=vPfuncNow, 

364 vPPfunc=vPPfuncNow, 

365 mNrmMin=mNrmMinNow, 

366 hNrm=hNrmNow, 

367 MPCmin=MPCminNow, 

368 MPCmax=MPCmaxEff, 

369 ) 

370 return solution_now 

371 

372 

373############################################################################### 

374 

375 

376def solve_one_period_ConsKinkyPref( 

377 solution_next, 

378 IncShkDstn, 

379 PrefShkDstn, 

380 LivPrb, 

381 DiscFac, 

382 CRRA, 

383 Rboro, 

384 Rsave, 

385 PermGroFac, 

386 BoroCnstArt, 

387 aXtraGrid, 

388 vFuncBool, 

389 CubicBool, 

390): 

391 """ 

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

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

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

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

396 plicative shifter to their marginal utility of consumption. 

397 

398 Parameters 

399 ---------- 

400 solution_next : ConsumerSolution 

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

402 IncShkDstn : distribution.Distribution 

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

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

405 PrefShkDstn : distribution.Distribution 

406 Discrete distribution of the multiplicative utility shifter. Order: 

407 probabilities, preference shocks. 

408 LivPrb : float 

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

410 the succeeding period. 

411 DiscFac : float 

412 Intertemporal discount factor for future utility. 

413 CRRA : float 

414 Coefficient of relative risk aversion. 

415 Rboro: float 

416 Interest factor on assets between this period and the succeeding 

417 period when assets are negative. 

418 Rsave: float 

419 Interest factor on assets between this period and the succeeding 

420 period when assets are positive. 

421 PermGroFac : float 

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

423 BoroCnstArt: float or None 

424 Borrowing constraint for the minimum allowable assets to end the 

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

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

427 rowing constraint. 

428 aXtraGrid: np.array 

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

430 absolute minimum acceptable level. 

431 vFuncBool: boolean 

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

433 included in the reported solution. 

434 CubicBool: boolean 

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

436 polation. 

437 

438 Returns 

439 ------- 

440 solution_now : ConsumerSolution 

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

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

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

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

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

446 function is defined over normalized market resources and the preference 

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

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

449 """ 

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

451 assert Rboro >= Rsave, ( 

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

453 ) 

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

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

456 if Rboro == Rsave: 

457 solution_now = solve_one_period_ConsPrefShock( 

458 solution_next, 

459 IncShkDstn, 

460 PrefShkDstn, 

461 LivPrb, 

462 DiscFac, 

463 CRRA, 

464 Rboro, 

465 PermGroFac, 

466 BoroCnstArt, 

467 aXtraGrid, 

468 vFuncBool, 

469 CubicBool, 

470 ) 

471 return solution_now 

472 

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

474 uFunc = UtilityFuncCRRA(CRRA) 

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

476 

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

478 ShkPrbsNext = IncShkDstn.pmv 

479 PermShkValsNext = IncShkDstn.atoms[0] 

480 TranShkValsNext = IncShkDstn.atoms[1] 

481 PermShkMinNext = np.min(PermShkValsNext) 

482 TranShkMinNext = np.min(TranShkValsNext) 

483 PrefShkPrbs = PrefShkDstn.pmv 

484 PrefShkVals = PrefShkDstn.atoms.flatten() 

485 

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

487 IncNext = PermShkValsNext * TranShkValsNext 

488 WorstIncNext = PermShkMinNext * TranShkMinNext 

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

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

491 

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

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

494 vPfuncNext = solution_next.vPfunc 

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

496 

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

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

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

500 try: 

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

502 except: 

503 MPCminNow = 0.0 

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

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

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

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

508 

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

510 PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rboro 

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

512 

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

514 # and artificial borrowing constraints 

515 if BoroCnstArt is None: 

516 mNrmMinNow = BoroCnstNat 

517 else: 

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

519 

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

521 # or artificial borrowing constraint actually binds 

522 if BoroCnstNat < mNrmMinNow: 

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

524 else: 

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

526 

527 # Define the borrowing-constrained consumption function 

528 cFuncNowCnst = LinearInterp( 

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

530 ) 

531 

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

533 aNrmNow = np.sort( 

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

535 ) 

536 

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

538 Rfree = Rsave * np.ones_like(aNrmNow) 

539 Rfree[aNrmNow < 0] = Rboro 

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

541 Rfree[i_kink] = Rboro 

542 

543 # Define local functions for taking future expectations 

544 def calc_mNrmNext(S, a, R): 

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

546 

547 def calc_vNext(S, a, R): 

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

549 calc_mNrmNext(S, a, R) 

550 ) 

551 

552 def calc_vPnext(S, a, R): 

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

554 

555 def calc_vPPnext(S, a, R): 

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

557 

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

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

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

561 

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

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

564 PrefShkCount = PrefShkVals.size 

565 PrefShk_temp = np.tile( 

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

567 (1, cNrm_base.size), 

568 ) 

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

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

571 # These are the endogenous gridpoints, as usual 

572 

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

574 m_for_interpolation = np.concatenate( 

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

576 ) 

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

578 

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

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

581 if CubicBool: 

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

583 raise ( 

584 ValueError, 

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

586 ) 

587 

588 # Make the preference-shock specific consumption functions 

589 cFuncs_by_PrefShk = [] 

590 for j in range(PrefShkCount): 

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

592 cFunc_this_shk = LowerEnvelope( 

593 LinearInterp( 

594 m_for_interpolation[j, :], 

595 c_for_interpolation[j, :], 

596 intercept_limit=hNrmNow * MPCmin_j, 

597 slope_limit=MPCmin_j, 

598 ), 

599 cFuncNowCnst, 

600 ) 

601 cFuncs_by_PrefShk.append(cFunc_this_shk) 

602 

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

604 cFuncNow = LinearInterpOnInterp1D(cFuncs_by_PrefShk, PrefShkVals) 

605 

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

607 m_grid = aXtraGrid + mNrmMinNow 

608 vP_vec = np.zeros_like(m_grid) 

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

610 vP_vec += ( 

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

612 ) 

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

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

615 

616 # Define this period's marginal marginal value function 

617 if CubicBool: 

618 pass # This is impossible to reach right now 

619 else: 

620 vPPfuncNow = NullFunc() # Dummy object 

621 

622 # Construct this period's value function if requested 

623 if vFuncBool: 

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

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

626 EndOfPrdvNvrs = uFunc.inv( 

627 EndOfPrdv 

628 ) # value transformed through inverse utility 

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

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

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

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

633 

634 # Construct the end-of-period value function 

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

636 EndOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, EndOfPrdvNvrs, EndOfPrdvNvrsP) 

637 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA) 

638 

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

640 # accounting for all of the discrete preference shocks 

641 mNrm_temp = mNrmMinNow + aXtraGrid 

642 v_temp = np.zeros_like(mNrm_temp) 

643 vP_temp = np.zeros_like(mNrm_temp) 

644 for j in range(PrefShkCount): 

645 this_shock = PrefShkVals[j] 

646 this_prob = PrefShkPrbs[j] 

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

648 aNrm_temp = mNrm_temp - cNrm_temp 

649 v_temp += this_prob * ( 

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

651 ) 

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

653 

654 # Construct the beginning-of-period value function 

655 # value transformed through inverse utility 

656 vNvrs_temp = uFunc.inv(v_temp) 

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

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

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

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

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

662 vNvrsFuncNow = CubicInterp( 

663 mNrm_temp, vNvrs_temp, vNvrsP_temp, MPCminNvrs * hNrmNow, MPCminNvrs 

664 ) 

665 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA) 

666 

667 else: 

668 vFuncNow = NullFunc() # Dummy object 

669 

670 # Create and return this period's solution 

671 solution_now = ConsumerSolution( 

672 cFunc=cFuncNow, 

673 vFunc=vFuncNow, 

674 vPfunc=vPfuncNow, 

675 vPPfunc=vPPfuncNow, 

676 mNrmMin=mNrmMinNow, 

677 hNrm=hNrmNow, 

678 MPCmin=MPCminNow, 

679 MPCmax=MPCmaxEff, 

680 ) 

681 return solution_now 

682 

683 

684############################################################################### 

685 

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

687PrefShockConsumerType_constructors_default = { 

688 "IncShkDstn": construct_lognormal_income_process_unemployment, 

689 "PermShkDstn": get_PermShkDstn_from_IncShkDstn, 

690 "TranShkDstn": get_TranShkDstn_from_IncShkDstn, 

691 "aXtraGrid": make_assets_grid, 

692 "PrefShkDstn": make_lognormal_PrefShkDstn, 

693 "solution_terminal": make_basic_CRRA_solution_terminal, 

694 "kNrmInitDstn": make_lognormal_kNrm_init_dstn, 

695 "pLvlInitDstn": make_lognormal_pLvl_init_dstn, 

696} 

697 

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

699PrefShockConsumerType_kNrmInitDstn_default = { 

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

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

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

703} 

704 

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

706PrefShockConsumerType_pLvlInitDstn_default = { 

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

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

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

710} 

711 

712# Default parameters to make IncShkDstn using construct_lognormal_income_process_unemployment 

713PrefShockConsumerType_IncShkDstn_default = { 

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

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

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

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

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

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

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

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

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

723} 

724 

725# Default parameters to make aXtraGrid using construct_assets_grid 

726 

727PrefShockConsumerType_aXtraGrid_default = { 

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

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

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

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

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

733} 

734 

735# Default parameters to make PrefShkDstn using make_lognormal_PrefShkDstn 

736 

737PrefShockConsumerType_PrefShkDstn_default = { 

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

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

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

741} 

742 

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

744PrefShockConsumerType_solving_default = { 

745 # BASIC HARK PARAMETERS REQUIRED TO SOLVE THE MODEL 

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

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

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

749 "constructors": PrefShockConsumerType_constructors_default, # See dictionary above 

750 # PRIMITIVE RAW PARAMETERS REQUIRED TO SOLVE THE MODEL 

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

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

753 "DiscFac": 0.96, # Intertemporal discount factor 

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

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

756 "BoroCnstArt": 0.0, # Artificial borrowing constraint 

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

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

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

760} 

761PrefShockConsumerType_simulation_default = { 

762 # PARAMETERS REQUIRED TO SIMULATE THE MODEL 

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

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

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

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

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

768 # ADDITIONAL OPTIONAL PARAMETERS 

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

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

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

772} 

773 

774PrefShockConsumerType_default = {} 

775PrefShockConsumerType_default.update(PrefShockConsumerType_IncShkDstn_default) 

776PrefShockConsumerType_default.update(PrefShockConsumerType_aXtraGrid_default) 

777PrefShockConsumerType_default.update(PrefShockConsumerType_PrefShkDstn_default) 

778PrefShockConsumerType_default.update(PrefShockConsumerType_kNrmInitDstn_default) 

779PrefShockConsumerType_default.update(PrefShockConsumerType_pLvlInitDstn_default) 

780PrefShockConsumerType_default.update(PrefShockConsumerType_solving_default) 

781PrefShockConsumerType_default.update(PrefShockConsumerType_simulation_default) 

782init_preference_shocks = ( 

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

784) 

785 

786 

787class PrefShockConsumerType(IndShockConsumerType): 

788 r""" 

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

790 

791 .. math:: 

792 \newcommand{\CRRA}{\rho} 

793 \newcommand{\DiePrb}{\mathsf{D}} 

794 \newcommand{\PermGroFac}{\Gamma} 

795 \newcommand{\Rfree}{\mathsf{R}} 

796 \newcommand{\DiscFac}{\beta} 

797 \begin{align*} 

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

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

800 a_t &= m_t - c_t, \\ 

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

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

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

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

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

806 \end{align*} 

807 

808 

809 Constructors 

810 ------------ 

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

812 The agent's income shock distributions. 

813 

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

815 aXtraGrid: Constructor 

816 The agent's asset grid. 

817 

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

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

820 The agent's preference shock distributions. 

821 

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

823 

824 Solving Parameters 

825 ------------------ 

826 cycles: int 

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

828 T_cycle: int 

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

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

831 Coefficient of Relative Risk Aversion. 

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

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

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

835 Intertemporal discount factor. 

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

837 Survival probability after each period. 

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

839 Permanent income growth factor. 

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

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

842 vFuncBool: bool 

843 Whether to calculate the value function during solution. 

844 CubicBool: bool 

845 Whether to use cubic spline interpoliation. 

846 

847 Simulation Parameters 

848 --------------------- 

849 AgentCount: int 

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

851 T_age: int 

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

853 T_sim: int, required for simulation 

854 Number of periods to simulate. 

855 track_vars: list[strings] 

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

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

858 

859 PermShk is the agent's permanent income shock 

860 

861 PrefShk is the agent's preference shock 

862 

863 TranShk is the agent's transitory income shock 

864 

865 aLvl is the nominal asset level 

866 

867 aNrm is the normalized assets 

868 

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

870 

871 cNrm is the normalized consumption 

872 

873 mNrm is the normalized market resources 

874 

875 pLvl is the permanent income level 

876 

877 who_dies is the array of which agents died 

878 aNrmInitMean: float 

879 Mean of Log initial Normalized Assets. 

880 aNrmInitStd: float 

881 Std of Log initial Normalized Assets. 

882 pLvlInitMean: float 

883 Mean of Log initial permanent income. 

884 pLvlInitStd: float 

885 Std of Log initial permanent income. 

886 PermGroFacAgg: float 

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

888 PerfMITShk: boolean 

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

890 NewbornTransShk: boolean 

891 Whether Newborns have transitory shock. 

892 

893 Attributes 

894 ---------- 

895 solution: list[Consumer solution object] 

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

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

898 

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

900 

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

902 history: Dict[Array] 

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

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

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

906 """ 

907 

908 IncShkDstn_defaults = PrefShockConsumerType_IncShkDstn_default 

909 aXtraGrid_defaults = PrefShockConsumerType_aXtraGrid_default 

910 PrefShkDstn_defaults = PrefShockConsumerType_PrefShkDstn_default 

911 solving_defaults = PrefShockConsumerType_solving_default 

912 simulation_defaults = PrefShockConsumerType_simulation_default 

913 default_ = { 

914 "params": PrefShockConsumerType_default, 

915 "solver": solve_one_period_ConsPrefShock, 

916 "model": "ConsMarkov.yaml", 

917 } 

918 

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

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

921 distributions = [ 

922 "IncShkDstn", 

923 "PermShkDstn", 

924 "TranShkDstn", 

925 "kNrmInitDstn", 

926 "pLvlInitDstn", 

927 "PrefShkDstn", 

928 ] 

929 

930 def pre_solve(self): 

931 self.construct("solution_terminal") 

932 

933 def reset_rng(self): 

934 """ 

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

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

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

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

939 of PrefShkDstn. 

940 

941 Parameters 

942 ---------- 

943 None 

944 

945 Returns 

946 ------- 

947 None 

948 """ 

949 IndShockConsumerType.reset_rng(self) 

950 

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

952 if hasattr(self, "PrefShkDstn"): 

953 for dstn in self.PrefShkDstn: 

954 dstn.reset() 

955 

956 def get_shocks(self): 

957 """ 

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

959 

960 Parameters 

961 ---------- 

962 None 

963 

964 Returns 

965 ------- 

966 None 

967 """ 

968 IndShockConsumerType.get_shocks( 

969 self 

970 ) # Get permanent and transitory income shocks 

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

972 for t in range(self.T_cycle): 

973 these = t == self.t_cycle 

974 N = np.sum(these) 

975 if N > 0: 

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

977 self.shocks["PrefShk"] = PrefShkNow 

978 

979 def get_controls(self): 

980 """ 

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

982 

983 Parameters 

984 ---------- 

985 None 

986 

987 Returns 

988 ------- 

989 None 

990 """ 

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

992 for t in range(self.T_cycle): 

993 these = t == self.t_cycle 

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

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

996 ) 

997 self.controls["cNrm"] = cNrmNow 

998 return None 

999 

1000 def calc_bounding_values(self): 

1001 """ 

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

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

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

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

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

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

1008 

1009 NOT YET IMPLEMENTED FOR THIS CLASS 

1010 

1011 Parameters 

1012 ---------- 

1013 None 

1014 

1015 Returns 

1016 ------- 

1017 None 

1018 """ 

1019 raise NotImplementedError() 

1020 

1021 def make_euler_error_func(self, mMax=100, approx_inc_dstn=True): 

1022 """ 

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

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

1025 Stores result in attribute eulerErrorFunc as an interpolated function. 

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

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

1028 

1029 NOT YET IMPLEMENTED FOR THIS CLASS 

1030 

1031 Parameters 

1032 ---------- 

1033 mMax : float 

1034 Maximum normalized market resources for the Euler error function. 

1035 approx_inc_dstn : Boolean 

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

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

1038 discrete approximation instead. When True, uses approximation in 

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

1040 

1041 Returns 

1042 ------- 

1043 None 

1044 

1045 Notes 

1046 ----- 

1047 This method is not used by any other code in the library. Rather, it is here 

1048 for expository and benchmarking purposes. 

1049 """ 

1050 raise NotImplementedError() 

1051 

1052 

1053############################################################################### 

1054 

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

1056kinky_pref_different_params = { 

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

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

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

1060} 

1061KinkyPrefConsumerType_constructors_default = ( 

1062 PrefShockConsumerType_constructors_default.copy() 

1063) 

1064KinkyPrefConsumerType_IncShkDstn_default = ( 

1065 PrefShockConsumerType_IncShkDstn_default.copy() 

1066) 

1067KinkyPrefConsumerType_pLvlInitDstn_default = ( 

1068 PrefShockConsumerType_pLvlInitDstn_default.copy() 

1069) 

1070KinkyPrefConsumerType_kNrmInitDstn_default = ( 

1071 PrefShockConsumerType_kNrmInitDstn_default.copy() 

1072) 

1073KinkyPrefConsumerType_aXtraGrid_default = PrefShockConsumerType_aXtraGrid_default.copy() 

1074KinkyPrefConsumerType_PrefShkDstn_default = ( 

1075 PrefShockConsumerType_PrefShkDstn_default.copy() 

1076) 

1077KinkyPrefConsumerType_solving_default = PrefShockConsumerType_solving_default.copy() 

1078KinkyPrefConsumerType_solving_default["constructors"] = ( 

1079 KinkyPrefConsumerType_constructors_default 

1080) 

1081KinkyPrefConsumerType_simulation_default = ( 

1082 PrefShockConsumerType_simulation_default.copy() 

1083) 

1084KinkyPrefConsumerType_solving_default.update(kinky_pref_different_params) 

1085 

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

1087KinkyPrefConsumerType_default = {} 

1088KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_IncShkDstn_default) 

1089KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_aXtraGrid_default) 

1090KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_PrefShkDstn_default) 

1091KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_kNrmInitDstn_default) 

1092KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_pLvlInitDstn_default) 

1093KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_solving_default) 

1094KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_simulation_default) 

1095init_kinky_pref = KinkyPrefConsumerType_default 

1096 

1097 

1098class KinkyPrefConsumerType(PrefShockConsumerType, KinkedRconsumerType): 

1099 r""" 

1100 A consumer type based on PrefShockConsumerType, with different 

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

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

1103 

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

1105 

1106 .. math:: 

1107 \newcommand{\CRRA}{\rho} 

1108 \newcommand{\DiePrb}{\mathsf{D}} 

1109 \newcommand{\PermGroFac}{\Gamma} 

1110 \newcommand{\Rfree}{\mathsf{R}} 

1111 \newcommand{\DiscFac}{\beta} 

1112 \begin{align*} 

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

1114 a_t &= m_t - c_t, \\ 

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

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

1117 \Rfree_t &= \begin{cases} 

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

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

1120 \end{cases}\\ 

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

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

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

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

1125 \end{align*} 

1126 

1127 

1128 Constructors 

1129 ------------ 

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

1131 The agent's income shock distributions. 

1132 

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

1134 aXtraGrid: Constructor 

1135 The agent's asset grid. 

1136 

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

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

1139 The agent's preference shock distributions. 

1140 

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

1142 

1143 Solving Parameters 

1144 ------------------ 

1145 cycles: int 

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

1147 T_cycle: int 

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

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

1150 Coefficient of Relative Risk Aversion. 

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

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

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

1154 Risk Free interest rate when assets are negative. 

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

1156 Risk Free interest rate when assets are positive. 

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

1158 Intertemporal discount factor. 

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

1160 Survival probability after each period. 

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

1162 Permanent income growth factor. 

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

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

1165 vFuncBool: bool 

1166 Whether to calculate the value function during solution. 

1167 CubicBool: bool 

1168 Whether to use cubic spline interpoliation. 

1169 

1170 Simulation Parameters 

1171 --------------------- 

1172 AgentCount: int 

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

1174 T_age: int 

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

1176 T_sim: int, required for simulation 

1177 Number of periods to simulate. 

1178 track_vars: list[strings] 

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

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

1181 

1182 PermShk is the agent's permanent income shock 

1183 

1184 PrefShk is the agent's preference shock 

1185 

1186 TranShk is the agent's transitory income shock 

1187 

1188 aLvl is the nominal asset level 

1189 

1190 aNrm is the normalized assets 

1191 

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

1193 

1194 cNrm is the normalized consumption 

1195 

1196 mNrm is the normalized market resources 

1197 

1198 pLvl is the permanent income level 

1199 

1200 who_dies is the array of which agents died 

1201 aNrmInitMean: float 

1202 Mean of Log initial Normalized Assets. 

1203 aNrmInitStd: float 

1204 Std of Log initial Normalized Assets. 

1205 pLvlInitMean: float 

1206 Mean of Log initial permanent income. 

1207 pLvlInitStd: float 

1208 Std of Log initial permanent income. 

1209 PermGroFacAgg: float 

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

1211 PerfMITShk: boolean 

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

1213 NewbornTransShk: boolean 

1214 Whether Newborns have transitory shock. 

1215 

1216 Attributes 

1217 ---------- 

1218 solution: list[Consumer solution object] 

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

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

1221 

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

1223 

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

1225 history: Dict[Array] 

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

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

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

1229 """ 

1230 

1231 IncShkDstn_defaults = KinkyPrefConsumerType_IncShkDstn_default 

1232 aXtraGrid_defaults = KinkyPrefConsumerType_aXtraGrid_default 

1233 PrefShkDstn_defaults = KinkyPrefConsumerType_PrefShkDstn_default 

1234 solving_defaults = KinkyPrefConsumerType_solving_default 

1235 simulation_defaults = KinkyPrefConsumerType_simulation_default 

1236 default_ = { 

1237 "params": KinkyPrefConsumerType_default, 

1238 "solver": solve_one_period_ConsKinkyPref, 

1239 } 

1240 

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

1242 distributions = [ 

1243 "IncShkDstn", 

1244 "PermShkDstn", 

1245 "TranShkDstn", 

1246 "kNrmInitDstn", 

1247 "pLvlInitDstn", 

1248 "PrefShkDstn", 

1249 ] 

1250 

1251 def pre_solve(self): 

1252 self.construct("solution_terminal") 

1253 

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

1255 return KinkedRconsumerType.get_Rfree(self)