Skip to content

Commit

Permalink
Merge branch 'econ-ark:master' into distribution/updates
Browse files Browse the repository at this point in the history
  • Loading branch information
alanlujan91 authored Nov 7, 2024
2 parents d8d89d2 + 894d53e commit 9d6fcf5
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 188 deletions.
14 changes: 5 additions & 9 deletions HARK/ConsumptionSaving/ConsBequestModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,8 @@ def make_warmglow_portfolio_solution_terminal(

class BequestWarmGlowConsumerType(IndShockConsumerType):
r"""
A consumer type with idiosyncratic shocks to permanent and transitory income.
Their problem is defined by a sequence of income distributions, survival probabilities
(:math:`1-\mathsf{D}`), and permanent income growth rates (:math:`\Gamma`), as well
as time invariant values for risk aversion (:math:`\rho`), discount factor (:math:`\beta`),
the interest rate (:math:`\mathsf{R}`), the grid of end-of-period assets, and an artificial
borrowing constraint (:math:`\underline{a}`).
A consumer type with based on IndShockConsumerType, with an additional bequest motive.
They gain utility for any wealth they leave when they die, according to a Stone-Geary utility.
.. math::
\newcommand{\CRRA}{\rho}
Expand All @@ -295,7 +291,7 @@ class BequestWarmGlowConsumerType(IndShockConsumerType):
(\psi_{t+1},\theta_{t+1}) &\sim F_{t+1}, \\
\mathbb{E}[\psi]=\mathbb{E}[\theta] &= 1, \\
u(c) &= \frac{c^{1-\CRRA}}{1-\CRRA} \\
u_{Beq} (a) = \textbf{BeqFac} \frac{(a+\textbf{BeqShift})^{1-\CRRA_{Beq}}}{1-\CRRA_{Beq}}
u_{Beq} (a) &= \textbf{BeqFac} \frac{(a+\textbf{BeqShift})^{1-\CRRA_{Beq}}}{1-\CRRA_{Beq}} \\
\end{align*}
Expand Down Expand Up @@ -1277,7 +1273,7 @@ def calc_EndOfPrd_v(S, a, z):

class BequestWarmGlowPortfolioType(PortfolioConsumerType):
r"""
A consumer type with based on IndShockConsumerType, with an additional bequest motive.
A consumer type with based on PortfolioConsumerType, with an additional bequest motive.
They gain utility for any wealth they leave when they die, according to a Stone-Geary utility.
.. math::
Expand All @@ -1300,7 +1296,7 @@ class BequestWarmGlowPortfolioType(PortfolioConsumerType):
(\psi_{t+1},\theta_{t+1},\phi_{t+1},p_t) &\sim F_{t+1}, \\
\mathbb{E}[\psi]=\mathbb{E}[\theta] &= 1. \\
u(c) &= \frac{c^{1-\CRRA}}{1-\CRRA} \\
u_{Beq} (a) = \textbf{BeqFac} \frac{(a+\textbf{BeqShift})^{1-\CRRA_{Beq}}}{1-\CRRA_{Beq}}
u_{Beq} (a) &= \textbf{BeqFac} \frac{(a+\textbf{BeqShift})^{1-\CRRA_{Beq}}}{1-\CRRA_{Beq}} \\
\end{align*}
Expand Down
2 changes: 1 addition & 1 deletion HARK/ConsumptionSaving/ConsIndShockModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2798,7 +2798,7 @@ class KinkedRconsumerType(IndShockConsumerType):
\end{cases}\\
\Rfree_{boro} &> \Rfree_{save}, \\
(\psi_{t+1},\theta_{t+1}) &\sim F_{t+1}, \\
\mathbb{E}[\psi]=\mathbb{E}[\theta] &= 1.
\mathbb{E}[\psi]=\mathbb{E}[\theta] &= 1.\\
u(c) &= \frac{c^{1-\CRRA}}{1-\CRRA} \\
\end{align*}
Expand Down
157 changes: 147 additions & 10 deletions HARK/ConsumptionSaving/ConsWealthPortfolioModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import numpy as np
from HARK.ConsumptionSaving.ConsPortfolioModel import (
PortfolioConsumerType,
init_portfolio,
PortfolioSolution,
make_portfolio_solution_terminal,
)
from HARK.distributions import expected
from HARK.interpolation import (
Expand All @@ -14,11 +14,22 @@
MargValueFuncCRRA,
ValueFuncCRRA,
)
from HARK.Calibration.Assets.AssetProcesses import (
make_lognormal_RiskyDstn,
combine_IncShkDstn_and_RiskyDstn,
calc_ShareLimit_for_CRRA,
)
from HARK.Calibration.Income.IncomeProcesses import (
construct_lognormal_income_process_unemployment,
get_PermShkDstn_from_IncShkDstn,
get_TranShkDstn_from_IncShkDstn,
)
from HARK.ConsumptionSaving.ConsRiskyAssetModel import (
make_simple_ShareGrid,
)
from HARK.rewards import UtilityFuncCRRA
from HARK.utilities import NullFunc
from HARK.utilities import NullFunc, make_assets_grid


import numpy as np
from HARK.interpolation import LinearInterp


Expand Down Expand Up @@ -100,6 +111,129 @@ def __call__(self, omega):
return np.nan_to_num(chi)


# Trivial constructor function
def make_ChiFromOmega_function(CRRA, WealthShare, ChiFromOmega_N, ChiFromOmega_bound):
return ChiFromOmegaFunction(
CRRA, WealthShare, N=ChiFromOmega_N, z_bound=ChiFromOmega_bound
)


###############################################################################

# Make a dictionary of constructors for the wealth-in-utility portfolio choice consumer type
WealthPortfolioConsumerType_constructors_default = {
"IncShkDstn": construct_lognormal_income_process_unemployment,
"PermShkDstn": get_PermShkDstn_from_IncShkDstn,
"TranShkDstn": get_TranShkDstn_from_IncShkDstn,
"aXtraGrid": make_assets_grid,
"RiskyDstn": make_lognormal_RiskyDstn,
"ShockDstn": combine_IncShkDstn_and_RiskyDstn,
"ShareLimit": calc_ShareLimit_for_CRRA,
"ShareGrid": make_simple_ShareGrid,
"ChiFunc": make_ChiFromOmega_function,
"solution_terminal": make_portfolio_solution_terminal,
}

# Default parameters to make IncShkDstn using construct_lognormal_income_process_unemployment
WealthPortfolioConsumerType_IncShkDstn_default = {
"PermShkStd": [0.1], # Standard deviation of log permanent income shocks
"PermShkCount": 7, # Number of points in discrete approximation to permanent income shocks
"TranShkStd": [0.1], # Standard deviation of log transitory income shocks
"TranShkCount": 7, # Number of points in discrete approximation to transitory income shocks
"UnempPrb": 0.05, # Probability of unemployment while working
"IncUnemp": 0.3, # Unemployment benefits replacement rate while working
"T_retire": 0, # Period of retirement (0 --> no retirement)
"UnempPrbRet": 0.005, # Probability of "unemployment" while retired
"IncUnempRet": 0.0, # "Unemployment" benefits when retired
}

# Default parameters to make aXtraGrid using make_assets_grid
WealthPortfolioConsumerType_aXtraGrid_default = {
"aXtraMin": 0.001, # Minimum end-of-period "assets above minimum" value
"aXtraMax": 100, # Maximum end-of-period "assets above minimum" value
"aXtraNestFac": 1, # Exponential nesting factor for aXtraGrid
"aXtraCount": 200, # Number of points in the grid of "assets above minimum"
"aXtraExtra": None, # Additional other values to add in grid (optional)
}

# Default parameters to make RiskyDstn with make_lognormal_RiskyDstn (and uniform ShareGrid)
WealthPortfolioConsumerType_RiskyDstn_default = {
"RiskyAvg": 1.08, # Mean return factor of risky asset
"RiskyStd": 0.18362634887, # Stdev of log returns on risky asset
"RiskyCount": 5, # Number of integration nodes to use in approximation of risky returns
}

WealthPortfolioConsumerType_ShareGrid_default = {
"ShareCount": 25 # Number of discrete points in the risky share approximation
}

# Default parameters to make ChiFunc with make_ChiFromOmega_function
WealthPortfolioConsumerType_ChiFunc_default = {
"ChiFromOmega_N": 501, # Number of gridpoints in chi-from-omega function
"ChiFromOmega_bound": 15, # Highest gridpoint to use for it
}

# Make a dictionary to specify a risky asset consumer type
WealthPortfolioConsumerType_solving_default = {
# BASIC HARK PARAMETERS REQUIRED TO SOLVE THE MODEL
"cycles": 1, # Finite, non-cyclic model
"T_cycle": 1, # Number of periods in the cycle for this agent type
"constructors": WealthPortfolioConsumerType_constructors_default, # See dictionary above
# PRIMITIVE RAW PARAMETERS REQUIRED TO SOLVE THE MODEL
"CRRA": 5.0, # Coefficient of relative risk aversion
"Rfree": 1.03, # Return factor on risk free asset
"DiscFac": 0.90, # Intertemporal discount factor
"LivPrb": [0.98], # Survival probability after each period
"PermGroFac": [1.01], # Permanent income growth factor
"BoroCnstArt": 0.0, # Artificial borrowing constraint
"WealthShare": 0.5, # Share of wealth in Cobb-Douglas aggregator in utility function
"WealthShift": 0.1, # Shifter for wealth in utility function
"DiscreteShareBool": False, # Whether risky asset share is restricted to discrete values
"vFuncBool": False, # Whether to calculate the value function during solution
"CubicBool": False, # Whether to use cubic spline interpolation when True
# (Uses linear spline interpolation for cFunc when False)
"AdjustPrb": 1.0, # Probability that the agent can update their risky portfolio share each period
"sim_common_Rrisky": True, # Whether risky returns have a shared/common value across agents
}
WealthPortfolioConsumerType_simulation_default = {
# PARAMETERS REQUIRED TO SIMULATE THE MODEL
"AgentCount": 10000, # Number of agents of this type
"T_age": None, # Age after which simulated agents are automatically killed
"aNrmInitMean": 0.0, # Mean of log initial assets
"aNrmInitStd": 1.0, # Standard deviation of log initial assets
"pLvlInitMean": 0.0, # Mean of log initial permanent income
"pLvlInitStd": 0.0, # Standard deviation of log initial permanent income
"PermGroFacAgg": 1.0, # Aggregate permanent income growth factor
# (The portion of PermGroFac attributable to aggregate productivity growth)
"NewbornTransShk": False, # Whether Newborns have transitory shock
# ADDITIONAL OPTIONAL PARAMETERS
"PerfMITShk": False, # Do Perfect Foresight MIT Shock
# (Forces Newborns to follow solution path of the agent they replaced if True)
"neutral_measure": False, # Whether to use permanent income neutral measure (see Harmenberg 2021)
}

# Assemble the default dictionary
WealthPortfolioConsumerType_default = {}
WealthPortfolioConsumerType_default.update(WealthPortfolioConsumerType_solving_default)
WealthPortfolioConsumerType_default.update(
WealthPortfolioConsumerType_simulation_default
)
WealthPortfolioConsumerType_default.update(
WealthPortfolioConsumerType_aXtraGrid_default
)
WealthPortfolioConsumerType_default.update(
WealthPortfolioConsumerType_ShareGrid_default
)
WealthPortfolioConsumerType_default.update(
WealthPortfolioConsumerType_IncShkDstn_default
)
WealthPortfolioConsumerType_default.update(
WealthPortfolioConsumerType_RiskyDstn_default
)
WealthPortfolioConsumerType_default.update(WealthPortfolioConsumerType_ChiFunc_default)
init_wealth_portfolio = WealthPortfolioConsumerType_default


class WealthPortfolioConsumerType(PortfolioConsumerType):
time_inv_ = deepcopy(PortfolioConsumerType.time_inv_)
time_inv_ = time_inv_ + ["WealthShare", "WealthShift", "ChiFunc"]
Expand All @@ -114,10 +248,18 @@ def __init__(self, **kwds):

self.solve_one_period = solve_one_period_WealthPortfolio

def update(self):
super().update()
self.update_ChiFunc()

def update_ChiFunc(self):
if self.WealthShare == 0.0:
self.ChiFunc = None
else:
self.ChiFunc = ChiFromOmegaFunction(self.CRRA, self.WealthShare)
self.construct("ChiFunc")


###############################################################################


def utility(c, a, CRRA, share=0.0, intercept=0.0):
Expand Down Expand Up @@ -502,8 +644,3 @@ def solve_one_period_WealthPortfolio(
vFuncAdj=vFuncNow,
)
return solution_now


init_wealth_portfolio = init_portfolio.copy()
init_wealth_portfolio["WealthShare"] = 0.5
init_wealth_portfolio["WealthShift"] = 0.1
47 changes: 31 additions & 16 deletions HARK/distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -963,11 +963,11 @@ def _approx_equiprobable(
)

if np.array_equal(self.Sigma, np.diag(np.diag(self.Sigma))):
ind_atoms = np.empty((self.M, N))
ind_atoms = np.empty((self.M, N + 2 * tail_N))

for i in range(self.M):
if self.Sigma[i, i] == 0.0:
x_atoms = np.repeat(np.exp(self.mu[i]), N)
x_atoms = np.repeat(np.exp(self.mu[i]), N + 2 * tail_N)
ind_atoms[i] = x_atoms
else:
x_atoms = (
Expand All @@ -983,7 +983,22 @@ def _approx_equiprobable(
atoms = np.stack(
[ar.flatten() for ar in list(np.meshgrid(*atoms_list))], axis=1
).T
pmv = np.repeat(1 / (N**self.M), N**self.M)

interiors = np.empty([self.M, (N + 2 * tail_N) ** (self.M)])

inners = np.zeros(N + 2 * tail_N)

if tail_N > 0:
inners[:tail_N] = [(tail_N - i) for i in range(tail_N)]
inners[-tail_N:] = [(i + 1) for i in range(tail_N)]

for i in range(self.M):
inners_i = [inners for _ in range((N + 2 * tail_N) ** i)]

interiors[i] = np.repeat(
[*inners_i], (N + 2 * tail_N) ** (self.M - (i + 1))
)

else:
if tail_bound is not None:
if type(tail_bound) is float:
Expand Down Expand Up @@ -1024,10 +1039,10 @@ def eval(params, z):
excl = []

for j in range(len(z)):
if z[j, 0] != z[j, 1]:
inds.append(j)
else:
if z[j, 0] == z[j, 1]:
excl.append(j)
elif params[j] != 0.0:
inds.append(j)

dim = len(inds)

Expand Down Expand Up @@ -1111,21 +1126,21 @@ def eval(params, z):

atoms[i] = xi_atoms

max_locs = np.argmax(np.abs(interiors), axis=0)
max_locs = np.argmax(np.abs(interiors), axis=0)

max_inds = np.stack([max_locs, np.arange(len(max_locs))], axis=1)
max_inds = np.stack([max_locs, np.arange(len(max_locs))], axis=1)

prob_locs = interiors[max_inds[:, 0], max_inds[:, 1]]
prob_locs = interiors[max_inds[:, 0], max_inds[:, 1]]

def prob_assign(x):
if x == 0:
return 1 / (N**self.M)
else:
return 0.0
def prob_assign(x):
if x == 0:
return 1 / (N**self.M)
else:
return 0.0

prob_vec = np.vectorize(prob_assign)
prob_vec = np.vectorize(prob_assign)

pmv = prob_vec(prob_locs)
pmv = prob_vec(prob_locs)

limit = {
"dist": self,
Expand Down
10 changes: 0 additions & 10 deletions binder/postBuild

This file was deleted.

3 changes: 0 additions & 3 deletions binder/postBuild.bat

This file was deleted.

8 changes: 1 addition & 7 deletions binder/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,2 @@
cite2c
git+https://github.com/econ-ark/hark@master
ipywidgets
jupyter_contrib_nbextensions
matplotlib
numpy
scipy
seaborn
sequence_jacobian # required for KS-HARK-presentation example
Loading

0 comments on commit 9d6fcf5

Please sign in to comment.