Skip to content

Commit

Permalink
Added hyperlinks to referenced sections of the html rendered version …
Browse files Browse the repository at this point in the history
…of the BST paper

Note, hyperlinks were only added to the sections of the code defining the solvers. Matt has a pull request which has already begun to make similar changes to the section of the code defining the agent types (specifically, the "check_conditions" and related methods which make direct references to the paper)
  • Loading branch information
dedwar65 committed Aug 13, 2023
1 parent e1b5d35 commit 446085d
Showing 1 changed file with 59 additions and 39 deletions.
98 changes: 59 additions & 39 deletions HARK/ConsumptionSaving/ConsIndShockModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ def append_solution(self, new_solution):
# === Classes and functions that solve consumption-saving models ===
# =====================================================================

# For theory, see hyperlink targets to expressions in
# url=https://econ-ark.github.io/BufferStockTheory/BufferStockTheory3.html
# For example, the hyperlink to the relevant section of the paper
# would be referenced as: [url]#Name-of-the-section

class ConsPerfForesightSolver(MetricObject):
"""
Expand Down Expand Up @@ -309,6 +313,10 @@ def def_value_funcs(self):
def make_cFunc_PF(self):
"""
Makes the (linear) consumption function for this period.
The conditions for which a solution to the unconstrained, perfect
foresight version of the problem are discussed at:
[url]#PF-Unconstrained-Solution
Parameters
----------
Expand All @@ -328,8 +336,8 @@ def make_cFunc_PF(self):
self.hNrmNow = (self.PermGroFac / self.Rfree) * (self.solution_next.hNrm + 1.0)

# Calculate the lower bound of the marginal propensity to consume
PatFac = ((self.Rfree * self.DiscFacEff) ** (1.0 / self.CRRA)) / self.Rfree
self.MPCmin = 1.0 / (1.0 + PatFac / self.solution_next.MPCmin)
RPFac = ((self.Rfree * self.DiscFacEff) ** (1.0 / self.CRRA)) / self.Rfree
self.MPCmin = 1.0 / (1.0 + RPFac / self.solution_next.MPCmin)

# Extract the discrete kink points in next period's consumption function;
# don't take the last one, as it only defines the extrapolation and is not a kink.
Expand Down Expand Up @@ -403,10 +411,10 @@ def make_cFunc_PF(self):
def add_mNrmTrg(self, solution):
"""
Finds value of (normalized) market resources m at which individual consumer
expects m not to change.
This will exist if the GICNrm holds.
expects m not to change. A *unique* normalized level of market resources will
exist for individuals when GICMod holds. Further discussed at:
https://econ-ark.github.io/BufferStockTheory#Unique-Stable-Points
[url]#Unique-Stable-Points
Parameters
----------
Expand Down Expand Up @@ -466,9 +474,9 @@ def add_mNrmStE(self, solution):
Finds market resources ratio at which 'balanced growth' is expected.
This is the m ratio such that the expected growth rate of the M level
matches the expected growth rate of permanent income. This value does
not exist if the Growth Impatience Condition does not hold.
not exist if the Growth Impatience Condition does not hold:
https://econ-ark.github.io/BufferStockTheory#Unique-Stable-Points
[url]#pseudo-steady-state
Parameters
----------
Expand Down Expand Up @@ -543,8 +551,10 @@ def add_stable_points(self, solution):
# 1. There is a non-degenerate SS for constrained PF model if GICRaw holds.
# Therefore
# Check if (GICRaw and BoroCnstArt) and if so compute them both
thorn = (self.Rfree * self.DiscFacEff) ** (1 / self.CRRA)
GICRaw = 1 > thorn / self.PermGroFac

APFac = (self.Rfree * self.DiscFacEff) ** (1 / self.CRRA)

GICRaw = 1 > APFac / self.PermGroFac
if self.BoroCnstArt is not None and GICRaw:
solution = self.add_mNrmStE(solution)
solution = self.add_mNrmTrg(solution)
Expand Down Expand Up @@ -666,6 +676,11 @@ def set_and_update_values(self, solution_next, IncShkDstn, LivPrb, DiscFac):
(etc), the probability of getting the worst income shock next period,
the patience factor, human wealth, and the bounding MPCs.
The exposition of the bounding MPC's for the problem incorporating uncertainty
can be found at:
[url]#Bounds-for-the-Consumption-Functions
Parameters
----------
solution_next : ConsumerSolution
Expand Down Expand Up @@ -706,8 +721,8 @@ def set_and_update_values(self, solution_next, IncShkDstn, LivPrb, DiscFac):
self.vFuncNext = solution_next.vFunc

# Update the bounding MPCs and PDV of human wealth:
self.PatFac = ((self.Rfree * self.DiscFacEff) ** (1.0 / self.CRRA)) / self.Rfree
self.MPCminNow = 1.0 / (1.0 + self.PatFac / solution_next.MPCmin)
self.RPFac = ((self.Rfree * self.DiscFacEff) ** (1.0 / self.CRRA)) / self.Rfree
self.MPCminNow = 1.0 / (1.0 + self.RPFac / solution_next.MPCmin)
self.Ex_IncNext = np.dot(
self.ShkPrbsNext, self.TranShkValsNext * self.PermShkValsNext
)
Expand All @@ -717,7 +732,7 @@ def set_and_update_values(self, solution_next, IncShkDstn, LivPrb, DiscFac):
self.MPCmaxNow = 1.0 / (
1.0
+ (self.WorstIncPrb ** (1.0 / self.CRRA))
* self.PatFac
* self.RPFac
/ solution_next.MPCmax
)

Expand All @@ -728,6 +743,10 @@ def def_BoroCnst(self, BoroCnstArt):
"""
Defines the constrained portion of the consumption function as cFuncNowCnst,
an attribute of self. Uses the artificial and natural borrowing constraints.
The solution to the perfect foresight (artifically) constrained version of the
problem is discussed at:
[url]#Constrained-Solution
Parameters
----------
Expand Down Expand Up @@ -1013,17 +1032,17 @@ def add_stable_points(self, solution):
"""

# 0. Check if GICRaw holds. If so, then mNrmStE will exist. So, compute it.
# 1. Check if GICNrm holds. If so, then mNrmTrg will exist. So, compute it.
# 1. Check if GICMod holds. If so, then mNrmTrg will exist. So, compute it.

thorn = (self.Rfree * self.DiscFacEff) ** (1 / self.CRRA)
APFac = (self.Rfree * self.DiscFacEff) ** (1 / self.CRRA)

GPFRaw = thorn / self.PermGroFac
GPFRaw = APFac / self.PermGroFac
self.GPFRaw = GPFRaw
GPFNrm = (
thorn / self.PermGroFac / np.dot(1 / self.PermShkValsNext, self.ShkPrbsNext)
APFac / self.PermGroFac / np.dot(1 / self.PermShkValsNext, self.ShkPrbsNext)
)
self.GPFNrm = GPFNrm
GICRaw = 1 > thorn / self.PermGroFac
GICRaw = 1 > APFac / self.PermGroFac
self.GICRaw = GICRaw
GICNrm = 1 > GPFNrm
self.GICNrm = GICNrm
Expand Down Expand Up @@ -1507,10 +1526,10 @@ def prepare_to_calc_EndOfPrdvP(self):
# Recalculate the minimum MPC and human wealth using the interest factor on saving.
# This overwrites values from set_and_update_values, which were based on Rboro instead.
if KinkBool:
PatFacTop = (
RPFac_save = (
(self.Rsave * self.DiscFacEff) ** (1.0 / self.CRRA)
) / self.Rsave
self.MPCminNow = 1.0 / (1.0 + PatFacTop / self.solution_next.MPCmin)
self.MPCminNow = 1.0 / (1.0 + RPFac_save / self.solution_next.MPCmin)
self.hNrmNow = (
self.PermGroFac
/ self.Rsave
Expand Down Expand Up @@ -1943,11 +1962,11 @@ def check_AIC(self, verbose=None):
name = "AIC"

def test(agent):
return agent.thorn < 1
return agent.APFac < 1

messages = {
True: "The value of the Absolute Patience Factor (APF) for the supplied parameter values satisfies the Absolute Impatience Condition.",
False: "The given type violates the Absolute Impatience Condition with the supplied parameter values; the APF is {0.thorn}",
False: "The given type violates the Absolute Impatience Condition with the supplied parameter values; the APF is {0.APFac}",
}
verbose_messages = {
True: " Because the APF < 1, the absolute amount of consumption is expected to fall over time.",
Expand All @@ -1963,7 +1982,7 @@ def check_GICRaw(self, verbose=None):
name = "GICRaw"
# url = "https://econ-ark.github.io/BufferStockTheory/BufferStockTheory3.html#GICRaw"

self.GPFRaw = self.thorn / self.PermGroFac[0]
self.GPFRaw = self.APFac / self.PermGroFac[0]

def test(agent):
return agent.GPFRaw < 1
Expand All @@ -1985,7 +2004,7 @@ def check_RIC(self, verbose=None):
Evaluate and report on the Return Impatience Condition
"""

self.RPF = self.thorn / self.Rfree
self.RPF = self.APFac / self.Rfree

name = "RIC"

Expand All @@ -2010,7 +2029,7 @@ def check_FHWC(self, verbose=None):
"""

self.FHWF = self.PermGroFac[0] / self.Rfree
self.cNrmPDV = 1.0 / (1.0 - self.thorn / self.Rfree)
self.cNrmPDV = 1.0 / (1.0 - self.APFac / self.Rfree)

name = "FHWC"

Expand Down Expand Up @@ -2058,7 +2077,7 @@ def check_conditions(self, verbose=None):
if self.cycles != 0 or self.T_cycle > 1:
return

self.thorn = (self.Rfree * self.DiscFac * self.LivPrb[0]) ** (1 / self.CRRA)
self.APFac = (self.Rfree * self.DiscFac * self.LivPrb[0]) ** (1 / self.CRRA)

verbose = self.verbose if verbose is None else verbose
self.check_AIC(verbose)
Expand Down Expand Up @@ -2332,14 +2351,14 @@ def calc_bounding_values(self):
temp = self.PermGroFac[0] * PermShkMinNext / self.Rfree
BoroCnstNat = -TranShkMinNext * temp / (1.0 - temp)

PatFac = (self.DiscFac * self.LivPrb[0] * self.Rfree) ** (
RPFac = (self.DiscFac * self.LivPrb[0] * self.Rfree) ** (
1.0 / self.CRRA
) / self.Rfree
if BoroCnstNat < self.BoroCnstArt:
MPCmax = 1.0 # if natural borrowing constraint is overridden by artificial one, MPCmax is 1
else:
MPCmax = 1.0 - WorstIncPrb ** (1.0 / self.CRRA) * PatFac
MPCmin = 1.0 - PatFac
MPCmax = 1.0 - WorstIncPrb ** (1.0 / self.CRRA) * RPFac
MPCmin = 1.0 - RPFac

# Store the results as attributes of self
self.hNrm = hNrm
Expand Down Expand Up @@ -3081,12 +3100,13 @@ def pre_solve(self):
def check_GICNrm(self, verbose=None):
"""
Check Individual Growth Patience Factor.
# [url]/#GPFacModDefn
"""
self.GPFNrm = self.thorn / (
self.GPFNrm = self.APFac / (
self.PermGroFac[0] * self.InvEx_PermShkInv
) # [url]/#GICRawI
)

name = "GICRaw"
name = "GICNrm"

def test(agent):
return agent.GPFNrm <= 1
Expand Down Expand Up @@ -3231,13 +3251,13 @@ def check_conditions(self, verbose=None):
self.PermGroFac[0] * self.InvEx_PermShkInv
) # [url]/#PGroAdj

self.thorn = (self.Rfree * self.DiscFac) ** (1 / self.CRRA)
self.APFac = (self.Rfree * self.DiscFac) ** (1 / self.CRRA)

# self.Ex_RNrm = self.Rfree*Ex_PermShkInv/(self.PermGroFac[0]*self.LivPrb[0])
self.GPFRaw = self.thorn / (self.PermGroFac[0]) # [url]/#GPF
self.GPFRaw = self.APFac / (self.PermGroFac[0]) # [url]/#GPF
# Lower bound of aggregate wealth growth if all inheritances squandered

self.GPFAggLivPrb = self.thorn * self.LivPrb[0] / self.PermGroFac[0]
self.GPFAggLivPrb = self.APFac * self.LivPrb[0] / self.PermGroFac[0]

self.DiscFacGPFRawMax = ((self.PermGroFac[0]) ** (self.CRRA)) / (
self.Rfree
Expand Down Expand Up @@ -3270,7 +3290,7 @@ def check_conditions(self, verbose=None):
_log.warning("GPFRaw = %2.6f " % (self.GPFRaw))
_log.warning("GPFNrm = %2.6f " % (self.GPFNrm))
_log.warning("GPFAggLivPrb = %2.6f " % (self.GPFAggLivPrb))
_log.warning("Thorn = APF = %2.6f " % (self.thorn))
_log.warning("APFac = APF = %2.6f " % (self.APFac))
_log.warning("PermGroFacAdj = %2.6f " % (self.PermGroFacAdj))
_log.warning("uInvEpShkuInv = %2.6f " % (self.uInvEpShkuInv))
_log.warning("VAF = %2.6f " % (self.VAF))
Expand Down Expand Up @@ -3669,17 +3689,17 @@ def calc_bounding_values(self):
temp = self.PermGroFac[0] * PermShkMinNext / self.Rboro
BoroCnstNat = -TranShkMinNext * temp / (1.0 - temp)

PatFacTop = (self.DiscFac * self.LivPrb[0] * self.Rsave) ** (
RPFac_save = (self.DiscFac * self.LivPrb[0] * self.Rsave) ** (
1.0 / self.CRRA
) / self.Rsave
PatFacBot = (self.DiscFac * self.LivPrb[0] * self.Rboro) ** (
RPFac_boro = (self.DiscFac * self.LivPrb[0] * self.Rboro) ** (
1.0 / self.CRRA
) / self.Rboro
if BoroCnstNat < self.BoroCnstArt:
MPCmax = 1.0 # if natural borrowing constraint is overridden by artificial one, MPCmax is 1
else:
MPCmax = 1.0 - WorstIncPrb ** (1.0 / self.CRRA) * PatFacBot
MPCmin = 1.0 - PatFacTop
MPCmax = 1.0 - WorstIncPrb ** (1.0 / self.CRRA) * RPFac_boro
MPCmin = 1.0 - RPFac_save

# Store the results as attributes of self
self.hNrm = hNrm
Expand Down

0 comments on commit 446085d

Please sign in to comment.