Skip to content

Commit

Permalink
Move methods to PerfForesightConsumerType
Browse files Browse the repository at this point in the history
I didn't realize that the unit tests include the "stable points" for a perfect foresight model. This commit moves the methods from IndShockConsumerType to PerfForesightConsumerType.
  • Loading branch information
mnwhite committed Sep 15, 2023
1 parent 5625914 commit c687ba0
Showing 1 changed file with 95 additions and 83 deletions.
178 changes: 95 additions & 83 deletions HARK/ConsumptionSaving/ConsIndShockModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,12 @@ def __init__(self, verbose=1, quiet=False, **kwds):
self.update_Rfree() # update interest rate if time varying

def pre_solve(self):
'''
Method that is run automatically just before solution by backward iteration.
Solves the (trivial) terminal period and does a quick check on the borrowing
constraint and MaxKinks attribute (only relevant in constrained, infinite
horizon problems).
'''
self.update_solution_terminal() # Solve the terminal period problem

# Fill in BoroCnstArt and MaxKinks if they're not specified or are irrelevant.
Expand All @@ -1412,6 +1418,23 @@ def pre_solve(self):
"PerfForesightConsumerType requires the attribute MaxKinks to be specified when BoroCnstArt is not None and cycles == 0."
)
)

def post_solve(self):
"""
Method that is run automatically at the end of a call to solve. Here, it
simply calls calc_stable_points() if appropriate: an infinite horizon
problem with a single repeated period in its cycle.
Parameters
----------
None
Returns
-------
None
"""
if (self.cycles == 0) and (self.T_cycle == 1):
self.calc_stable_points()

def check_restrictions(self):
"""
Expand Down Expand Up @@ -1834,6 +1857,8 @@ def calc_limiting_values(self):
MPCmin : Limiting minimum MPC as market resources go to infinity
MPCmax : Limiting maximum MPC as market resources approach minimum level.
hNrm : Human wealth divided by permanent income.
Delta_mNrm_ZeroFunc : Linear consumption function where expected change in market resource ratio is zero
BalGroFunc : Linear consumption function where the level of market resources grows at the same rate as permanent income
Returns
-------
Expand All @@ -1858,6 +1883,14 @@ def calc_limiting_values(self):
else:
aux_dict['hNrm'] = np.inf

# Generate the "Delta m = 0" function, which is used to find target market resources
Ex_Rnrm = self.Rfree / self.PermGroFac[0]
aux_dict['Delta_mNrm_ZeroFunc'] = lambda m : (1. - 1./Ex_Rnrm) * m + 1./Ex_Rnrm

# Generate the "E[M_tp1 / M_t] = G" function, which is used to find balanced growth market resources
PF_Rnrm = self.Rfree / self.PermGroFac[0]
aux_dict['BalGroFunc'] = lambda m : (1. - 1./PF_Rnrm) * m + 1./PF_Rnrm

self.bilt = aux_dict

def check_conditions(self, verbose=None):
Expand Down Expand Up @@ -1972,6 +2005,66 @@ def check_conditions(self, verbose=None):

if not self.quiet:
_log.info(self.bilt['conditions_report'])


def calc_stable_points(self):
"""
If the problem is one that satisfies the conditions required for target ratios of different
variables to permanent income to exist, and has been solved to within the self-defined
tolerance, this method calculates the target values of market resources.
Parameters
----------
None
Returns
-------
None
"""
infinite_horizon = self.cycles == 0
single_period = self.T_cycle = 1
if not infinite_horizon:
_log.warning("The calc_stable_points method works only for infinite horizon models.")
return
if not single_period:
_log.warning("The calc_stable_points method works only with a single infinitely repeated period.")
return
if not hasattr(self, 'conditions'):
_log.warning("The calc_limiting_values method must be run before the calc_stable_points method.")
return
if not hasattr(self, 'solution'):
_log.warning("The solve method must be run before the calc_stable_points method.")
return

# Extract balanced growth and delta m_t+1 = 0 functions
BalGroFunc = self.bilt['BalGroFunc']
Delta_mNrm_ZeroFunc = self.bilt['Delta_mNrm_ZeroFunc']

# If the GICRaw holds, then there is a balanced growth market resources ratio
if self.conditions['GICRaw']:
cFunc = self.solution[0].cFunc
func_to_zero = lambda m : BalGroFunc(m) - cFunc(m)
m0 = 1.0
try:
mNrmStE = newton(func_to_zero, m0)
except:
mNrmStE = np.nan

# A target level of assets *might* exist even if the GICMod fails, so check no matter what
func_to_zero = lambda m : Delta_mNrm_ZeroFunc(m) - cFunc(m)
m0 = 1.0 if np.isnan(mNrmStE) else mNrmStE
try:
mNrmTrg = newton(func_to_zero, m0, maxiter=200)
except:
mNrmTrg = np.nan
else:
mNrmStE = np.nan
mNrmTrg = np.nan

self.solution[0].mNrmStE = mNrmStE
self.solution[0].mNrmTrg = mNrmTrg
self.bilt['mNrmStE'] = mNrmStE
self.bilt['mNrmTrg'] = mNrmTrg


# Make a dictionary to specify an idiosyncratic income shocks consumer
Expand Down Expand Up @@ -2124,23 +2217,6 @@ def reset_rng(self):
if hasattr(self, "IncShkDstn"):
for dstn in self.IncShkDstn:
dstn.reset()

def post_solve(self):
"""
Method that is run automatically at the end of a call to solve. Here, it
simply calls calc_stable_points() if appropriate: an infinite horizon
problem with a single repeated period in its cycle.
Parameters
----------
None
Returns
-------
None
"""
if (self.cycles == 0) and (self.T_cycle == 1):
self.calc_stable_points()

def get_shocks(self):
"""
Expand Down Expand Up @@ -2883,7 +2959,6 @@ def J_from_F(F):
return J_C, J_A



def make_euler_error_func(self, mMax=100, approx_inc_dstn=True):
"""
Creates a "normalized Euler error" function for this instance, mapping
Expand Down Expand Up @@ -3054,7 +3129,7 @@ def calc_limiting_values(self):
hNrm : Human wealth divided by permanent income.
ELogPermShk : Expected log permanent income shock
WorstPrb : Probability of worst income shock realization
Delta_mNrm_ZeroFunc : Linear consumption function where expected change in market resource ratio is zero
Delta_mNrm_ZeroFunc : Linear locus where expected change in market resource ratio is zero
BalGroFunc : Linear consumption function where the level of market resources grows at the same rate as permanent income
Returns
Expand Down Expand Up @@ -3131,13 +3206,10 @@ def calc_limiting_values(self):
aux_dict['MPCmax'] = MPCmax

# Generate the "Delta m = 0" function, which is used to find target market resources
# This overwrites the function generated by the perfect foresight version
Ex_Rnrm = self.Rfree / self.PermGroFac[0] * Ex_PermShkInv
aux_dict['Delta_mNrm_ZeroFunc'] = lambda m : (1. - 1./Ex_Rnrm) * m + 1./Ex_Rnrm

# Generate the "E[M_tp1 / M_t] = G" function, which is used to find balanced growth market resources
PF_Rnrm = self.Rfree / self.PermGroFac[0]
aux_dict['BalGroFunc'] = lambda m : (1. - 1./PF_Rnrm) * m + 1./PF_Rnrm

self.bilt = aux_dict


Expand Down Expand Up @@ -3361,66 +3433,6 @@ def check_conditions(self, verbose=None):
_log.info(self.bilt['conditions_report'])


def calc_stable_points(self):
"""
If the problem is one that satisfies the conditions required for target ratios of different
variables to permanent income to exist, and has been solved to within the self-defined
tolerance, this method calculates the target values of market resources.
Parameters
----------
None
Returns
-------
None
"""
infinite_horizon = self.cycles == 0
single_period = self.T_cycle = 1
if not infinite_horizon:
_log.warning("The calc_stable_points method works only for infinite horizon models.")
return
if not single_period:
_log.warning("The calc_stable_points method works only with a single infinitely repeated period.")
return
if not hasattr(self, 'conditions'):
_log.warning("The calc_limiting_values method must be run before the calc_stable_points method.")
return
if not hasattr(self, 'solution'):
_log.warning("The solve method must be run before the calc_stable_points method.")
return

# Extract balanced growth and delta m_t+1 = 0 functions
BalGroFunc = self.bilt['BalGroFunc']
Delta_mNrm_ZeroFunc = self.bilt['Delta_mNrm_ZeroFunc']

# If the GICRaw holds, then there is a balanced growth market resources ratio
if self.conditions['GICRaw']:
cFunc = self.solution[0].cFunc
func_to_zero = lambda m : BalGroFunc(m) - cFunc(m)
m0 = 1.0
try:
mNrmStE = newton(func_to_zero, m0)
except:
mNrmStE = np.nan

# A target level of assets *might* exist even if the GICMod fails, so check no matter what
func_to_zero = lambda m : Delta_mNrm_ZeroFunc(m) - cFunc(m)
m0 = 1.0 if np.isnan(mNrmStE) else mNrmStE
try:
mNrmTrg = newton(func_to_zero, m0, maxiter=200)
except:
mNrmTrg = np.nan
else:
mNrmStE = np.nan
mNrmTrg = np.nan

self.solution[0].mNrmStE = mNrmStE
self.solution[0].mNrmTrg = mNrmTrg
self.bilt['mNrmStE'] = mNrmStE
self.bilt['mNrmTrg'] = mNrmTrg


# = Functions for generating discrete income processes and
# simulated income shocks =
# ========================================================
Expand Down

0 comments on commit c687ba0

Please sign in to comment.