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
« 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.
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_ = {"params": init_tractable, "solver": add_to_stable_arm_points}
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.
349 TODO: This should probably all be moved to a constructor function.
351 Parameters
352 ----------
353 none
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
365 # Define utility functions
366 def uPP(x):
367 return utilityPP(x, rho=CRRA)
369 def uPPP(x):
370 return utilityPPP(x, rho=CRRA)
372 def uPPPP(x):
373 return utilityPPPP(x, rho=CRRA)
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)
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!")
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
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 )
413 MPCtarg = newton(mpcTargFixedPointFunc, 0)
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 )
441 MMPCtarg = newton(mmpcTargFixedPointFunc, 0)
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 )
489 MMMPCtarg = newton(mmmpcTargFixedPointFunc, 0)
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 )
501 def mpcAtZeroFixedPointFunc(k):
502 return k - f_temp(k) / (1 + f_temp(k))
504 # self.MPCmax = newton(mpcAtZeroFixedPointFunc,0.5)
505 MPCmax = brentq(
506 mpcAtZeroFixedPointFunc, PFMPC, 0.99, xtol=0.00000001, rtol=0.00000001
507 )
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]
536 # Set bounds for money (stable arm construction stops when these are exceeded)
537 mLowerBnd = 1.0
538 mUpperBnd = 2.0 * mTarg
540 # Make the terminal period solution
541 solution_terminal = TractableConsumerSolution(
542 mNrm_list=mNrm_list, cNrm_list=cNrm_list, MPC_list=MPC_list
543 )
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
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)
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.
582 Parameters
583 ----------
584 none
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)
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])
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.
611 Parameters
612 ----------
613 which_agents : np.array(Bool)
614 Boolean array of size self.AgentCount indicating which agents should be "born".
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
637 def sim_death(self):
638 """
639 Trivial function that returns boolean array of all False, as there is no death.
641 Parameters
642 ----------
643 None
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
654 def get_shocks(self):
655 """
656 Determine which agents switch from employment to unemployment. All unemployed agents remain
657 unemployed until death.
659 Parameters
660 ----------
661 None
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
674 def transition(self):
675 """
676 Calculate market resources for all agents this period.
678 Parameters
679 ----------
680 None
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"]
691 return bNrmNow, mNrmNow
693 def get_controls(self):
694 """
695 Calculate consumption for each agent this period.
697 Parameters
698 ----------
699 None
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
714 def get_poststates(self):
715 """
716 Calculates end-of-period assets for each consumer of this type.
718 Parameters
719 ----------
720 None
722 Returns
723 -------
724 None
725 """
726 self.state_now["aNrm"] = self.state_now["mNrm"] - self.controls["cNrm"]
727 return None