Coverage for HARK / ConsumptionSaving / TractableBufferStockModel.py: 99%
160 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-25 05:22 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-25 05:22 +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.
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.
20Despite the non-standard solution method, the iterative process can be embedded
21in the HARK framework, as shown below.
22"""
24from copy import copy
26import numpy as np
27from scipy.optimize import brentq, newton
29from HARK import AgentType, NullFunc
30from HARK.distributions import Bernoulli, Lognormal
31from HARK.interpolation import LinearInterp, CubicInterp
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)
46__all__ = ["TractableConsumerSolution", "TractableConsumerType"]
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
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
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.
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 """
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.
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.
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.
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 """
158 def uPP(x):
159 return utilityPP(x, rho=CRRA)
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
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.
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.
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)
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]
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 )
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)
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]
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 )
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)
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
302###############################################################################
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}
318class TractableConsumerType(AgentType):
319 """
320 Parameters
321 ----------
322 Same as AgentType
323 """
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_ = {
341 "params": init_tractable,
342 "solver": add_to_stable_arm_points,
343 "track_vars": ["mNrm", "eState", "cNrm", "aNrm"],
344 }
346 def pre_solve(self):
347 """
348 Calculates all of the solution objects that can be obtained before con-
349 ducting the backshooting routine, including the target levels, the per-
350 fect foresight solution, (marginal) consumption at m=0, and the small
351 perturbations around the steady state.
353 TODO: This should probably all be moved to a constructor function.
355 Parameters
356 ----------
357 none
359 Returns
360 -------
361 none
362 """
363 CRRA = self.CRRA
364 UnempPrb = self.UnempPrb
365 DiscFac = self.DiscFac
366 PermGroFac = self.PermGroFac
367 Rfree = self.Rfree
369 # Define utility functions
370 def uPP(x):
371 return utilityPP(x, rho=CRRA)
373 def uPPP(x):
374 return utilityPPP(x, rho=CRRA)
376 def uPPPP(x):
377 return utilityPPPP(x, rho=CRRA)
379 # Define some useful constants from model primitives
380 PermGroFacCmp = PermGroFac / (
381 1.0 - UnempPrb
382 ) # "uncertainty compensated" wage growth factor
383 Rnrm = (
384 Rfree / PermGroFacCmp
385 ) # net interest factor (Rfree normalized by wage growth)
386 PFMPC = 1.0 - (Rfree ** (-1.0)) * (Rfree * DiscFac) ** (
387 1.0 / CRRA
388 ) # MPC for a perfect forsight consumer
389 Beth = Rnrm * DiscFac * PermGroFacCmp ** (1.0 - CRRA)
391 # Verify that this consumer is impatient
392 PatFacGrowth = (Rfree * DiscFac) ** (1.0 / CRRA) / PermGroFacCmp
393 PatFacReturn = (Rfree * DiscFac) ** (1.0 / CRRA) / Rfree
394 if PatFacReturn >= 1.0:
395 raise Exception("Employed consumer not return impatient, cannot solve!")
396 if PatFacGrowth >= 1.0:
397 raise Exception("Employed consumer not growth impatient, cannot solve!")
399 # Find target money and consumption
400 # See TBS Appendix "B.2 A Target Always Exists When Human Wealth Is Infinite"
401 Pi = (1 + (PatFacGrowth ** (-CRRA) - 1.0) / UnempPrb) ** (1 / CRRA)
402 h = 1.0 / (1.0 - PermGroFac / Rfree)
403 zeta = Rnrm * PFMPC * Pi # See TBS Appendix "C The Exact Formula for target m"
404 mTarg = 1.0 + (Rfree / (PermGroFacCmp + zeta * PermGroFacCmp - Rfree))
405 cTarg = (1.0 - Rnrm ** (-1.0)) * mTarg + Rnrm ** (-1.0)
406 mTargU = (mTarg - cTarg) * Rnrm
407 cTargU = mTargU * PFMPC
408 SSperturbance = mTarg * 0.1
410 # Find the MPC, MMPC, and MMMPC at the target
411 def mpcTargFixedPointFunc(k):
412 return k * uPP(cTarg) - Beth * (
413 (1.0 - UnempPrb) * (1.0 - k) * k * Rnrm * uPP(cTarg)
414 + PFMPC * UnempPrb * (1.0 - k) * Rnrm * uPP(cTargU)
415 )
417 MPCtarg = newton(mpcTargFixedPointFunc, 0)
419 def mmpcTargFixedPointFunc(kk):
420 return (
421 kk * uPP(cTarg)
422 + MPCtarg**2.0 * uPPP(cTarg)
423 - Beth
424 * (
425 -(1.0 - UnempPrb) * MPCtarg * kk * Rnrm * uPP(cTarg)
426 + (1.0 - UnempPrb)
427 * (1.0 - MPCtarg) ** 2.0
428 * kk
429 * Rnrm**2.0
430 * uPP(cTarg)
431 - PFMPC * UnempPrb * kk * Rnrm * uPP(cTargU)
432 + (1.0 - UnempPrb)
433 * (1.0 - MPCtarg) ** 2.0
434 * MPCtarg**2.0
435 * Rnrm**2.0
436 * uPPP(cTarg)
437 + PFMPC**2.0
438 * UnempPrb
439 * (1.0 - MPCtarg) ** 2.0
440 * Rnrm**2.0
441 * uPPP(cTargU)
442 )
443 )
445 MMPCtarg = newton(mmpcTargFixedPointFunc, 0)
447 def mmmpcTargFixedPointFunc(kkk):
448 return (
449 kkk * uPP(cTarg)
450 + 3 * MPCtarg * MMPCtarg * uPPP(cTarg)
451 + MPCtarg**3 * uPPPP(cTarg)
452 - Beth
453 * (
454 -(1 - UnempPrb) * MPCtarg * kkk * Rnrm * uPP(cTarg)
455 - 3
456 * (1 - UnempPrb)
457 * (1 - MPCtarg)
458 * MMPCtarg**2
459 * Rnrm**2
460 * uPP(cTarg)
461 + (1 - UnempPrb) * (1 - MPCtarg) ** 3 * kkk * Rnrm**3 * uPP(cTarg)
462 - PFMPC * UnempPrb * kkk * Rnrm * uPP(cTargU)
463 - 3
464 * (1 - UnempPrb)
465 * (1 - MPCtarg)
466 * MPCtarg**2
467 * MMPCtarg
468 * Rnrm**2
469 * uPPP(cTarg)
470 + 3
471 * (1 - UnempPrb)
472 * (1 - MPCtarg) ** 3
473 * MPCtarg
474 * MMPCtarg
475 * Rnrm**3
476 * uPPP(cTarg)
477 - 3
478 * PFMPC**2
479 * UnempPrb
480 * (1 - MPCtarg)
481 * MMPCtarg
482 * Rnrm**2
483 * uPPP(cTargU)
484 + (1 - UnempPrb)
485 * (1 - MPCtarg) ** 3
486 * MPCtarg**3
487 * Rnrm**3
488 * uPPPP(cTarg)
489 + PFMPC**3 * UnempPrb * (1 - MPCtarg) ** 3 * Rnrm**3 * uPPPP(cTargU)
490 )
491 )
493 MMMPCtarg = newton(mmmpcTargFixedPointFunc, 0)
495 # Find the MPC at m=0
496 def f_temp(k):
497 return (
498 Beth
499 * Rnrm
500 * UnempPrb
501 * (PFMPC * Rnrm * ((1.0 - k) / k)) ** (-CRRA - 1.0)
502 * PFMPC
503 )
505 def mpcAtZeroFixedPointFunc(k):
506 return k - f_temp(k) / (1 + f_temp(k))
508 # self.MPCmax = newton(mpcAtZeroFixedPointFunc,0.5)
509 MPCmax = brentq(
510 mpcAtZeroFixedPointFunc, PFMPC, 0.99, xtol=0.00000001, rtol=0.00000001
511 )
513 # Make the initial list of Euler points: target and perturbation to either side
514 mNrm_list = [
515 mTarg - SSperturbance,
516 mTarg,
517 mTarg + SSperturbance,
518 ]
519 c_perturb_lo = (
520 cTarg
521 - SSperturbance * MPCtarg
522 + 0.5 * SSperturbance**2.0 * MMPCtarg
523 - (1.0 / 6.0) * SSperturbance**3.0 * MMMPCtarg
524 )
525 c_perturb_hi = (
526 cTarg
527 + SSperturbance * MPCtarg
528 + 0.5 * SSperturbance**2.0 * MMPCtarg
529 + (1.0 / 6.0) * SSperturbance**3.0 * MMMPCtarg
530 )
531 cNrm_list = [c_perturb_lo, cTarg, c_perturb_hi]
532 MPC_perturb_lo = (
533 MPCtarg - SSperturbance * MMPCtarg + 0.5 * SSperturbance**2.0 * MMMPCtarg
534 )
535 MPC_perturb_hi = (
536 MPCtarg + SSperturbance * MMPCtarg + 0.5 * SSperturbance**2.0 * MMMPCtarg
537 )
538 MPC_list = [MPC_perturb_lo, MPCtarg, MPC_perturb_hi]
540 # Set bounds for money (stable arm construction stops when these are exceeded)
541 mLowerBnd = 1.0
542 mUpperBnd = 2.0 * mTarg
544 # Make the terminal period solution
545 solution_terminal = TractableConsumerSolution(
546 mNrm_list=mNrm_list, cNrm_list=cNrm_list, MPC_list=MPC_list
547 )
549 # Make two linear steady state functions
550 cSSfunc = lambda m: m * ((Rnrm * PFMPC * Pi) / (1.0 + Rnrm * PFMPC * Pi))
551 mSSfunc = lambda m: (PermGroFacCmp / Rfree) + (1.0 - PermGroFacCmp / Rfree) * m
553 # Put all the parameters into self
554 new_params = {
555 "PermGroFacCmp": PermGroFacCmp,
556 "Rnrm": Rnrm,
557 "PFMPC": PFMPC,
558 "Beth": Beth,
559 "PatFacGrowth": PatFacGrowth,
560 "Pi": Pi,
561 "h": h,
562 "zeta": zeta,
563 "mTarg": mTarg,
564 "cTarg": cTarg,
565 "mTargU": mTargU,
566 "cTargU": cTargU,
567 "SSperturbance": SSperturbance,
568 "MPCtarg": MPCtarg,
569 "MMPCtarg": MMPCtarg,
570 "MMMPCtarg": MMMPCtarg,
571 "MPCmax": MPCmax,
572 "mLowerBnd": mLowerBnd,
573 "mUpperBnd": mUpperBnd,
574 "solution_terminal": solution_terminal,
575 "cSSfunc": cSSfunc,
576 "mSSfunc": mSSfunc,
577 }
578 self.assign_parameters(**new_params)
580 def post_solve(self):
581 """
582 This method adds consumption at m=0 to the list of stable arm points,
583 then constructs the consumption function as a cubic interpolation over
584 those points. Should be run after the backshooting routine is complete.
586 Parameters
587 ----------
588 none
590 Returns
591 -------
592 none
593 """
594 # Add bottom point to the stable arm points
595 self.solution[0].mNrm_list.insert(0, 0.0)
596 self.solution[0].cNrm_list.insert(0, 0.0)
597 self.solution[0].MPC_list.insert(0, self.MPCmax)
599 # Construct an interpolation of the consumption function from the stable arm points
600 self.solution[0].cFunc = CubicInterp(
601 self.solution[0].mNrm_list,
602 self.solution[0].cNrm_list,
603 self.solution[0].MPC_list,
604 self.PFMPC * (self.h - 1.0),
605 self.PFMPC,
606 )
607 self.solution[0].cFunc_U = LinearInterp([0.0, 1.0], [0.0, self.PFMPC])
609 def sim_birth(self, which_agents):
610 """
611 Makes new consumers for the given indices. Initialized variables include aNrm, as
612 well as time variables t_age and t_cycle. Normalized assets are drawn from a lognormal
613 distributions given by aLvlInitMean and aLvlInitStd.
615 Parameters
616 ----------
617 which_agents : np.array(Bool)
618 Boolean array of size self.AgentCount indicating which agents should be "born".
620 Returns
621 -------
622 None
623 """
624 # Get and store states for newly born agents
625 N = np.sum(which_agents) # Number of new consumers to make
626 self.state_now["aNrm"][which_agents] = Lognormal(
627 self.kLogInitMean,
628 sigma=self.kLogInitStd,
629 seed=self.RNG.integers(0, 2**31 - 1),
630 ).draw(N)
631 self.shocks["eState"] = np.zeros(self.AgentCount) # Initialize shock array
632 # Agents are born employed
633 self.shocks["eState"][which_agents] = 1.0
634 # How many periods since each agent was born
635 self.t_age[which_agents] = 0
636 self.t_cycle[which_agents] = (
637 0 # Which period of the cycle each agent is currently in
638 )
639 return None
641 def sim_death(self):
642 """
643 Trivial function that returns boolean array of all False, as there is no death.
645 Parameters
646 ----------
647 None
649 Returns
650 -------
651 which_agents : np.array(bool)
652 Boolean array of size AgentCount indicating which agents die.
653 """
654 # Nobody dies in this model
655 which_agents = np.zeros(self.AgentCount, dtype=bool)
656 return which_agents
658 def get_shocks(self):
659 """
660 Determine which agents switch from employment to unemployment. All unemployed agents remain
661 unemployed until death.
663 Parameters
664 ----------
665 None
667 Returns
668 -------
669 None
670 """
671 employed = self.shocks["eState"] == 1.0
672 N = int(np.sum(employed))
673 newly_unemployed = Bernoulli(
674 self.UnempPrb, seed=self.RNG.integers(0, 2**31 - 1)
675 ).draw(N)
676 self.shocks["eState"][employed] = 1.0 - newly_unemployed
678 def transition(self):
679 """
680 Calculate market resources for all agents this period.
682 Parameters
683 ----------
684 None
686 Returns
687 -------
688 None
689 """
690 bNrmNow = self.Rfree * self.state_prev["aNrm"]
691 EmpNow = self.shocks["eState"] == 1.0
692 bNrmNow[EmpNow] /= self.PermGroFacCmp
693 mNrmNow = bNrmNow + self.shocks["eState"]
695 return bNrmNow, mNrmNow
697 def get_controls(self):
698 """
699 Calculate consumption for each agent this period.
701 Parameters
702 ----------
703 None
705 Returns
706 -------
707 None
708 """
709 employed = self.shocks["eState"] == 1.0
710 unemployed = np.logical_not(employed)
711 cNrmNow = np.zeros(self.AgentCount)
712 cNrmNow[employed] = self.solution[0].cFunc(self.state_now["mNrm"][employed])
713 cNrmNow[unemployed] = self.solution[0].cFunc_U(
714 self.state_now["mNrm"][unemployed]
715 )
716 self.controls["cNrm"] = cNrmNow
718 def get_poststates(self):
719 """
720 Calculates end-of-period assets for each consumer of this type.
722 Parameters
723 ----------
724 None
726 Returns
727 -------
728 None
729 """
730 self.state_now["aNrm"] = self.state_now["mNrm"] - self.controls["cNrm"]
731 return None