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