Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add constructor architecture #1410

Merged
merged 45 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
a036f24
First draft of construct() method
mnwhite Apr 15, 2024
4d3e407
IncShkDstn is constructed using construct
mnwhite Apr 15, 2024
0b96804
Add missing constructor lines to ConsAggShock
mnwhite Apr 15, 2024
1d56aa8
Add make_univariate, simplify formatting
mnwhite Apr 24, 2024
f440295
Remove old lines from ConsAggShockModel
mnwhite Apr 24, 2024
613fa5c
Turn lists into TimeVaryingDiscreteDistributions
mnwhite Apr 24, 2024
35cf624
Run black on changed files
mnwhite Apr 24, 2024
51ed70e
Properly run pre-commit
mnwhite Apr 24, 2024
ad6994d
Add aXtraGrid and solution_terminal to constructors
mnwhite Apr 26, 2024
3c5b2c7
Fix syntax in asset grid test
mnwhite Apr 26, 2024
fd1dd45
Fix odd behavior of calc_jacobian
mnwhite Apr 26, 2024
ff134b6
Fix indexing error in distribution tests
mnwhite Apr 26, 2024
bdc2b0f
Missed one index spot
mnwhite Apr 26, 2024
154512d
Change two tests to AlmostEqual
mnwhite Apr 26, 2024
38c376b
Adjust notation in examples
mnwhite Apr 26, 2024
f1f963e
Two more tiny fixes
mnwhite Apr 27, 2024
6c6fc97
Move general income process objects to constructors
mnwhite May 7, 2024
246874f
Merge branch 'master' into AddConstructorArchitecture
mnwhite May 7, 2024
0134f23
Fix example notebook
mnwhite May 7, 2024
2a23106
Remove pLvlGrid dependence on AgentCount
mnwhite May 7, 2024
35fadae
Missed two test values
mnwhite May 7, 2024
070cd83
Another test, another fourth digit change
mnwhite May 7, 2024
e5a85b4
Move RiskyDstn, ShkDstn, ShareLimit to constructors
mnwhite May 8, 2024
9638460
Fix spelling
mnwhite May 8, 2024
1b03e14
Fix dictionary imports in examples
mnwhite May 8, 2024
1265977
Move PrefShkDstn to constructor format
mnwhite May 8, 2024
4ffee80
Move MedShkDstn to constructor format
mnwhite May 8, 2024
f905c8f
Add describe_constructors method
mnwhite May 9, 2024
0c2e5ed
Move AggShock terminal solution to constructor
mnwhite May 10, 2024
e74a269
Forgot to actually delete old method
mnwhite May 10, 2024
019e39b
Moved MedShock terminal solution and pLvlGrid to constructors
mnwhite May 10, 2024
6fdacdc
Typo in dictionary key
mnwhite May 10, 2024
e79d786
And typo in dictionary value
mnwhite May 10, 2024
a9a9df6
Moved terminal solution for portfolio and bequest models to constriuc…
mnwhite May 10, 2024
f92b29f
Fix portfolio and bequest examples
mnwhite May 10, 2024
22efb70
Move ConsLaborModel objects to constructors
mnwhite May 10, 2024
243bdd7
Move Markov terminal solution to constructor
mnwhite May 13, 2024
a8eadab
Fix MPC in terminal solution
mnwhite May 13, 2024
32f3f51
Add two example constructors for MrkvArray
mnwhite May 13, 2024
6df9acb
Fix dictionary import in one example notebook
mnwhite May 13, 2024
fa6030a
Changes weren't saved on prior commit
mnwhite May 13, 2024
b282159
Added error logging functionality to construct
mnwhite May 14, 2024
357e412
Add a few alternate constructors
mnwhite May 14, 2024
5b9d91d
Add CHANGELOG entry
mnwhite May 14, 2024
d82714c
Merge branch 'master' into AddConstructorArchitecture
mnwhite May 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
189 changes: 189 additions & 0 deletions HARK/Calibration/Assets/AssetProcesses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
"""
This file contains tools for creating risky asset return distributions, for use
as inputs to several consumption-saving model solvers.
"""

import numpy as np
from scipy.optimize import minimize_scalar
from HARK.distribution import (
combine_indep_dstns,
DiscreteDistributionLabeled,
IndexDistribution,
Lognormal,
)


def make_lognormal_RiskyDstn(T_cycle, RiskyAvg, RiskyStd, RiskyCount, RNG):
"""
Creates a discrete approximation of lognormal risky asset returns, either
as a single distribution or as a lifecycle sequence.

Parameters
----------
T_cycle : int
Number of non-terminal periods in this agent's cycle.
RiskyAvg : float or [float]
Mean return of the risky asset. If a single number, it is used for all
periods. If it is a list, then it represents lifecycle returns (or
perceptions thereof).
RiskyStd : float or [float]
Standard deviation of log returns of the risky asset. Allows the same
options as RiskyAvg.
RiskyCount : int
Number of equiprobable discrete nodes in the risky return distribution.
RNG : RandomState
Internal random number generator for the AgentType instance, used to
generate random seeds.

Returns
-------
RiskyDstn : DiscreteDistribution or [DiscreteDistribution]
Discretized approximation to lognormal asset returns.
"""
# Determine whether this instance has time-varying risk perceptions
if (
(type(RiskyAvg) is list)
and (type(RiskyStd) is list)
and (len(RiskyAvg) == len(RiskyStd))
and (len(RiskyAvg) == T_cycle)
):
time_varying_RiskyDstn = True
elif (type(RiskyStd) is list) or (type(RiskyAvg) is list):
raise AttributeError(
"If RiskyAvg is time-varying, then RiskyStd must be as well, and they must both have length of T_cycle!"
)
else:
time_varying_RiskyDstn = False

# Generate a discrete approximation to the risky return distribution
# if its parameters are time-varying
if time_varying_RiskyDstn:
RiskyDstn = IndexDistribution(
Lognormal.from_mean_std,
{"mean": RiskyAvg, "std": RiskyStd},
seed=RNG.integers(0, 2**31 - 1),
).discretize(RiskyCount, method="equiprobable")

# Generate a discrete approximation to the risky return distribution if
# its parameters are constant
else:
RiskyDstn = Lognormal.from_mean_std(RiskyAvg, RiskyStd).discretize(
RiskyCount, method="equiprobable"
)

return RiskyDstn


def combine_IncShkDstn_and_RiskyDstn(T_cycle, RiskyDstn, IncShkDstn):
"""
Combine the income shock distribution (over PermShk and TranShk) with the
risky return distribution (RiskyDstn) to make a new object called ShockDstn.

Parameters
----------
T_cycle : int
Number of non-terminal periods in this agent's cycle.
RiskyDstn : DiscreteDistribution or [DiscreteDistribution]
Discretized approximation to lognormal asset returns.
IncShkDstn : [Distribution]
A discrete approximation to the income process between each period.

Returns
-------
ShockDstn : IndexDistribution
A combined trivariate discrete distribution of permanent shocks, transitory
shocks, and risky returns. Has one element per period of the agent's cycle.
"""
# Create placeholder distributions
if hasattr(RiskyDstn, "__getitem__"):
dstn_list = [
combine_indep_dstns(IncShkDstn[t], RiskyDstn[t]) for t in range(T_cycle)
]
else:
dstn_list = [
combine_indep_dstns(IncShkDstn[t], RiskyDstn) for t in range(T_cycle)
]

# Names of the variables (hedging for the unlikely case that in
# some index of IncShkDstn variables are in a switched order)
names_list = [
list(IncShkDstn[t].variables.keys()) + ["Risky"] for t in range(T_cycle)
]

conditional = {
"pmv": [x.pmv for x in dstn_list],
"atoms": [x.atoms for x in dstn_list],
"var_names": names_list,
}

# Now create the actual distribution using the index and labeled class
ShockDstn = IndexDistribution(
engine=DiscreteDistributionLabeled,
conditional=conditional,
)
return ShockDstn


def calc_ShareLimit_for_CRRA(T_cycle, RiskyDstn, CRRA, Rfree):
"""
Calculates the lower bound on the risky asset share as market resources go
to infinity, given that utility is CRRA.

Parameters
----------
T_cycle : int
Number of non-terminal periods in this agent's cycle.
RiskyDstn : DiscreteDistribution or [DiscreteDistribution]
Discretized approximation to lognormal asset returns.
CRRA : float
Coefficient of relative risk aversion.
Rfree : float
Return factor on the risk-free asset.

Returns
-------
ShareLimit : float or [float]
Lower bound on risky asset share. Can be a single number or a lifecycle sequence.
"""
RiskyDstn_is_time_varying = hasattr(RiskyDstn, "__getitem__")
Rfree_is_time_varying = type(Rfree) is list

# If the risky share lower bound is time varying...
if RiskyDstn_is_time_varying or Rfree_is_time_varying:
ShareLimit = []
for t in range(T_cycle):
if RiskyDstn_is_time_varying:
RiskyDstn_t = RiskyDstn[t]
else:
RiskyDstn_t = RiskyDstn
if Rfree_is_time_varying:
Rfree_t = Rfree[t]
else:
Rfree_t = Rfree

def temp_f(s):
return -((1.0 - CRRA) ** -1) * np.dot(
(Rfree_t + s * (RiskyDstn_t.atoms - Rfree_t)) ** (1.0 - CRRA),
RiskyDstn_t.pmv,
)

SharePF = minimize_scalar(temp_f, bounds=(0.0, 1.0), method="bounded").x
ShareLimit.append(SharePF)

# If the risky share lower bound is not time-varying...
else:

def temp_f(s):
return -((1.0 - CRRA) ** -1) * np.dot(
(Rfree + s * (RiskyDstn.atoms - Rfree)) ** (1.0 - CRRA),
RiskyDstn.pmv,
)

SharePF = minimize_scalar(
temp_f, bracket=(0.0, 1.0), method="golden", tol=1e-10
).x
if type(SharePF) is np.array:
SharePF = SharePF[0]
ShareLimit = SharePF

return ShareLimit