Coverage for HARK / ConsumptionSaving / ConsPrefShockModel.py: 97%
312 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-07 05:16 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-07 05:16 +0000
1"""
2Extensions to ConsIndShockModel concerning models with preference shocks.
3It currently only two models:
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"""
10import numpy as np
12from HARK import NullFunc
13from HARK.ConsumptionSaving.ConsIndShockModel import (
14 ConsumerSolution,
15 IndShockConsumerType,
16 KinkedRconsumerType,
17 make_assets_grid,
18 make_lognormal_kNrm_init_dstn,
19 make_lognormal_pLvl_init_dstn,
20)
21from HARK.Calibration.Income.IncomeProcesses import (
22 construct_lognormal_income_process_unemployment,
23 get_PermShkDstn_from_IncShkDstn,
24 get_TranShkDstn_from_IncShkDstn,
25)
26from HARK.distributions import MeanOneLogNormal, expected
27from HARK.interpolation import (
28 IdentityFunction,
29 CubicInterp,
30 LinearInterp,
31 LinearInterpOnInterp1D,
32 LowerEnvelope,
33 MargValueFuncCRRA,
34 MargMargValueFuncCRRA,
35 ValueFuncCRRA,
36)
37from HARK.rewards import UtilityFuncCRRA
39__all__ = [
40 "PrefShockConsumerType",
41 "KinkyPrefConsumerType",
42 "make_lognormal_PrefShkDstn",
43]
46def make_pref_shock_solution_terminal(CRRA):
47 """
48 Construct the terminal period solution for a consumption-saving model with
49 CRRA utility and two state variables. The consumption function depends *only*
50 on the first dimension, representing market resources.
52 Parameters
53 ----------
54 CRRA : float
55 Coefficient of relative risk aversion. This is the only relevant parameter.
57 Returns
58 -------
59 solution_terminal : ConsumerSolution
60 Terminal period solution for someone with the given CRRA.
61 """
62 cFunc_terminal = IdentityFunction(i_dim=0, n_dims=2) # c=m at t=T
63 vFunc_terminal = ValueFuncCRRA(cFunc_terminal, CRRA)
64 vPfunc_terminal = MargValueFuncCRRA(cFunc_terminal, CRRA)
65 vPPfunc_terminal = MargMargValueFuncCRRA(cFunc_terminal, CRRA)
66 solution_terminal = ConsumerSolution(
67 cFunc=cFunc_terminal,
68 vFunc=vFunc_terminal,
69 vPfunc=vPfunc_terminal,
70 vPPfunc=vPPfunc_terminal,
71 mNrmMin=0.0,
72 hNrm=0.0,
73 MPCmin=1.0,
74 MPCmax=1.0,
75 )
76 return solution_terminal
79def make_lognormal_PrefShkDstn(
80 T_cycle,
81 PrefShkStd,
82 PrefShkCount,
83 RNG,
84 PrefShk_tail_N=0,
85 PrefShk_tail_order=np.e,
86 PrefShk_tail_bound=[0.02, 0.98],
87):
88 r"""
89 Make a discretized mean one lognormal preference shock distribution for each
90 period of the agent's problem.
92 .. math::
93 \eta_t \sim \mathcal{N}(-\textbf{PrefShkStd}_{t}^{2}/2,\textbf{PrefShkStd}_{t}^2)
95 Parameters
96 ----------
97 T_cycle : int
98 Number of non-terminal periods in the agent's cycle.
99 PrefShkStd : [float]
100 Standard deviation of log preference shocks in each period.
101 PrefShkCount : int
102 Number of equiprobable preference shock nodes in the "body" of the distribution.
103 RNG : RandomState
104 The AgentType's internal random number generator.
105 PrefShk_tail_N : int
106 Number of shock nodes in each "tail" of the distribution (optional).
107 PrefShk_tail_order : float
108 Scaling factor for tail nodes (optional).
109 PrefShk_tail_bound : [float,float]
110 CDF bounds for tail nodes (optional).
112 Returns
113 -------
114 PrefShkDstn : [DiscreteDistribution]
115 List of discretized lognormal distributions for shocks.
116 """
117 PrefShkDstn = [] # discrete distributions of preference shocks
118 for t in range(T_cycle):
119 PrefShkStd = PrefShkStd[t]
120 new_dstn = MeanOneLogNormal(
121 sigma=PrefShkStd, seed=RNG.integers(0, 2**31 - 1)
122 ).discretize(
123 N=PrefShkCount,
124 method="equiprobable",
125 tail_N=PrefShk_tail_N,
126 tail_order=PrefShk_tail_order,
127 tail_bound=PrefShk_tail_bound,
128 )
129 PrefShkDstn.append(new_dstn)
130 return PrefShkDstn
133###############################################################################
136def solve_one_period_ConsPrefShock(
137 solution_next,
138 IncShkDstn,
139 PrefShkDstn,
140 LivPrb,
141 DiscFac,
142 CRRA,
143 Rfree,
144 PermGroFac,
145 BoroCnstArt,
146 aXtraGrid,
147 vFuncBool,
148 CubicBool,
149):
150 """
151 Solves one period of a consumption-saving model with idiosyncratic shocks to
152 permanent and transitory income, with one risk free asset and CRRA utility.
153 The consumer also faces iid preference shocks as a multiplicative shifter to
154 their marginal utility of consumption.
156 Parameters
157 ----------
158 solution_next : ConsumerSolution
159 The solution to the succeeding one period problem.
160 IncShkDstn : distribution.Distribution
161 A discrete approximation to the income process between the period being
162 solved and the one immediately following (in solution_next). Order:
163 permanent shocks, transitory shocks.
164 PrefShkDstn : distribution.Distribution
165 Discrete distribution of the multiplicative utility shifter. Order:
166 probabilities, preference shocks.
167 LivPrb : float
168 Survival probability; likelihood of being alive at the beginning of
169 the succeeding period.
170 DiscFac : float
171 Intertemporal discount factor for future utility.
172 CRRA : float
173 Coefficient of relative risk aversion.
174 Rfree : float
175 Risk free interest factor on end-of-period assets.
176 PermGroGac : float
177 Expected permanent income growth factor at the end of this period.
178 BoroCnstArt: float or None
179 Borrowing constraint for the minimum allowable assets to end the
180 period with. If it is less than the natural borrowing constraint,
181 then it is irrelevant; BoroCnstArt=None indicates no artificial bor-
182 rowing constraint.
183 aXtraGrid: np.array
184 Array of "extra" end-of-period asset values-- assets above the
185 absolute minimum acceptable level.
186 vFuncBool: boolean
187 An indicator for whether the value function should be computed and
188 included in the reported solution.
189 CubicBool: boolean
190 An indicator for whether the solver should use cubic or linear inter-
191 polation.
193 Returns
194 -------
195 solution: ConsumerSolution
196 The solution to the single period consumption-saving problem. Includes
197 a consumption function cFunc (using linear splines), a marginal value
198 function vPfunc, a minimum acceptable level of normalized market re-
199 sources mNrmMin, normalized human wealth hNrm, and bounding MPCs MPCmin
200 and MPCmax. It might also have a value function vFunc. The consumption
201 function is defined over normalized market resources and the preference
202 shock, c = cFunc(m,PrefShk), but the (marginal) value function is defined
203 unconditionally on the shock, just before it is revealed.
204 """
205 # Define the current period utility function and effective discount factor
206 uFunc = UtilityFuncCRRA(CRRA)
207 DiscFacEff = DiscFac * LivPrb # "effective" discount factor
209 # Unpack next period's income and preference shock distributions
210 ShkPrbsNext = IncShkDstn.pmv
211 PermShkValsNext = IncShkDstn.atoms[0]
212 TranShkValsNext = IncShkDstn.atoms[1]
213 PermShkMinNext = np.min(PermShkValsNext)
214 TranShkMinNext = np.min(TranShkValsNext)
215 PrefShkPrbs = PrefShkDstn.pmv
216 PrefShkVals = PrefShkDstn.atoms.flatten()
218 # Calculate the probability that we get the worst possible income draw
219 IncNext = PermShkValsNext * TranShkValsNext
220 WorstIncNext = PermShkMinNext * TranShkMinNext
221 WorstIncPrb = np.sum(ShkPrbsNext[IncNext == WorstIncNext])
222 # WorstIncPrb is the "Weierstrass p" concept: the odds we get the WORST thing
224 # Unpack next period's (marginal) value function
225 vFuncNext = solution_next.vFunc # This is None when vFuncBool is False
226 vPfuncNext = solution_next.vPfunc
227 vPPfuncNext = solution_next.vPPfunc # This is None when CubicBool is False
229 # Update the bounding MPCs and PDV of human wealth:
230 PatFac = ((Rfree * DiscFacEff) ** (1.0 / CRRA)) / Rfree
231 try:
232 MPCminNow = 1.0 / (1.0 + PatFac / solution_next.MPCmin)
233 except:
234 MPCminNow = 0.0
235 Ex_IncNext = np.dot(ShkPrbsNext, TranShkValsNext * PermShkValsNext)
236 hNrmNow = PermGroFac / Rfree * (Ex_IncNext + solution_next.hNrm)
237 temp_fac = (WorstIncPrb ** (1.0 / CRRA)) * PatFac
238 MPCmaxNow = 1.0 / (1.0 + temp_fac / solution_next.MPCmax)
240 # Calculate the minimum allowable value of money resources in this period
241 PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rfree
242 BoroCnstNat = (solution_next.mNrmMin - TranShkMinNext) * PermGroFacEffMin
244 # Set the minimum allowable (normalized) market resources based on the natural
245 # and artificial borrowing constraints
246 if BoroCnstArt is None:
247 mNrmMinNow = BoroCnstNat
248 else:
249 mNrmMinNow = np.max([BoroCnstNat, BoroCnstArt])
251 # Set the upper limit of the MPC (at mNrmMinNow) based on whether the natural
252 # or artificial borrowing constraint actually binds
253 if BoroCnstNat < mNrmMinNow:
254 MPCmaxEff = 1.0 # If actually constrained, MPC near limit is 1
255 else:
256 MPCmaxEff = MPCmaxNow # Otherwise, it's the MPC calculated above
258 # Define the borrowing-constrained consumption function
259 cFuncNowCnst = LinearInterp(
260 np.array([mNrmMinNow, mNrmMinNow + 1.0]), np.array([0.0, 1.0])
261 )
263 # Construct the assets grid by adjusting aXtra by the natural borrowing constraint
264 aNrmNow = np.asarray(aXtraGrid) + BoroCnstNat
266 # Define local functions for taking future expectations
267 def calc_mNrmNext(S, a, R):
268 return R / (PermGroFac * S["PermShk"]) * a + S["TranShk"]
270 def calc_vNext(S, a, R):
271 return (S["PermShk"] ** (1.0 - CRRA) * PermGroFac ** (1.0 - CRRA)) * vFuncNext(
272 calc_mNrmNext(S, a, R)
273 )
275 def calc_vPnext(S, a, R):
276 return S["PermShk"] ** (-CRRA) * vPfuncNext(calc_mNrmNext(S, a, R))
278 def calc_vPPnext(S, a, R):
279 return S["PermShk"] ** (-CRRA - 1.0) * vPPfuncNext(calc_mNrmNext(S, a, R))
281 # Calculate end-of-period marginal value of assets at each gridpoint
282 vPfacEff = DiscFacEff * Rfree * PermGroFac ** (-CRRA)
283 EndOfPrdvP = vPfacEff * expected(calc_vPnext, IncShkDstn, args=(aNrmNow, Rfree))
285 # Find optimal consumption corresponding to each aNrm, PrefShk combination
286 cNrm_base = uFunc.derinv(EndOfPrdvP, order=(1, 0))
287 PrefShkCount = PrefShkVals.size
288 PrefShk_temp = np.tile(
289 np.reshape(PrefShkVals ** (1.0 / CRRA), (PrefShkCount, 1)),
290 (1, cNrm_base.size),
291 )
292 cNrmNow = np.tile(cNrm_base, (PrefShkCount, 1)) * PrefShk_temp
293 mNrmNow = cNrmNow + np.tile(aNrmNow, (PrefShkCount, 1))
294 # These are the endogenous gridpoints, as usual
296 # Add the bottom point to the c and m arrays
297 m_for_interpolation = np.concatenate(
298 (BoroCnstNat * np.ones((PrefShkCount, 1)), mNrmNow), axis=1
299 )
300 c_for_interpolation = np.concatenate((np.zeros((PrefShkCount, 1)), cNrmNow), axis=1)
302 # Construct the consumption function as a cubic or linear spline interpolation
303 # for each value of PrefShk, interpolated across those values.
304 if CubicBool:
305 # This is not yet supported, not sure why we never got to it
306 raise ValueError(
307 "Cubic interpolation is not yet supported by the preference shock model!"
308 )
310 # Make the preference-shock specific consumption functions
311 cFuncs_by_PrefShk = []
312 for j in range(PrefShkCount):
313 MPCmin_j = MPCminNow * PrefShkVals[j] ** (1.0 / CRRA)
314 cFunc_this_shk = LowerEnvelope(
315 LinearInterp(
316 m_for_interpolation[j, :],
317 c_for_interpolation[j, :],
318 intercept_limit=hNrmNow * MPCmin_j,
319 slope_limit=MPCmin_j,
320 ),
321 cFuncNowCnst,
322 )
323 cFuncs_by_PrefShk.append(cFunc_this_shk)
325 # Combine the list of consumption functions into a single interpolation
326 cFuncNow = LinearInterpOnInterp1D(cFuncs_by_PrefShk, PrefShkVals)
328 # Make the ex ante marginal value function (before the preference shock)
329 m_grid = aXtraGrid + mNrmMinNow
330 vP_vec = np.zeros_like(m_grid)
331 for j in range(PrefShkCount): # numeric integration over the preference shock
332 vP_vec += (
333 uFunc.der(cFuncs_by_PrefShk[j](m_grid)) * PrefShkPrbs[j] * PrefShkVals[j]
334 )
335 vPnvrs_vec = uFunc.derinv(vP_vec, order=(1, 0))
336 vPfuncNow = MargValueFuncCRRA(LinearInterp(m_grid, vPnvrs_vec), CRRA)
338 # Define this period's marginal marginal value function
339 vPPfuncNow = NullFunc() # Dummy object, cubic interpolation not implemented
341 # Construct this period's value function if requested
342 if vFuncBool:
343 # Calculate end-of-period value, its derivative, and their pseudo-inverse
344 EndOfPrdv = DiscFacEff * expected(calc_vNext, IncShkDstn, args=(aNrmNow, Rfree))
345 EndOfPrdvNvrs = uFunc.inv(
346 EndOfPrdv
347 ) # value transformed through inverse utility
348 EndOfPrdvNvrsP = EndOfPrdvP * uFunc.derinv(EndOfPrdv, order=(0, 1))
349 EndOfPrdvNvrs = np.insert(EndOfPrdvNvrs, 0, 0.0)
350 EndOfPrdvNvrsP = np.insert(EndOfPrdvNvrsP, 0, EndOfPrdvNvrsP[0])
351 # This is a very good approximation, vNvrsPP = 0 at the asset minimum
353 # Construct the end-of-period value function
354 aNrm_temp = np.insert(aNrmNow, 0, BoroCnstNat)
355 EndOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, EndOfPrdvNvrs, EndOfPrdvNvrsP)
356 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA)
358 # Compute expected value and marginal value on a grid of market resources,
359 # accounting for all of the discrete preference shocks
360 mNrm_temp = mNrmMinNow + aXtraGrid
361 v_temp = np.zeros_like(mNrm_temp)
362 vP_temp = np.zeros_like(mNrm_temp)
363 for j in range(PrefShkCount):
364 this_shock = PrefShkVals[j]
365 this_prob = PrefShkPrbs[j]
366 cNrm_temp = cFuncNow(mNrm_temp, this_shock * np.ones_like(mNrm_temp))
367 aNrm_temp = mNrm_temp - cNrm_temp
368 v_temp += this_prob * (
369 this_shock * uFunc(cNrm_temp) + EndOfPrd_vFunc(aNrm_temp)
370 )
371 vP_temp += this_prob * this_shock * uFunc.der(cNrm_temp)
373 # Construct the beginning-of-period value function
374 # value transformed through inverse utility
375 vNvrs_temp = uFunc.inv(v_temp)
376 vNvrsP_temp = vP_temp * uFunc.derinv(v_temp, order=(0, 1))
377 mNrm_temp = np.insert(mNrm_temp, 0, mNrmMinNow)
378 vNvrs_temp = np.insert(vNvrs_temp, 0, 0.0)
379 vNvrsP_temp = np.insert(vNvrsP_temp, 0, MPCmaxEff ** (-CRRA / (1.0 - CRRA)))
380 MPCminNvrs = MPCminNow ** (-CRRA / (1.0 - CRRA))
381 vNvrsFuncNow = CubicInterp(
382 mNrm_temp, vNvrs_temp, vNvrsP_temp, MPCminNvrs * hNrmNow, MPCminNvrs
383 )
384 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA)
386 else:
387 vFuncNow = NullFunc() # Dummy object
389 # Create and return this period's solution
390 solution_now = ConsumerSolution(
391 cFunc=cFuncNow,
392 vFunc=vFuncNow,
393 vPfunc=vPfuncNow,
394 vPPfunc=vPPfuncNow,
395 mNrmMin=mNrmMinNow,
396 hNrm=hNrmNow,
397 MPCmin=MPCminNow,
398 MPCmax=MPCmaxEff,
399 )
400 return solution_now
403###############################################################################
406def solve_one_period_ConsKinkyPref(
407 solution_next,
408 IncShkDstn,
409 PrefShkDstn,
410 LivPrb,
411 DiscFac,
412 CRRA,
413 Rboro,
414 Rsave,
415 PermGroFac,
416 BoroCnstArt,
417 aXtraGrid,
418 vFuncBool,
419 CubicBool,
420):
421 """
422 Solves one period of a consumption-saving model with idiosyncratic shocks to
423 permanent and transitory income, with a risk free asset and CRRA utility.
424 In this variation, the interest rate on borrowing Rboro exceeds the interest
425 rate on saving Rsave. The consumer also faces iid preference shocks as a multi-
426 plicative shifter to their marginal utility of consumption.
428 Parameters
429 ----------
430 solution_next : ConsumerSolution
431 The solution to next period's one period problem.
432 IncShkDstn : distribution.Distribution
433 A discrete approximation to the income process between the period being
434 solved and the one immediately following (in solution_next).
435 PrefShkDstn : distribution.Distribution
436 Discrete distribution of the multiplicative utility shifter. Order:
437 probabilities, preference shocks.
438 LivPrb : float
439 Survival probability; likelihood of being alive at the beginning of
440 the succeeding period.
441 DiscFac : float
442 Intertemporal discount factor for future utility.
443 CRRA : float
444 Coefficient of relative risk aversion.
445 Rboro: float
446 Interest factor on assets between this period and the succeeding
447 period when assets are negative.
448 Rsave: float
449 Interest factor on assets between this period and the succeeding
450 period when assets are positive.
451 PermGroFac : float
452 Expected permanent income growth factor at the end of this period.
453 BoroCnstArt: float or None
454 Borrowing constraint for the minimum allowable assets to end the
455 period with. If it is less than the natural borrowing constraint,
456 then it is irrelevant; BoroCnstArt=None indicates no artificial bor-
457 rowing constraint.
458 aXtraGrid: np.array
459 Array of "extra" end-of-period asset values-- assets above the
460 absolute minimum acceptable level.
461 vFuncBool: boolean
462 An indicator for whether the value function should be computed and
463 included in the reported solution.
464 CubicBool: boolean
465 An indicator for whether the solver should use cubic or linear inter-
466 polation.
468 Returns
469 -------
470 solution_now : ConsumerSolution
471 The solution to the single period consumption-saving problem. Includes
472 a consumption function cFunc (using linear splines), a marginal value
473 function vPfunc, a minimum acceptable level of normalized market re-
474 sources mNrmMin, normalized human wealth hNrm, and bounding MPCs MPCmin
475 and MPCmax. It might also have a value function vFunc. The consumption
476 function is defined over normalized market resources and the preference
477 shock, c = cFunc(m,PrefShk), but the (marginal) value function is defined
478 unconditionally on the shock, just before it is revealed.
479 """
480 # Verifiy that there is actually a kink in the interest factor
481 assert Rboro >= Rsave, (
482 "Interest factor on debt less than interest factor on savings!"
483 )
484 # If the kink is in the wrong direction, code should break here. If there's
485 # no kink at all, then just use the ConsIndShockModel solver.
486 if Rboro == Rsave:
487 solution_now = solve_one_period_ConsPrefShock(
488 solution_next,
489 IncShkDstn,
490 PrefShkDstn,
491 LivPrb,
492 DiscFac,
493 CRRA,
494 Rboro,
495 PermGroFac,
496 BoroCnstArt,
497 aXtraGrid,
498 vFuncBool,
499 CubicBool,
500 )
501 return solution_now
503 # Define the current period utility function and effective discount factor
504 uFunc = UtilityFuncCRRA(CRRA)
505 DiscFacEff = DiscFac * LivPrb # "effective" discount factor
507 # Unpack next period's income and preference shock distributions
508 ShkPrbsNext = IncShkDstn.pmv
509 PermShkValsNext = IncShkDstn.atoms[0]
510 TranShkValsNext = IncShkDstn.atoms[1]
511 PermShkMinNext = np.min(PermShkValsNext)
512 TranShkMinNext = np.min(TranShkValsNext)
513 PrefShkPrbs = PrefShkDstn.pmv
514 PrefShkVals = PrefShkDstn.atoms.flatten()
516 # Calculate the probability that we get the worst possible income draw
517 IncNext = PermShkValsNext * TranShkValsNext
518 WorstIncNext = PermShkMinNext * TranShkMinNext
519 WorstIncPrb = np.sum(ShkPrbsNext[IncNext == WorstIncNext])
520 # WorstIncPrb is the "Weierstrass p" concept: the odds we get the WORST thing
522 # Unpack next period's (marginal) value function
523 vFuncNext = solution_next.vFunc # This is None when vFuncBool is False
524 vPfuncNext = solution_next.vPfunc
525 vPPfuncNext = solution_next.vPPfunc # This is None when CubicBool is False
527 # Update the bounding MPCs and PDV of human wealth:
528 PatFac = ((Rsave * DiscFacEff) ** (1.0 / CRRA)) / Rsave
529 PatFacAlt = ((Rboro * DiscFacEff) ** (1.0 / CRRA)) / Rboro
530 try:
531 MPCminNow = 1.0 / (1.0 + PatFac / solution_next.MPCmin)
532 except:
533 MPCminNow = 0.0
534 Ex_IncNext = np.dot(ShkPrbsNext, TranShkValsNext * PermShkValsNext)
535 hNrmNow = (PermGroFac / Rsave) * (Ex_IncNext + solution_next.hNrm)
536 temp_fac = (WorstIncPrb ** (1.0 / CRRA)) * PatFacAlt
537 MPCmaxNow = 1.0 / (1.0 + temp_fac / solution_next.MPCmax)
539 # Calculate the minimum allowable value of money resources in this period
540 PermGroFacEffMin = (PermGroFac * PermShkMinNext) / Rboro
541 BoroCnstNat = (solution_next.mNrmMin - TranShkMinNext) * PermGroFacEffMin
543 # Set the minimum allowable (normalized) market resources based on the natural
544 # and artificial borrowing constraints
545 if BoroCnstArt is None:
546 mNrmMinNow = BoroCnstNat
547 else:
548 mNrmMinNow = np.max([BoroCnstNat, BoroCnstArt])
550 # Set the upper limit of the MPC (at mNrmMinNow) based on whether the natural
551 # or artificial borrowing constraint actually binds
552 if BoroCnstNat < mNrmMinNow:
553 MPCmaxEff = 1.0 # If actually constrained, MPC near limit is 1
554 else:
555 MPCmaxEff = MPCmaxNow # Otherwise, it's the MPC calculated above
557 # Define the borrowing-constrained consumption function
558 cFuncNowCnst = LinearInterp(
559 np.array([mNrmMinNow, mNrmMinNow + 1.0]), np.array([0.0, 1.0])
560 )
562 # Construct the assets grid by adjusting aXtra by the natural borrowing constraint
563 aNrmNow = np.sort(
564 np.hstack((np.asarray(aXtraGrid) + mNrmMinNow, np.array([0.0, 0.0])))
565 )
567 # Make a 1D array of the interest factor at each asset gridpoint
568 Rfree = Rsave * np.ones_like(aNrmNow)
569 Rfree[aNrmNow < 0] = Rboro
570 i_kink = np.argwhere(aNrmNow == 0.0)[0][0]
571 Rfree[i_kink] = Rboro
573 # Define local functions for taking future expectations
574 def calc_mNrmNext(S, a, R):
575 return R / (PermGroFac * S["PermShk"]) * a + S["TranShk"]
577 def calc_vNext(S, a, R):
578 return (S["PermShk"] ** (1.0 - CRRA) * PermGroFac ** (1.0 - CRRA)) * vFuncNext(
579 calc_mNrmNext(S, a, R)
580 )
582 def calc_vPnext(S, a, R):
583 return S["PermShk"] ** (-CRRA) * vPfuncNext(calc_mNrmNext(S, a, R))
585 def calc_vPPnext(S, a, R):
586 return S["PermShk"] ** (-CRRA - 1.0) * vPPfuncNext(calc_mNrmNext(S, a, R))
588 # Calculate end-of-period marginal value of assets at each gridpoint
589 vPfacEff = DiscFacEff * Rfree * PermGroFac ** (-CRRA)
590 EndOfPrdvP = vPfacEff * expected(calc_vPnext, IncShkDstn, args=(aNrmNow, Rfree))
592 # Find optimal consumption corresponding to each aNrm, PrefShk combination
593 cNrm_base = uFunc.derinv(EndOfPrdvP, order=(1, 0))
594 PrefShkCount = PrefShkVals.size
595 PrefShk_temp = np.tile(
596 np.reshape(PrefShkVals ** (1.0 / CRRA), (PrefShkCount, 1)),
597 (1, cNrm_base.size),
598 )
599 cNrmNow = np.tile(cNrm_base, (PrefShkCount, 1)) * PrefShk_temp
600 mNrmNow = cNrmNow + np.tile(aNrmNow, (PrefShkCount, 1))
601 # These are the endogenous gridpoints, as usual
603 # Add the bottom point to the c and m arrays
604 m_for_interpolation = np.concatenate(
605 (BoroCnstNat * np.ones((PrefShkCount, 1)), mNrmNow), axis=1
606 )
607 c_for_interpolation = np.concatenate((np.zeros((PrefShkCount, 1)), cNrmNow), axis=1)
609 # Construct the consumption function as a cubic or linear spline interpolation
610 # for each value of PrefShk, interpolated across those values.
611 if CubicBool:
612 # This is not yet supported, not sure why we never got to it
613 raise ValueError(
614 "Cubic interpolation is not yet supported by the preference shock model!"
615 )
617 # Make the preference-shock specific consumption functions
618 cFuncs_by_PrefShk = []
619 for j in range(PrefShkCount):
620 MPCmin_j = MPCminNow * PrefShkVals[j] ** (1.0 / CRRA)
621 cFunc_this_shk = LowerEnvelope(
622 LinearInterp(
623 m_for_interpolation[j, :],
624 c_for_interpolation[j, :],
625 intercept_limit=hNrmNow * MPCmin_j,
626 slope_limit=MPCmin_j,
627 ),
628 cFuncNowCnst,
629 )
630 cFuncs_by_PrefShk.append(cFunc_this_shk)
632 # Combine the list of consumption functions into a single interpolation
633 cFuncNow = LinearInterpOnInterp1D(cFuncs_by_PrefShk, PrefShkVals)
635 # Make the ex ante marginal value function (before the preference shock)
636 m_grid = aXtraGrid + mNrmMinNow
637 vP_vec = np.zeros_like(m_grid)
638 for j in range(PrefShkCount): # numeric integration over the preference shock
639 vP_vec += (
640 uFunc.der(cFuncs_by_PrefShk[j](m_grid)) * PrefShkPrbs[j] * PrefShkVals[j]
641 )
642 vPnvrs_vec = uFunc.derinv(vP_vec, order=(1, 0))
643 vPfuncNow = MargValueFuncCRRA(LinearInterp(m_grid, vPnvrs_vec), CRRA)
645 # Define this period's marginal marginal value function
646 vPPfuncNow = NullFunc() # Dummy object, cubic interpolation not implemented
648 # Construct this period's value function if requested
649 if vFuncBool:
650 # Calculate end-of-period value, its derivative, and their pseudo-inverse
651 EndOfPrdv = DiscFacEff * expected(calc_vNext, IncShkDstn, args=(aNrmNow, Rfree))
652 EndOfPrdvNvrs = uFunc.inv(
653 EndOfPrdv
654 ) # value transformed through inverse utility
655 EndOfPrdvNvrsP = EndOfPrdvP * uFunc.derinv(EndOfPrdv, order=(0, 1))
656 EndOfPrdvNvrs = np.insert(EndOfPrdvNvrs, 0, 0.0)
657 EndOfPrdvNvrsP = np.insert(EndOfPrdvNvrsP, 0, EndOfPrdvNvrsP[0])
658 # This is a very good approximation, vNvrsPP = 0 at the asset minimum
660 # Construct the end-of-period value function
661 aNrm_temp = np.insert(aNrmNow, 0, BoroCnstNat)
662 EndOfPrd_vNvrsFunc = CubicInterp(aNrm_temp, EndOfPrdvNvrs, EndOfPrdvNvrsP)
663 EndOfPrd_vFunc = ValueFuncCRRA(EndOfPrd_vNvrsFunc, CRRA)
665 # Compute expected value and marginal value on a grid of market resources,
666 # accounting for all of the discrete preference shocks
667 mNrm_temp = mNrmMinNow + aXtraGrid
668 v_temp = np.zeros_like(mNrm_temp)
669 vP_temp = np.zeros_like(mNrm_temp)
670 for j in range(PrefShkCount):
671 this_shock = PrefShkVals[j]
672 this_prob = PrefShkPrbs[j]
673 cNrm_temp = cFuncNow(mNrm_temp, this_shock * np.ones_like(mNrm_temp))
674 aNrm_temp = mNrm_temp - cNrm_temp
675 v_temp += this_prob * (
676 this_shock * uFunc(cNrm_temp) + EndOfPrd_vFunc(aNrm_temp)
677 )
678 vP_temp += this_prob * this_shock * uFunc.der(cNrm_temp)
680 # Construct the beginning-of-period value function
681 # value transformed through inverse utility
682 vNvrs_temp = uFunc.inv(v_temp)
683 vNvrsP_temp = vP_temp * uFunc.derinv(v_temp, order=(0, 1))
684 mNrm_temp = np.insert(mNrm_temp, 0, mNrmMinNow)
685 vNvrs_temp = np.insert(vNvrs_temp, 0, 0.0)
686 vNvrsP_temp = np.insert(vNvrsP_temp, 0, MPCmaxEff ** (-CRRA / (1.0 - CRRA)))
687 MPCminNvrs = MPCminNow ** (-CRRA / (1.0 - CRRA))
688 vNvrsFuncNow = CubicInterp(
689 mNrm_temp, vNvrs_temp, vNvrsP_temp, MPCminNvrs * hNrmNow, MPCminNvrs
690 )
691 vFuncNow = ValueFuncCRRA(vNvrsFuncNow, CRRA)
693 else:
694 vFuncNow = NullFunc() # Dummy object
696 # Create and return this period's solution
697 solution_now = ConsumerSolution(
698 cFunc=cFuncNow,
699 vFunc=vFuncNow,
700 vPfunc=vPfuncNow,
701 vPPfunc=vPPfuncNow,
702 mNrmMin=mNrmMinNow,
703 hNrm=hNrmNow,
704 MPCmin=MPCminNow,
705 MPCmax=MPCmaxEff,
706 )
707 return solution_now
710###############################################################################
712# Make a dictionary of constructors for the preference shock model
713PrefShockConsumerType_constructors_default = {
714 "IncShkDstn": construct_lognormal_income_process_unemployment,
715 "PermShkDstn": get_PermShkDstn_from_IncShkDstn,
716 "TranShkDstn": get_TranShkDstn_from_IncShkDstn,
717 "aXtraGrid": make_assets_grid,
718 "PrefShkDstn": make_lognormal_PrefShkDstn,
719 "solution_terminal": make_pref_shock_solution_terminal,
720 "kNrmInitDstn": make_lognormal_kNrm_init_dstn,
721 "pLvlInitDstn": make_lognormal_pLvl_init_dstn,
722}
724# Make a dictionary with parameters for the default constructor for kNrmInitDstn
725PrefShockConsumerType_kNrmInitDstn_default = {
726 "kLogInitMean": -12.0, # Mean of log initial capital
727 "kLogInitStd": 0.0, # Stdev of log initial capital
728 "kNrmInitCount": 15, # Number of points in initial capital discretization
729}
731# Make a dictionary with parameters for the default constructor for pLvlInitDstn
732PrefShockConsumerType_pLvlInitDstn_default = {
733 "pLogInitMean": 0.0, # Mean of log permanent income
734 "pLogInitStd": 0.0, # Stdev of log permanent income
735 "pLvlInitCount": 15, # Number of points in initial capital discretization
736}
738# Default parameters to make IncShkDstn using construct_lognormal_income_process_unemployment
739PrefShockConsumerType_IncShkDstn_default = {
740 "PermShkStd": [0.1], # Standard deviation of log permanent income shocks
741 "PermShkCount": 7, # Number of points in discrete approximation to permanent income shocks
742 "TranShkStd": [0.1], # Standard deviation of log transitory income shocks
743 "TranShkCount": 7, # Number of points in discrete approximation to transitory income shocks
744 "UnempPrb": 0.05, # Probability of unemployment while working
745 "IncUnemp": 0.3, # Unemployment benefits replacement rate while working
746 "T_retire": 0, # Period of retirement (0 --> no retirement)
747 "UnempPrbRet": 0.005, # Probability of "unemployment" while retired
748 "IncUnempRet": 0.0, # "Unemployment" benefits when retired
749}
751# Default parameters to make aXtraGrid using construct_assets_grid
753PrefShockConsumerType_aXtraGrid_default = {
754 "aXtraMin": 0.001, # Minimum end-of-period "assets above minimum" value
755 "aXtraMax": 20, # Maximum end-of-period "assets above minimum" value
756 "aXtraNestFac": 3, # Exponential nesting factor for aXtraGrid
757 "aXtraCount": 48, # Number of points in the grid of "assets above minimum"
758 "aXtraExtra": None, # Additional other values to add in grid (optional)
759}
761# Default parameters to make PrefShkDstn using make_lognormal_PrefShkDstn
763PrefShockConsumerType_PrefShkDstn_default = {
764 "PrefShkCount": 12, # Number of points in discrete approximation to preference shock dist
765 "PrefShk_tail_N": 4, # Number of "tail points" on each end of pref shock dist
766 "PrefShkStd": [0.30], # Standard deviation of utility shocks
767}
769# Make a dictionary to specify an preference shocks consumer type
770PrefShockConsumerType_solving_default = {
771 # BASIC HARK PARAMETERS REQUIRED TO SOLVE THE MODEL
772 "cycles": 1, # Finite, non-cyclic model
773 "T_cycle": 1, # Number of periods in the cycle for this agent type
774 "pseudo_terminal": False, # Terminal period really does exist
775 "constructors": PrefShockConsumerType_constructors_default, # See dictionary above
776 # PRIMITIVE RAW PARAMETERS REQUIRED TO SOLVE THE MODEL
777 "CRRA": 2.0, # Coefficient of relative risk aversion
778 "Rfree": [1.03], # Interest factor on retained assets
779 "DiscFac": 0.96, # Intertemporal discount factor
780 "LivPrb": [0.98], # Survival probability after each period
781 "PermGroFac": [1.01], # Permanent income growth factor
782 "BoroCnstArt": 0.0, # Artificial borrowing constraint
783 "vFuncBool": False, # Whether to calculate the value function during solution
784 "CubicBool": False, # Whether to use cubic spline interpolation when True
785 # (Uses linear spline interpolation for cFunc when False)
786}
787PrefShockConsumerType_simulation_default = {
788 # PARAMETERS REQUIRED TO SIMULATE THE MODEL
789 "AgentCount": 10000, # Number of agents of this type
790 "T_age": None, # Age after which simulated agents are automatically killed
791 "PermGroFacAgg": 1.0, # Aggregate permanent income growth factor
792 # (The portion of PermGroFac attributable to aggregate productivity growth)
793 "NewbornTransShk": False, # Whether Newborns have transitory shock
794 # ADDITIONAL OPTIONAL PARAMETERS
795 "PerfMITShk": False, # Do Perfect Foresight MIT Shock
796 # (Forces Newborns to follow solution path of the agent they replaced if True)
797 "neutral_measure": False, # Whether to use permanent income neutral measure (see Harmenberg 2021)
798}
800PrefShockConsumerType_default = {}
801PrefShockConsumerType_default.update(PrefShockConsumerType_IncShkDstn_default)
802PrefShockConsumerType_default.update(PrefShockConsumerType_aXtraGrid_default)
803PrefShockConsumerType_default.update(PrefShockConsumerType_PrefShkDstn_default)
804PrefShockConsumerType_default.update(PrefShockConsumerType_kNrmInitDstn_default)
805PrefShockConsumerType_default.update(PrefShockConsumerType_pLvlInitDstn_default)
806PrefShockConsumerType_default.update(PrefShockConsumerType_solving_default)
807PrefShockConsumerType_default.update(PrefShockConsumerType_simulation_default)
808init_preference_shocks = (
809 PrefShockConsumerType_default # So models that aren't updated don't break
810)
813class PrefShockConsumerType(IndShockConsumerType):
814 r"""
815 A consumer type based on IndShockConsumerType, with multiplicative shocks to utility each period.
817 .. math::
818 \newcommand{\CRRA}{\rho}
819 \newcommand{\DiePrb}{\mathsf{D}}
820 \newcommand{\PermGroFac}{\Gamma}
821 \newcommand{\Rfree}{\mathsf{R}}
822 \newcommand{\DiscFac}{\beta}
823 \begin{align*}
824 v_t(m_t,\eta_t) &=\max_{c_t} \eta_{t} u(c_t) + \DiscFac (1 - \DiePrb_{t+1}) \mathbb{E}_{t} \left[ (\PermGroFac_{t+1} \psi_{t+1})^{1-\CRRA} v_{t+1}(m_{t+1},\eta_{t+1}) \right], \\
825 & \text{s.t.} \\
826 a_t &= m_t - c_t, \\
827 a_t &\geq \underline{a}, \\
828 m_{t+1} &= a_t \Rfree_{t+1}/(\PermGroFac_{t+1} \psi_{t+1}) + \theta_{t+1}, \\
829 (\psi_{t+1},\theta_{t+1},\eta_{t+1}) &\sim F_{t+1}, \\
830 \mathbb{E}[\psi]=\mathbb{E}[\theta] &= 1, \\
831 u(c) &= \frac{c^{1-\CRRA}}{1-\CRRA} \\
832 \end{align*}
835 Constructors
836 ------------
837 IncShkDstn: Constructor, :math:`\psi`, :math:`\theta`
838 The agent's income shock distributions.
840 Its default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.construct_lognormal_income_process_unemployment`
841 aXtraGrid: Constructor
842 The agent's asset grid.
844 Its default constructor is :func:`HARK.utilities.make_assets_grid`
845 PrefShkDstn: Constructor, :math:`\eta`
846 The agent's preference shock distributions.
848 Its default constuctor is :func:`HARK.ConsumptionSaving.ConsPrefShockModel.make_lognormal_PrefShkDstn`
850 Solving Parameters
851 ------------------
852 cycles: int
853 0 specifies an infinite horizon model, 1 specifies a finite model.
854 T_cycle: int
855 Number of periods in the cycle for this agent type.
856 CRRA: float, :math:`\rho`
857 Coefficient of Relative Risk Aversion.
858 Rfree: float or list[float], time varying, :math:`\mathsf{R}`
859 Risk Free interest rate. Pass a list of floats to make Rfree time varying.
860 DiscFac: float, :math:`\beta`
861 Intertemporal discount factor.
862 LivPrb: list[float], time varying, :math:`1-\mathsf{D}`
863 Survival probability after each period.
864 PermGroFac: list[float], time varying, :math:`\Gamma`
865 Permanent income growth factor.
866 BoroCnstArt: float, :math:`\underline{a}`
867 The minimum Asset/Perminant Income ratio, None to ignore.
868 vFuncBool: bool
869 Whether to calculate the value function during solution.
870 CubicBool: bool
871 Whether to use cubic spline interpoliation.
873 Simulation Parameters
874 ---------------------
875 AgentCount: int
876 Number of agents of this kind that are created during simulations.
877 T_age: int
878 Age after which to automatically kill agents, None to ignore.
879 T_sim: int, required for simulation
880 Number of periods to simulate.
881 track_vars: list[strings]
882 List of variables that should be tracked when running the simulation.
883 For this agent, the options are 'PermShk', 'PrefShk', 'TranShk', 'aLvl', 'aNrm', 'bNrm', 'cNrm', 'mNrm', 'pLvl', and 'who_dies'.
885 PermShk is the agent's permanent income shock
887 PrefShk is the agent's preference shock
889 TranShk is the agent's transitory income shock
891 aLvl is the nominal asset level
893 aNrm is the normalized assets
895 bNrm is the normalized resources without this period's labor income
897 cNrm is the normalized consumption
899 mNrm is the normalized market resources
901 pLvl is the permanent income level
903 who_dies is the array of which agents died
904 aNrmInitMean: float
905 Mean of Log initial Normalized Assets.
906 aNrmInitStd: float
907 Std of Log initial Normalized Assets.
908 pLvlInitMean: float
909 Mean of Log initial permanent income.
910 pLvlInitStd: float
911 Std of Log initial permanent income.
912 PermGroFacAgg: float
913 Aggregate permanent income growth factor (The portion of PermGroFac attributable to aggregate productivity growth).
914 PerfMITShk: boolean
915 Do Perfect Foresight MIT Shock (Forces Newborns to follow solution path of the agent they replaced if True).
916 NewbornTransShk: boolean
917 Whether Newborns have transitory shock.
919 Attributes
920 ----------
921 solution: list[Consumer solution object]
922 Created by the :func:`.solve` method. Finite horizon models create a list with T_cycle+1 elements, for each period in the solution.
923 Infinite horizon solutions return a list with T_cycle elements for each period in the cycle.
925 For this model, cFunc is defined over normalized market resources and :math:`\eta`, cNrm = cFunc(mNrm, :math:`\eta`).
927 Visit :class:`HARK.ConsumptionSaving.ConsIndShockModel.ConsumerSolution` for more information about the solution.
928 history: Dict[Array]
929 Created by running the :func:`.simulate()` method.
930 Contains the variables in track_vars. Each item in the dictionary is an array with the shape (T_sim,AgentCount).
931 Visit :class:`HARK.core.AgentType.simulate` for more information.
932 """
934 IncShkDstn_defaults = PrefShockConsumerType_IncShkDstn_default
935 aXtraGrid_defaults = PrefShockConsumerType_aXtraGrid_default
936 PrefShkDstn_defaults = PrefShockConsumerType_PrefShkDstn_default
937 solving_defaults = PrefShockConsumerType_solving_default
938 simulation_defaults = PrefShockConsumerType_simulation_default
939 default_ = {
940 "params": PrefShockConsumerType_default,
941 "solver": solve_one_period_ConsPrefShock,
942 "model": "ConsMarkov.yaml",
943 }
945 shock_vars_ = IndShockConsumerType.shock_vars_ + ["PrefShk"]
946 time_vary_ = IndShockConsumerType.time_vary_ + ["PrefShkDstn"]
947 distributions = [
948 "IncShkDstn",
949 "PermShkDstn",
950 "TranShkDstn",
951 "kNrmInitDstn",
952 "pLvlInitDstn",
953 "PrefShkDstn",
954 ]
956 def pre_solve(self):
957 self.construct("solution_terminal")
959 def reset_rng(self):
960 """
961 Reset the RNG behavior of this type. This method is called automatically
962 by initialize_sim(), ensuring that each simulation run uses the same sequence
963 of random shocks; this is necessary for structural estimation to work.
964 This method extends IndShockConsumerType.reset_rng() to also reset elements
965 of PrefShkDstn.
967 Parameters
968 ----------
969 None
971 Returns
972 -------
973 None
974 """
975 IndShockConsumerType.reset_rng(self)
977 # Reset PrefShkDstn if it exists (it might not because reset_rng is called at init)
978 if hasattr(self, "PrefShkDstn"):
979 for dstn in self.PrefShkDstn:
980 dstn.reset()
982 def get_shocks(self):
983 """
984 Gets permanent and transitory income shocks for this period as well as preference shocks.
986 Parameters
987 ----------
988 None
990 Returns
991 -------
992 None
993 """
994 IndShockConsumerType.get_shocks(
995 self
996 ) # Get permanent and transitory income shocks
997 PrefShkNow = np.zeros(self.AgentCount) # Initialize shock array
998 for t in range(self.T_cycle):
999 these = t == self.t_cycle
1000 N = np.sum(these)
1001 if N > 0:
1002 PrefShkNow[these] = self.PrefShkDstn[t].draw(N)
1003 self.shocks["PrefShk"] = PrefShkNow
1005 def get_controls(self):
1006 """
1007 Calculates consumption for each consumer of this type using the consumption functions.
1009 Parameters
1010 ----------
1011 None
1013 Returns
1014 -------
1015 None
1016 """
1017 cNrmNow = np.zeros(self.AgentCount) + np.nan
1018 for t in range(self.T_cycle):
1019 these = t == self.t_cycle
1020 cNrmNow[these] = self.solution[t].cFunc(
1021 self.state_now["mNrm"][these], self.shocks["PrefShk"][these]
1022 )
1023 self.controls["cNrm"] = cNrmNow
1024 return None
1026 def calc_bounding_values(self): # pragma: nocover
1027 """
1028 Calculate human wealth plus minimum and maximum MPC in an infinite
1029 horizon model with only one period repeated indefinitely. Store results
1030 as attributes of self. Human wealth is the present discounted value of
1031 expected future income after receiving income this period, ignoring mort-
1032 ality. The maximum MPC is the limit of the MPC as m --> mNrmMin. The
1033 minimum MPC is the limit of the MPC as m --> infty.
1035 NOT YET IMPLEMENTED FOR THIS CLASS
1037 Parameters
1038 ----------
1039 None
1041 Returns
1042 -------
1043 None
1044 """
1045 raise NotImplementedError()
1047 def make_euler_error_func(self, mMax=100, approx_inc_dstn=True): # pragma: nocover
1048 """
1049 Creates a "normalized Euler error" function for this instance, mapping
1050 from market resources to "consumption error per dollar of consumption."
1051 Stores result in attribute eulerErrorFunc as an interpolated function.
1052 Has option to use approximate income distribution stored in self.IncShkDstn
1053 or to use a (temporary) very dense approximation.
1055 NOT YET IMPLEMENTED FOR THIS CLASS
1057 Parameters
1058 ----------
1059 mMax : float
1060 Maximum normalized market resources for the Euler error function.
1061 approx_inc_dstn : Boolean
1062 Indicator for whether to use the approximate discrete income distri-
1063 bution stored in self.IncShkDstn[0], or to use a very accurate
1064 discrete approximation instead. When True, uses approximation in
1065 IncShkDstn; when False, makes and uses a very dense approximation.
1067 Returns
1068 -------
1069 None
1071 """
1072 raise NotImplementedError()
1074 def check_conditions(self, verbose=None):
1075 raise NotImplementedError()
1077 def calc_limiting_values(self):
1078 raise NotImplementedError()
1081###############################################################################
1083# Specify default parameters that differ in "kinky preference" model compared to base PrefShockConsumerType
1084kinky_pref_different_params = {
1085 "Rboro": 1.20, # Interest factor on assets when borrowing, a < 0
1086 "Rsave": 1.02, # Interest factor on assets when saving, a > 0
1087 "BoroCnstArt": None, # Kinked R only matters if borrowing is allowed
1088}
1089KinkyPrefConsumerType_constructors_default = (
1090 PrefShockConsumerType_constructors_default.copy()
1091)
1092KinkyPrefConsumerType_IncShkDstn_default = (
1093 PrefShockConsumerType_IncShkDstn_default.copy()
1094)
1095KinkyPrefConsumerType_pLvlInitDstn_default = (
1096 PrefShockConsumerType_pLvlInitDstn_default.copy()
1097)
1098KinkyPrefConsumerType_kNrmInitDstn_default = (
1099 PrefShockConsumerType_kNrmInitDstn_default.copy()
1100)
1101KinkyPrefConsumerType_aXtraGrid_default = PrefShockConsumerType_aXtraGrid_default.copy()
1102KinkyPrefConsumerType_PrefShkDstn_default = (
1103 PrefShockConsumerType_PrefShkDstn_default.copy()
1104)
1105KinkyPrefConsumerType_solving_default = PrefShockConsumerType_solving_default.copy()
1106KinkyPrefConsumerType_solving_default["constructors"] = (
1107 KinkyPrefConsumerType_constructors_default
1108)
1109KinkyPrefConsumerType_simulation_default = (
1110 PrefShockConsumerType_simulation_default.copy()
1111)
1112KinkyPrefConsumerType_solving_default.update(kinky_pref_different_params)
1114# Make a dictionary to specify a "kinky preference" consumer
1115KinkyPrefConsumerType_default = {}
1116KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_IncShkDstn_default)
1117KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_aXtraGrid_default)
1118KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_PrefShkDstn_default)
1119KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_kNrmInitDstn_default)
1120KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_pLvlInitDstn_default)
1121KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_solving_default)
1122KinkyPrefConsumerType_default.update(KinkyPrefConsumerType_simulation_default)
1123init_kinky_pref = KinkyPrefConsumerType_default
1126class KinkyPrefConsumerType(PrefShockConsumerType, KinkedRconsumerType):
1127 r"""
1128 A consumer type based on PrefShockConsumerType, with different
1129 interest rates for saving (:math:`\mathsf{R}_{save}`) and borrowing
1130 (:math:`\mathsf{R}_{boro}`).
1132 Solver for this class is currently only compatible with linear spline interpolation.
1134 .. math::
1135 \newcommand{\CRRA}{\rho}
1136 \newcommand{\DiePrb}{\mathsf{D}}
1137 \newcommand{\PermGroFac}{\Gamma}
1138 \newcommand{\Rfree}{\mathsf{R}}
1139 \newcommand{\DiscFac}{\beta}
1140 \begin{align*}
1141 v_t(m_t,\eta_t) &= \max_{c_t} \eta_{t} u(c_t) + \DiscFac (1-\DiePrb_{t+1}) \mathbb{E}_{t} \left[(\PermGroFac_{t+1}\psi_{t+1})^{1-\CRRA} v_{t+1}(m_{t+1},\eta_{t+1}) \right], \\
1142 a_t &= m_t - c_t, \\
1143 a_t &\geq \underline{a}, \\
1144 m_{t+1} &= \Rfree_t/(\PermGroFac_{t+1} \psi_{t+1}) a_t + \theta_{t+1}, \\
1145 \Rfree_t &= \begin{cases}
1146 \Rfree_{boro} & \text{if } a_t < 0\\
1147 \Rfree_{save} & \text{if } a_t \geq 0,
1148 \end{cases}\\
1149 \Rfree_{boro} &> \Rfree_{save}, \\
1150 (\psi_{t+1},\theta_{t+1},\eta_{t+1}) &\sim F_{t+1}, \\
1151 \mathbb{E}[\psi]=\mathbb{E}[\theta] &= 1. \\
1152 u(c) &= \frac{c^{1-\CRRA}}{1-\CRRA} \\
1153 \end{align*}
1156 Constructors
1157 ------------
1158 IncShkDstn: Constructor, :math:`\psi`, :math:`\theta`
1159 The agent's income shock distributions.
1161 It's default constructor is :func:`HARK.Calibration.Income.IncomeProcesses.construct_lognormal_income_process_unemployment`
1162 aXtraGrid: Constructor
1163 The agent's asset grid.
1165 It's default constructor is :func:`HARK.utilities.make_assets_grid`
1166 PrefShkDstn: Constructor, :math:`\eta`
1167 The agent's preference shock distributions.
1169 It's default constuctor is :func:`HARK.ConsumptionSaving.ConsPrefShockModel.make_lognormal_PrefShkDstn`
1171 Solving Parameters
1172 ------------------
1173 cycles: int
1174 0 specifies an infinite horizon model, 1 specifies a finite model.
1175 T_cycle: int
1176 Number of periods in the cycle for this agent type.
1177 CRRA: float, :math:`\rho`
1178 Coefficient of Relative Risk Aversion.
1179 Rfree: float or list[float], time varying, :math:`\mathsf{R}`
1180 Risk Free interest rate. Pass a list of floats to make Rfree time varying.
1181 Rboro: float, :math:`\mathsf{R}_{boro}`
1182 Risk Free interest rate when assets are negative.
1183 Rsave: float, :math:`\mathsf{R}_{save}`
1184 Risk Free interest rate when assets are positive.
1185 DiscFac: float, :math:`\beta`
1186 Intertemporal discount factor.
1187 LivPrb: list[float], time varying, :math:`1-\mathsf{D}`
1188 Survival probability after each period.
1189 PermGroFac: list[float], time varying, :math:`\Gamma`
1190 Permanent income growth factor.
1191 BoroCnstArt: float, :math:`\underline{a}`
1192 The minimum Asset/Perminant Income ratio, None to ignore.
1193 vFuncBool: bool
1194 Whether to calculate the value function during solution.
1195 CubicBool: bool
1196 Whether to use cubic spline interpoliation.
1198 Simulation Parameters
1199 ---------------------
1200 AgentCount: int
1201 Number of agents of this kind that are created during simulations.
1202 T_age: int
1203 Age after which to automatically kill agents, None to ignore.
1204 T_sim: int, required for simulation
1205 Number of periods to simulate.
1206 track_vars: list[strings]
1207 List of variables that should be tracked when running the simulation.
1208 For this agent, the options are 'PermShk', 'PrefShk', 'TranShk', 'aLvl', 'aNrm', 'bNrm', 'cNrm', 'mNrm', 'pLvl', and 'who_dies'.
1210 PermShk is the agent's permanent income shock
1212 PrefShk is the agent's preference shock
1214 TranShk is the agent's transitory income shock
1216 aLvl is the nominal asset level
1218 aNrm is the normalized assets
1220 bNrm is the normalized resources without this period's labor income
1222 cNrm is the normalized consumption
1224 mNrm is the normalized market resources
1226 pLvl is the permanent income level
1228 who_dies is the array of which agents died
1229 aNrmInitMean: float
1230 Mean of Log initial Normalized Assets.
1231 aNrmInitStd: float
1232 Std of Log initial Normalized Assets.
1233 pLvlInitMean: float
1234 Mean of Log initial permanent income.
1235 pLvlInitStd: float
1236 Std of Log initial permanent income.
1237 PermGroFacAgg: float
1238 Aggregate permanent income growth factor (The portion of PermGroFac attributable to aggregate productivity growth).
1239 PerfMITShk: boolean
1240 Do Perfect Foresight MIT Shock (Forces Newborns to follow solution path of the agent they replaced if True).
1241 NewbornTransShk: boolean
1242 Whether Newborns have transitory shock.
1244 Attributes
1245 ----------
1246 solution: list[Consumer solution object]
1247 Created by the :func:`.solve` method. Finite horizon models create a list with T_cycle+1 elements, for each period in the solution.
1248 Infinite horizon solutions return a list with T_cycle elements for each period in the cycle.
1250 For this model, cFunc is defined over normalized market resources and :math:`\eta`, cNrm = cFunc(mNrm, :math:`\eta`).
1252 Visit :class:`HARK.ConsumptionSaving.ConsIndShockModel.ConsumerSolution` for more information about the solution.
1253 history: Dict[Array]
1254 Created by running the :func:`.simulate()` method.
1255 Contains the variables in track_vars. Each item in the dictionary is an array with the shape (T_sim,AgentCount).
1256 Visit :class:`HARK.core.AgentType.simulate` for more information.
1257 """
1259 IncShkDstn_defaults = KinkyPrefConsumerType_IncShkDstn_default
1260 aXtraGrid_defaults = KinkyPrefConsumerType_aXtraGrid_default
1261 PrefShkDstn_defaults = KinkyPrefConsumerType_PrefShkDstn_default
1262 solving_defaults = KinkyPrefConsumerType_solving_default
1263 simulation_defaults = KinkyPrefConsumerType_simulation_default
1264 default_ = {
1265 "params": KinkyPrefConsumerType_default,
1266 "solver": solve_one_period_ConsKinkyPref,
1267 }
1269 time_inv_ = IndShockConsumerType.time_inv_ + ["Rboro", "Rsave"]
1270 distributions = [
1271 "IncShkDstn",
1272 "PermShkDstn",
1273 "TranShkDstn",
1274 "kNrmInitDstn",
1275 "pLvlInitDstn",
1276 "PrefShkDstn",
1277 ]
1279 def pre_solve(self):
1280 self.construct("solution_terminal")
1282 def get_Rfree(self): # Specify which get_Rfree to use
1283 return KinkedRconsumerType.get_Rfree(self)