Coverage for HARK / ConsumptionSaving / TractableBufferStockModel.py: 99%

160 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-07 05:16 +0000

1""" 

2Defines and solves the Tractable Buffer Stock model described in lecture notes 

3for "A Tractable Model of Buffer Stock Saving" (henceforth, TBS) available at 

4https://www.econ2.jhu.edu/people/ccarroll/public/lecturenotes/consumption/TractableBufferStock 

5The model concerns an agent with constant relative risk aversion utility making 

6decisions over consumption and saving. He is subject to only a very particular 

7sort of risk: the possibility that he will become permanently unemployed until 

8the day he dies; barring this, his income is certain and grows at a constant rate. 

9 

10The model has an infinite horizon, but is not solved by backward iteration in a 

11traditional sense. Because of the very specific assumptions about risk, it is 

12possible to find the agent's steady state or target level of market resources 

13when employed, as well as information about the optimal consumption rule at this 

14target level. The full consumption function can then be constructed by "back- 

15shooting", inverting the Euler equation to find what consumption *must have been* 

16in the previous period. The consumption function is thus constructed by repeat- 

17edly adding "stable arm" points to either end of a growing list until specified 

18bounds are exceeded. 

19 

20Despite the non-standard solution method, the iterative process can be embedded 

21in the HARK framework, as shown below. 

22""" 

23 

24from copy import copy 

25 

26import numpy as np 

27from scipy.optimize import brentq, newton 

28 

29from HARK import AgentType, NullFunc 

30from HARK.distributions import Bernoulli, Lognormal 

31from HARK.interpolation import LinearInterp, CubicInterp 

32 

33# Import the HARK library. 

34from HARK.metric import MetricObject 

35from HARK.rewards import ( 

36 CRRAutility, 

37 CRRAutility_inv, 

38 CRRAutility_invP, 

39 CRRAutilityP, 

40 CRRAutilityP_inv, 

41 CRRAutilityPP, 

42 CRRAutilityPPP, 

43 CRRAutilityPPPP, 

44) 

45 

46__all__ = ["TractableConsumerSolution", "TractableConsumerType"] 

47 

48# If you want to run the "tractable" version of cstwMPC, use cstwMPCagent from 

49# cstwMPC REMARK and have TractableConsumerType inherit from cstwMPCagent rather than AgentType 

50 

51# Define utility function and its derivatives (plus inverses) 

52utility = CRRAutility 

53utilityP = CRRAutilityP 

54utilityPP = CRRAutilityPP 

55utilityPPP = CRRAutilityPPP 

56utilityPPPP = CRRAutilityPPPP 

57utilityP_inv = CRRAutilityP_inv 

58utility_invP = CRRAutility_invP 

59utility_inv = CRRAutility_inv 

60 

61 

62class TractableConsumerSolution(MetricObject): 

63 """ 

64 A class representing the solution to a tractable buffer saving problem. 

65 Attributes include a list of money points mNrm_list, a list of consumption points 

66 cNrm_list, a list of MPCs MPC_list, a perfect foresight consumption function 

67 while employed, and a perfect foresight consumption function while unemployed. 

68 The solution includes a consumption function constructed from the lists. 

69 

70 Parameters 

71 ---------- 

72 mNrm_list : [float] 

73 List of normalized market resources points on the stable arm. 

74 cNrm_list : [float] 

75 List of normalized consumption points on the stable arm. 

76 MPC_list : [float] 

77 List of marginal propensities to consume on the stable arm, corres- 

78 ponding to the (mNrm,cNrm) points. 

79 cFunc_U : function 

80 The (linear) consumption function when permanently unemployed. 

81 cFunc : function 

82 The consumption function when employed. 

83 """ 

84 

85 def __init__( 

86 self, 

87 mNrm_list=None, 

88 cNrm_list=None, 

89 MPC_list=None, 

90 cFunc_U=NullFunc, 

91 cFunc=NullFunc, 

92 ): 

93 self.mNrm_list = mNrm_list if mNrm_list is not None else list() 

94 self.cNrm_list = cNrm_list if cNrm_list is not None else list() 

95 self.MPC_list = MPC_list if MPC_list is not None else list() 

96 self.cFunc_U = cFunc_U 

97 self.cFunc = cFunc 

98 self.distance_criteria = ["PointCount"] 

99 # The distance between two solutions is the difference in the number of 

100 # stable arm points in each. This is a very crude measure of distance 

101 # that captures the notion that the process is over when no points are added. 

102 

103 

104def find_next_point( 

105 DiscFac, 

106 Rfree, 

107 CRRA, 

108 PermGroFacCmp, 

109 UnempPrb, 

110 Rnrm, 

111 Beth, 

112 cNext, 

113 mNext, 

114 MPCnext, 

115 PFMPC, 

116): 

117 """ 

118 Calculates what consumption, market resources, and the marginal propensity 

119 to consume must have been in the previous period given model parameters and 

120 values of market resources, consumption, and MPC today. 

121 

122 Parameters 

123 ---------- 

124 DiscFac : float 

125 Intertemporal discount factor on future utility. 

126 Rfree : float 

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

128 PermGroFacCmp : float 

129 Permanent income growth factor, compensated for the possibility of 

130 permanent unemployment. 

131 UnempPrb : float 

132 Probability of becoming permanently unemployed. 

133 Rnrm : float 

134 Interest factor normalized by compensated permanent income growth factor. 

135 Beth : float 

136 Composite effective discount factor for reverse shooting solution; defined 

137 in appendix "Numerical Solution/The Consumption Function" in TBS 

138 lecture notes 

139 cNext : float 

140 Normalized consumption in the succeeding period. 

141 mNext : float 

142 Normalized market resources in the succeeding period. 

143 MPCnext : float 

144 The marginal propensity to consume in the succeeding period. 

145 PFMPC : float 

146 The perfect foresight MPC; also the MPC when permanently unemployed. 

147 

148 Returns 

149 ------- 

150 mNow : float 

151 Normalized market resources this period. 

152 cNow : float 

153 Normalized consumption this period. 

154 MPCnow : float 

155 Marginal propensity to consume this period. 

156 """ 

157 

158 def uPP(x): 

159 return utilityPP(x, rho=CRRA) 

160 

161 cNow = ( 

162 PermGroFacCmp 

163 * (DiscFac * Rfree) ** (-1.0 / CRRA) 

164 * cNext 

165 * (1 + UnempPrb * ((cNext / (PFMPC * (mNext - 1.0))) ** CRRA - 1.0)) 

166 ** (-1.0 / CRRA) 

167 ) 

168 mNow = (PermGroFacCmp / Rfree) * (mNext - 1.0) + cNow 

169 cUNext = PFMPC * (mNow - cNow) * Rnrm 

170 # See TBS Appendix "E.1 The Consumption Function" 

171 natural = ( 

172 Beth 

173 * Rnrm 

174 * (1.0 / uPP(cNow)) 

175 * ((1.0 - UnempPrb) * uPP(cNext) * MPCnext + UnempPrb * uPP(cUNext) * PFMPC) 

176 ) # Convenience variable 

177 MPCnow = natural / (natural + 1) 

178 return mNow, cNow, MPCnow 

179 

180 

181def add_to_stable_arm_points( 

182 solution_next, 

183 DiscFac, 

184 Rfree, 

185 CRRA, 

186 PermGroFacCmp, 

187 UnempPrb, 

188 PFMPC, 

189 Rnrm, 

190 Beth, 

191 mLowerBnd, 

192 mUpperBnd, 

193): 

194 """ 

195 Adds a one point to the bottom and top of the list of stable arm points if 

196 the bounding levels of mLowerBnd (lower) and mUpperBnd (upper) have not yet 

197 been met by a stable arm point in mNrm_list. This acts as the "one period 

198 solver" / solve_one_period in the tractable buffer stock model. 

199 

200 Parameters 

201 ---------- 

202 solution_next : TractableConsumerSolution 

203 The solution object from the previous iteration of the backshooting 

204 procedure. Not the "next period" solution per se. 

205 DiscFac : float 

206 Intertemporal discount factor on future utility. 

207 Rfree : float 

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

209 CRRA : float 

210 Coefficient of relative risk aversion. 

211 PermGroFacCmp : float 

212 Permanent income growth factor, compensated for the possibility of 

213 permanent unemployment. 

214 UnempPrb : float 

215 Probability of becoming permanently unemployed. 

216 PFMPC : float 

217 The perfect foresight MPC; also the MPC when permanently unemployed. 

218 Rnrm : float 

219 Interest factor normalized by compensated permanent income growth factor. 

220 Beth : float 

221 Damned if I know. 

222 mLowerBnd : float 

223 Lower bound on market resources for the backshooting process. If 

224 min(solution_next.mNrm_list) < mLowerBnd, no new bottom point is found. 

225 mUpperBnd : float 

226 Upper bound on market resources for the backshooting process. If 

227 max(solution_next.mNrm_list) > mUpperBnd, no new top point is found. 

228 

229 Returns: 

230 --------- 

231 solution_now : TractableConsumerSolution 

232 A new solution object with new points added to the top and bottom. If 

233 no new points were added, then the backshooting process is about to end. 

234 """ 

235 # Unpack the lists of Euler points 

236 mNrm_list = copy(solution_next.mNrm_list) 

237 cNrm_list = copy(solution_next.cNrm_list) 

238 MPC_list = copy(solution_next.MPC_list) 

239 

240 # Check whether to add a stable arm point to the top 

241 mNext = mNrm_list[-1] 

242 if mNext < mUpperBnd: 

243 # Get the rest of the data for the previous top point 

244 cNext = solution_next.cNrm_list[-1] 

245 MPCNext = solution_next.MPC_list[-1] 

246 

247 # Calculate employed levels of c, m, and MPC from next period's values 

248 mNow, cNow, MPCnow = find_next_point( 

249 DiscFac, 

250 Rfree, 

251 CRRA, 

252 PermGroFacCmp, 

253 UnempPrb, 

254 Rnrm, 

255 Beth, 

256 cNext, 

257 mNext, 

258 MPCNext, 

259 PFMPC, 

260 ) 

261 

262 # Add this point to the top of the stable arm list 

263 mNrm_list.append(mNow) 

264 cNrm_list.append(cNow) 

265 MPC_list.append(MPCnow) 

266 

267 # Check whether to add a stable arm point to the bottom 

268 mNext = mNrm_list[0] 

269 if mNext > mLowerBnd: 

270 # Get the rest of the data for the previous bottom point 

271 cNext = solution_next.cNrm_list[0] 

272 MPCNext = solution_next.MPC_list[0] 

273 

274 # Calculate employed levels of c, m, and MPC from next period's values 

275 mNow, cNow, MPCnow = find_next_point( 

276 DiscFac, 

277 Rfree, 

278 CRRA, 

279 PermGroFacCmp, 

280 UnempPrb, 

281 Rnrm, 

282 Beth, 

283 cNext, 

284 mNext, 

285 MPCNext, 

286 PFMPC, 

287 ) 

288 

289 # Add this point to the top of the stable arm list 

290 mNrm_list.insert(0, mNow) 

291 cNrm_list.insert(0, cNow) 

292 MPC_list.insert(0, MPCnow) 

293 

294 # Construct and return this period's solution 

295 solution_now = TractableConsumerSolution( 

296 mNrm_list=mNrm_list, cNrm_list=cNrm_list, MPC_list=MPC_list 

297 ) 

298 solution_now.PointCount = len(mNrm_list) 

299 return solution_now 

300 

301 

302############################################################################### 

303 

304# Define a dictionary for the tractable buffer stock model 

305init_tractable = { 

306 "cycles": 0, # infinite horizon 

307 "T_cycle": 1, # only one period repeated indefinitely 

308 "UnempPrb": 0.00625, # Probability of becoming permanently unemployed 

309 "DiscFac": 0.975, # Intertemporal discount factor 

310 "Rfree": 1.01, # Risk-free interest factor on assets 

311 "PermGroFac": 1.0025, # Permanent income growth factor (uncompensated) 

312 "CRRA": 1.0, # Coefficient of relative risk aversion 

313 "kLogInitMean": -3.0, # Mean of initial log normalized assets 

314 "kLogInitStd": 0.0, # Standard deviation of initial log normalized assets 

315} 

316 

317 

318class TractableConsumerType(AgentType): 

319 """ 

320 Parameters 

321 ---------- 

322 Same as AgentType 

323 """ 

324 

325 time_inv_ = [ 

326 "DiscFac", 

327 "Rfree", 

328 "CRRA", 

329 "PermGroFacCmp", 

330 "UnempPrb", 

331 "PFMPC", 

332 "Rnrm", 

333 "Beth", 

334 "mLowerBnd", 

335 "mUpperBnd", 

336 ] 

337 shock_vars_ = ["eState"] 

338 state_vars = ["bNrm", "mNrm", "aNrm"] 

339 poststate_vars = ["aNrm", "eState"] # For simulation 

340 default_ = {"params": init_tractable, "solver": add_to_stable_arm_points} 

341 

342 def pre_solve(self): 

343 """ 

344 Calculates all of the solution objects that can be obtained before con- 

345 ducting the backshooting routine, including the target levels, the per- 

346 fect foresight solution, (marginal) consumption at m=0, and the small 

347 perturbations around the steady state. 

348 

349 TODO: This should probably all be moved to a constructor function. 

350 

351 Parameters 

352 ---------- 

353 none 

354 

355 Returns 

356 ------- 

357 none 

358 """ 

359 CRRA = self.CRRA 

360 UnempPrb = self.UnempPrb 

361 DiscFac = self.DiscFac 

362 PermGroFac = self.PermGroFac 

363 Rfree = self.Rfree 

364 

365 # Define utility functions 

366 def uPP(x): 

367 return utilityPP(x, rho=CRRA) 

368 

369 def uPPP(x): 

370 return utilityPPP(x, rho=CRRA) 

371 

372 def uPPPP(x): 

373 return utilityPPPP(x, rho=CRRA) 

374 

375 # Define some useful constants from model primitives 

376 PermGroFacCmp = PermGroFac / ( 

377 1.0 - UnempPrb 

378 ) # "uncertainty compensated" wage growth factor 

379 Rnrm = ( 

380 Rfree / PermGroFacCmp 

381 ) # net interest factor (Rfree normalized by wage growth) 

382 PFMPC = 1.0 - (Rfree ** (-1.0)) * (Rfree * DiscFac) ** ( 

383 1.0 / CRRA 

384 ) # MPC for a perfect forsight consumer 

385 Beth = Rnrm * DiscFac * PermGroFacCmp ** (1.0 - CRRA) 

386 

387 # Verify that this consumer is impatient 

388 PatFacGrowth = (Rfree * DiscFac) ** (1.0 / CRRA) / PermGroFacCmp 

389 PatFacReturn = (Rfree * DiscFac) ** (1.0 / CRRA) / Rfree 

390 if PatFacReturn >= 1.0: 

391 raise Exception("Employed consumer not return impatient, cannot solve!") 

392 if PatFacGrowth >= 1.0: 

393 raise Exception("Employed consumer not growth impatient, cannot solve!") 

394 

395 # Find target money and consumption 

396 # See TBS Appendix "B.2 A Target Always Exists When Human Wealth Is Infinite" 

397 Pi = (1 + (PatFacGrowth ** (-CRRA) - 1.0) / UnempPrb) ** (1 / CRRA) 

398 h = 1.0 / (1.0 - PermGroFac / Rfree) 

399 zeta = Rnrm * PFMPC * Pi # See TBS Appendix "C The Exact Formula for target m" 

400 mTarg = 1.0 + (Rfree / (PermGroFacCmp + zeta * PermGroFacCmp - Rfree)) 

401 cTarg = (1.0 - Rnrm ** (-1.0)) * mTarg + Rnrm ** (-1.0) 

402 mTargU = (mTarg - cTarg) * Rnrm 

403 cTargU = mTargU * PFMPC 

404 SSperturbance = mTarg * 0.1 

405 

406 # Find the MPC, MMPC, and MMMPC at the target 

407 def mpcTargFixedPointFunc(k): 

408 return k * uPP(cTarg) - Beth * ( 

409 (1.0 - UnempPrb) * (1.0 - k) * k * Rnrm * uPP(cTarg) 

410 + PFMPC * UnempPrb * (1.0 - k) * Rnrm * uPP(cTargU) 

411 ) 

412 

413 MPCtarg = newton(mpcTargFixedPointFunc, 0) 

414 

415 def mmpcTargFixedPointFunc(kk): 

416 return ( 

417 kk * uPP(cTarg) 

418 + MPCtarg**2.0 * uPPP(cTarg) 

419 - Beth 

420 * ( 

421 -(1.0 - UnempPrb) * MPCtarg * kk * Rnrm * uPP(cTarg) 

422 + (1.0 - UnempPrb) 

423 * (1.0 - MPCtarg) ** 2.0 

424 * kk 

425 * Rnrm**2.0 

426 * uPP(cTarg) 

427 - PFMPC * UnempPrb * kk * Rnrm * uPP(cTargU) 

428 + (1.0 - UnempPrb) 

429 * (1.0 - MPCtarg) ** 2.0 

430 * MPCtarg**2.0 

431 * Rnrm**2.0 

432 * uPPP(cTarg) 

433 + PFMPC**2.0 

434 * UnempPrb 

435 * (1.0 - MPCtarg) ** 2.0 

436 * Rnrm**2.0 

437 * uPPP(cTargU) 

438 ) 

439 ) 

440 

441 MMPCtarg = newton(mmpcTargFixedPointFunc, 0) 

442 

443 def mmmpcTargFixedPointFunc(kkk): 

444 return ( 

445 kkk * uPP(cTarg) 

446 + 3 * MPCtarg * MMPCtarg * uPPP(cTarg) 

447 + MPCtarg**3 * uPPPP(cTarg) 

448 - Beth 

449 * ( 

450 -(1 - UnempPrb) * MPCtarg * kkk * Rnrm * uPP(cTarg) 

451 - 3 

452 * (1 - UnempPrb) 

453 * (1 - MPCtarg) 

454 * MMPCtarg**2 

455 * Rnrm**2 

456 * uPP(cTarg) 

457 + (1 - UnempPrb) * (1 - MPCtarg) ** 3 * kkk * Rnrm**3 * uPP(cTarg) 

458 - PFMPC * UnempPrb * kkk * Rnrm * uPP(cTargU) 

459 - 3 

460 * (1 - UnempPrb) 

461 * (1 - MPCtarg) 

462 * MPCtarg**2 

463 * MMPCtarg 

464 * Rnrm**2 

465 * uPPP(cTarg) 

466 + 3 

467 * (1 - UnempPrb) 

468 * (1 - MPCtarg) ** 3 

469 * MPCtarg 

470 * MMPCtarg 

471 * Rnrm**3 

472 * uPPP(cTarg) 

473 - 3 

474 * PFMPC**2 

475 * UnempPrb 

476 * (1 - MPCtarg) 

477 * MMPCtarg 

478 * Rnrm**2 

479 * uPPP(cTargU) 

480 + (1 - UnempPrb) 

481 * (1 - MPCtarg) ** 3 

482 * MPCtarg**3 

483 * Rnrm**3 

484 * uPPPP(cTarg) 

485 + PFMPC**3 * UnempPrb * (1 - MPCtarg) ** 3 * Rnrm**3 * uPPPP(cTargU) 

486 ) 

487 ) 

488 

489 MMMPCtarg = newton(mmmpcTargFixedPointFunc, 0) 

490 

491 # Find the MPC at m=0 

492 def f_temp(k): 

493 return ( 

494 Beth 

495 * Rnrm 

496 * UnempPrb 

497 * (PFMPC * Rnrm * ((1.0 - k) / k)) ** (-CRRA - 1.0) 

498 * PFMPC 

499 ) 

500 

501 def mpcAtZeroFixedPointFunc(k): 

502 return k - f_temp(k) / (1 + f_temp(k)) 

503 

504 # self.MPCmax = newton(mpcAtZeroFixedPointFunc,0.5) 

505 MPCmax = brentq( 

506 mpcAtZeroFixedPointFunc, PFMPC, 0.99, xtol=0.00000001, rtol=0.00000001 

507 ) 

508 

509 # Make the initial list of Euler points: target and perturbation to either side 

510 mNrm_list = [ 

511 mTarg - SSperturbance, 

512 mTarg, 

513 mTarg + SSperturbance, 

514 ] 

515 c_perturb_lo = ( 

516 cTarg 

517 - SSperturbance * MPCtarg 

518 + 0.5 * SSperturbance**2.0 * MMPCtarg 

519 - (1.0 / 6.0) * SSperturbance**3.0 * MMMPCtarg 

520 ) 

521 c_perturb_hi = ( 

522 cTarg 

523 + SSperturbance * MPCtarg 

524 + 0.5 * SSperturbance**2.0 * MMPCtarg 

525 + (1.0 / 6.0) * SSperturbance**3.0 * MMMPCtarg 

526 ) 

527 cNrm_list = [c_perturb_lo, cTarg, c_perturb_hi] 

528 MPC_perturb_lo = ( 

529 MPCtarg - SSperturbance * MMPCtarg + 0.5 * SSperturbance**2.0 * MMMPCtarg 

530 ) 

531 MPC_perturb_hi = ( 

532 MPCtarg + SSperturbance * MMPCtarg + 0.5 * SSperturbance**2.0 * MMMPCtarg 

533 ) 

534 MPC_list = [MPC_perturb_lo, MPCtarg, MPC_perturb_hi] 

535 

536 # Set bounds for money (stable arm construction stops when these are exceeded) 

537 mLowerBnd = 1.0 

538 mUpperBnd = 2.0 * mTarg 

539 

540 # Make the terminal period solution 

541 solution_terminal = TractableConsumerSolution( 

542 mNrm_list=mNrm_list, cNrm_list=cNrm_list, MPC_list=MPC_list 

543 ) 

544 

545 # Make two linear steady state functions 

546 cSSfunc = lambda m: m * ((Rnrm * PFMPC * Pi) / (1.0 + Rnrm * PFMPC * Pi)) 

547 mSSfunc = lambda m: (PermGroFacCmp / Rfree) + (1.0 - PermGroFacCmp / Rfree) * m 

548 

549 # Put all the parameters into self 

550 new_params = { 

551 "PermGroFacCmp": PermGroFacCmp, 

552 "Rnrm": Rnrm, 

553 "PFMPC": PFMPC, 

554 "Beth": Beth, 

555 "PatFacGrowth": PatFacGrowth, 

556 "Pi": Pi, 

557 "h": h, 

558 "zeta": zeta, 

559 "mTarg": mTarg, 

560 "cTarg": cTarg, 

561 "mTargU": mTargU, 

562 "cTargU": cTargU, 

563 "SSperturbance": SSperturbance, 

564 "MPCtarg": MPCtarg, 

565 "MMPCtarg": MMPCtarg, 

566 "MMMPCtarg": MMMPCtarg, 

567 "MPCmax": MPCmax, 

568 "mLowerBnd": mLowerBnd, 

569 "mUpperBnd": mUpperBnd, 

570 "solution_terminal": solution_terminal, 

571 "cSSfunc": cSSfunc, 

572 "mSSfunc": mSSfunc, 

573 } 

574 self.assign_parameters(**new_params) 

575 

576 def post_solve(self): 

577 """ 

578 This method adds consumption at m=0 to the list of stable arm points, 

579 then constructs the consumption function as a cubic interpolation over 

580 those points. Should be run after the backshooting routine is complete. 

581 

582 Parameters 

583 ---------- 

584 none 

585 

586 Returns 

587 ------- 

588 none 

589 """ 

590 # Add bottom point to the stable arm points 

591 self.solution[0].mNrm_list.insert(0, 0.0) 

592 self.solution[0].cNrm_list.insert(0, 0.0) 

593 self.solution[0].MPC_list.insert(0, self.MPCmax) 

594 

595 # Construct an interpolation of the consumption function from the stable arm points 

596 self.solution[0].cFunc = CubicInterp( 

597 self.solution[0].mNrm_list, 

598 self.solution[0].cNrm_list, 

599 self.solution[0].MPC_list, 

600 self.PFMPC * (self.h - 1.0), 

601 self.PFMPC, 

602 ) 

603 self.solution[0].cFunc_U = LinearInterp([0.0, 1.0], [0.0, self.PFMPC]) 

604 

605 def sim_birth(self, which_agents): 

606 """ 

607 Makes new consumers for the given indices. Initialized variables include aNrm, as 

608 well as time variables t_age and t_cycle. Normalized assets are drawn from a lognormal 

609 distributions given by aLvlInitMean and aLvlInitStd. 

610 

611 Parameters 

612 ---------- 

613 which_agents : np.array(Bool) 

614 Boolean array of size self.AgentCount indicating which agents should be "born". 

615 

616 Returns 

617 ------- 

618 None 

619 """ 

620 # Get and store states for newly born agents 

621 N = np.sum(which_agents) # Number of new consumers to make 

622 self.state_now["aNrm"][which_agents] = Lognormal( 

623 self.kLogInitMean, 

624 sigma=self.kLogInitStd, 

625 seed=self.RNG.integers(0, 2**31 - 1), 

626 ).draw(N) 

627 self.shocks["eState"] = np.zeros(self.AgentCount) # Initialize shock array 

628 # Agents are born employed 

629 self.shocks["eState"][which_agents] = 1.0 

630 # How many periods since each agent was born 

631 self.t_age[which_agents] = 0 

632 self.t_cycle[which_agents] = ( 

633 0 # Which period of the cycle each agent is currently in 

634 ) 

635 return None 

636 

637 def sim_death(self): 

638 """ 

639 Trivial function that returns boolean array of all False, as there is no death. 

640 

641 Parameters 

642 ---------- 

643 None 

644 

645 Returns 

646 ------- 

647 which_agents : np.array(bool) 

648 Boolean array of size AgentCount indicating which agents die. 

649 """ 

650 # Nobody dies in this model 

651 which_agents = np.zeros(self.AgentCount, dtype=bool) 

652 return which_agents 

653 

654 def get_shocks(self): 

655 """ 

656 Determine which agents switch from employment to unemployment. All unemployed agents remain 

657 unemployed until death. 

658 

659 Parameters 

660 ---------- 

661 None 

662 

663 Returns 

664 ------- 

665 None 

666 """ 

667 employed = self.shocks["eState"] == 1.0 

668 N = int(np.sum(employed)) 

669 newly_unemployed = Bernoulli( 

670 self.UnempPrb, seed=self.RNG.integers(0, 2**31 - 1) 

671 ).draw(N) 

672 self.shocks["eState"][employed] = 1.0 - newly_unemployed 

673 

674 def transition(self): 

675 """ 

676 Calculate market resources for all agents this period. 

677 

678 Parameters 

679 ---------- 

680 None 

681 

682 Returns 

683 ------- 

684 None 

685 """ 

686 bNrmNow = self.Rfree * self.state_prev["aNrm"] 

687 EmpNow = self.shocks["eState"] == 1.0 

688 bNrmNow[EmpNow] /= self.PermGroFacCmp 

689 mNrmNow = bNrmNow + self.shocks["eState"] 

690 

691 return bNrmNow, mNrmNow 

692 

693 def get_controls(self): 

694 """ 

695 Calculate consumption for each agent this period. 

696 

697 Parameters 

698 ---------- 

699 None 

700 

701 Returns 

702 ------- 

703 None 

704 """ 

705 employed = self.shocks["eState"] == 1.0 

706 unemployed = np.logical_not(employed) 

707 cNrmNow = np.zeros(self.AgentCount) 

708 cNrmNow[employed] = self.solution[0].cFunc(self.state_now["mNrm"][employed]) 

709 cNrmNow[unemployed] = self.solution[0].cFunc_U( 

710 self.state_now["mNrm"][unemployed] 

711 ) 

712 self.controls["cNrm"] = cNrmNow 

713 

714 def get_poststates(self): 

715 """ 

716 Calculates end-of-period assets for each consumer of this type. 

717 

718 Parameters 

719 ---------- 

720 None 

721 

722 Returns 

723 ------- 

724 None 

725 """ 

726 self.state_now["aNrm"] = self.state_now["mNrm"] - self.controls["cNrm"] 

727 return None