diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 37bf73ff5..16afc2b7d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -38,3 +38,5 @@ jobs: pytest --cov=./ --cov-report=xml - name: upload coverage report uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 79a3470c7..1acc0a54e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,29 +2,28 @@ exclude: Documentation/example_notebooks/ repos: - repo: https://github.com/mwouts/jupytext - rev: v1.14.5 + rev: v1.15.0 hooks: - id: jupytext - args: - [--sync, --set-formats, "ipynb,py:percent", --pipe, black, --execute] + args: [--sync, --set-formats, "ipynb", --pipe, black, --execute] additional_dependencies: [jupytext, black, nbconvert] files: ^examples/.*\.ipynb$ - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black exclude: ^examples/ - repo: https://github.com/asottile/pyupgrade - rev: v3.4.0 + rev: v3.10.1 hooks: - id: pyupgrade args: ["--py38-plus"] exclude: ^examples/ - repo: https://github.com/asottile/blacken-docs - rev: 1.13.0 + rev: 1.15.0 hooks: - id: blacken-docs exclude: ^examples/ @@ -38,7 +37,7 @@ repos: exclude: ^examples/ - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.9-for-vscode + rev: v3.0.1 hooks: - id: prettier exclude: ^examples/ diff --git a/Documentation/CHANGELOG.md b/Documentation/CHANGELOG.md index 479a5814f..62a24addd 100644 --- a/Documentation/CHANGELOG.md +++ b/Documentation/CHANGELOG.md @@ -15,6 +15,7 @@ Release Date: TBD ### Major Changes - Adds `HARK.core.AgentPopulation` class to represent a population of agents with ex-ante heterogeneous parametrizations as distributions. [#1237](https://github.com/econ-ark/HARK/pull/1237) +- Adds `HARK.core.Parameters` class to represent a collection of time varying and time invariant parameters in a model. [#1240](https://github.com/econ-ark/HARK/pull/1240) ### Minor Changes diff --git a/HARK/ConsumptionSaving/ConsGenIncProcessModel.py b/HARK/ConsumptionSaving/ConsGenIncProcessModel.py index 7a9848d91..b586299ff 100644 --- a/HARK/ConsumptionSaving/ConsGenIncProcessModel.py +++ b/HARK/ConsumptionSaving/ConsGenIncProcessModel.py @@ -862,7 +862,7 @@ def solve(self): # long run permanent income growth doesn't work yet init_explicit_perm_inc["PermGroFac"] = [1.0] init_explicit_perm_inc["aXtraMax"] = 30 -init_explicit_perm_inc["aXtraExtra"] = [0.005, 0.01] +init_explicit_perm_inc["aXtraExtra"] = np.array([0.005, 0.01]) class GenIncProcessConsumerType(IndShockConsumerType): diff --git a/HARK/ConsumptionSaving/ConsLaborModel.py b/HARK/ConsumptionSaving/ConsLaborModel.py index d0fba2be8..3a47115f4 100644 --- a/HARK/ConsumptionSaving/ConsLaborModel.py +++ b/HARK/ConsumptionSaving/ConsLaborModel.py @@ -787,10 +787,8 @@ def plot_LbrFunc(self, t, bMin=None, bMax=None, ShkSet=None): 1.0, 1.0, ] # Wage rate in a lifecycle -init_labor_lifecycle["LbrCostCoeffs"] = [ - -2.0, - 0.4, -] # Assume labor cost coeffs is a polynomial of degree 1 +# Assume labor cost coeffs is a polynomial of degree 1 +init_labor_lifecycle["LbrCostCoeffs"] = np.array([-2.0, 0.4]) init_labor_lifecycle["T_cycle"] = 10 # init_labor_lifecycle['T_retire'] = 7 # IndexError at line 774 in interpolation.py. init_labor_lifecycle[ diff --git a/HARK/ConsumptionSaving/tests/test_ConsGenIncProcessModel.py b/HARK/ConsumptionSaving/tests/test_ConsGenIncProcessModel.py index d9b03145f..8a040a88e 100644 --- a/HARK/ConsumptionSaving/tests/test_ConsGenIncProcessModel.py +++ b/HARK/ConsumptionSaving/tests/test_ConsGenIncProcessModel.py @@ -25,10 +25,8 @@ # Parameters for constructing the "assets above minimum" grid "aXtraMin": 0.001, # Minimum end-of-period "assets above minimum" value "aXtraMax": 30, # Maximum end-of-period "assets above minimum" value - "aXtraExtra": [ - 0.005, - 0.01, - ], # Some other value of "assets above minimum" to add to the grid + # Some other value of "assets above minimum" to add to the grid + "aXtraExtra": np.array([0.005, 0.01]), "aXtraNestFac": 3, # Exponential nesting factor when constructing "assets above minimum" grid "aXtraCount": 48, # Number of points in the grid of "assets above minimum" # Parameters describing the income process diff --git a/HARK/ConsumptionSaving/tests/test_IndShockConsumerType.py b/HARK/ConsumptionSaving/tests/test_IndShockConsumerType.py index 4379128db..3a61b2166 100644 --- a/HARK/ConsumptionSaving/tests/test_IndShockConsumerType.py +++ b/HARK/ConsumptionSaving/tests/test_IndShockConsumerType.py @@ -762,13 +762,13 @@ def setUp(self): "LivPrb": LivPrb, "PermGroFac": [PermGroFac], "Rfree": Rfree, - "track_vars": ["bNrm", "t_age"], } ) def test_NewbornStatesAndShocks(self): # Make agent, shock and initial condition histories agent = IndShockConsumerType(**self.base_params) + agent.track_vars = ["bNrm", "t_age"] agent.make_shock_history() # Find indices of agents and time periods that correspond to deaths @@ -814,13 +814,13 @@ def setUp(self): { "AgentCount": agent_count, "T_sim": t_sim, - "track_vars": ["t_age", "t_cycle"], } ) def test_compare_t_age_t_cycle(self): # Make agent, shock and initial condition histories agent = IndShockConsumerType(**self.base_params) + agent.track_vars = ["t_age", "t_cycle"] agent.make_shock_history() # Solve and simulate the agent @@ -855,6 +855,7 @@ def test_compare_t_age_t_cycle_premature_death(self): par["T_age"] = par["T_age"] - 8 # Make agent, shock and initial condition histories agent = IndShockConsumerType(**par) + agent.track_vars = ["t_age", "t_cycle"] agent.make_shock_history() # Solve and simulate the agent diff --git a/HARK/core.py b/HARK/core.py index 5d4f2941d..d7db51b30 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -7,6 +7,7 @@ problem by finding a general equilibrium dynamic rule. """ import sys +from collections import defaultdict, namedtuple from copy import copy, deepcopy from dataclasses import dataclass, field from time import time @@ -57,11 +58,216 @@ def set_verbosity_level(level): _log.setLevel(level) +class Parameters: + """ + This class defines an object that stores all of the parameters for a model + as an internal dictionary. It is designed to also handle the age-varying + dynamics of parameters. + + Attributes + ---------- + + _length : int + The terminal age of the agents in the model. + _invariant_params : list + A list of the names of the parameters that are invariant over time. + _varying_params : list + A list of the names of the parameters that vary over time. + """ + + def __init__(self, **parameters: Any): + """ + Initializes a Parameters object and parses the age-varying + dynamics of the parameters. + + Parameters + ---------- + + parameters : keyword arguments + Any number of keyword arguments of the form key=value. + To parse a dictionary of parameters, use the ** operator. + """ + params = parameters.copy() + self._length = params.pop("T_cycle", None) + self._invariant_params = set() + self._varying_params = set() + self._parameters: Dict[str, Union[int, float, np.ndarray, list, tuple]] = {} + + for key, value in params.items(): + self._parameters[key] = self.__infer_dims__(key, value) + + def __infer_dims__( + self, key: str, value: Union[int, float, np.ndarray, list, tuple, None] + ) -> Union[int, float, np.ndarray, list, tuple]: + """ + Infers the age-varying dimensions of a parameter. + + If the parameter is a scalar, numpy array, or None, it is assumed to be + invariant over time. If the parameter is a list or tuple, it is assumed + to be varying over time. If the parameter is a list or tuple of length + greater than 1, the length of the list or tuple must match the + `_term_age` attribute of the Parameters object. + + Parameters + ---------- + key : str + name of parameter + value : Any + value of parameter + + """ + if isinstance(value, (int, float, np.ndarray, type(None))): + self.__add_to_invariant__(key) + return value + if isinstance(value, (list, tuple)): + if len(value) == 1: + self.__add_to_invariant__(key) + return value[0] + if self._length is None or self._length == 1: + self._length = len(value) + if len(value) == self._length: + self.__add_to_varying__(key) + return value + raise ValueError( + f"Parameter {key} must be of length 1 or {self._length}, not {len(value)}" + ) + raise ValueError(f"Parameter {key} has unsupported type {type(value)}") + + def __add_to_invariant__(self, key: str): + """ + Adds parameter name to invariant set and removes from varying set. + """ + self._varying_params.discard(key) + self._invariant_params.add(key) + + def __add_to_varying__(self, key: str): + """ + Adds parameter name to varying set and removes from invariant set. + """ + self._invariant_params.discard(key) + self._varying_params.add(key) + + def __getitem__(self, item_or_key: Union[int, str]): + """ + If item_or_key is an integer, returns a Parameters object with the parameters + that apply to that age. This includes all invariant parameters and the + `item_or_key`th element of all age-varying parameters. If item_or_key is a string, + it returns the value of the parameter with that name. + """ + if isinstance(item_or_key, int): + if item_or_key >= self._length: + raise ValueError( + f"Age {item_or_key} is greater than or equal to terminal age {self._length}." + ) + + params = {key: self._parameters[key] for key in self._invariant_params} + params.update( + { + key: self._parameters[key][item_or_key] + for key in self._varying_params + } + ) + return Parameters(**params) + elif isinstance(item_or_key, str): + return self._parameters[item_or_key] + + def __setitem__(self, key: str, value: Any): + """ + Sets the value of a parameter. + + Parameters + ---------- + key : str + name of parameter + value : Any + value of parameter + + """ + if not isinstance(key, str): + raise ValueError("Parameters must be set with a string key") + self._parameters[key] = self.__infer_dims__(key, value) + + def keys(self): + """ + Returns a list of the names of the parameters. + """ + return self._invariant_params | self._varying_params + + def values(self): + """ + Returns a list of the values of the parameters. + """ + return list(self._parameters.values()) + + def items(self): + """ + Returns a list of tuples of the form (name, value) for each parameter. + """ + return list(self._parameters.items()) + + def __iter__(self): + """ + Allows for iterating over the parameter names. + """ + return iter(self.keys()) + + def __deepcopy__(self, memo): + """ + Returns a deep copy of the Parameters object. + """ + return Parameters(**deepcopy(self.to_dict(), memo)) + + def to_dict(self): + """ + Returns a dictionary of the parameters. + """ + return {key: self._parameters[key] for key in self.keys()} + + def to_namedtuple(self): + """ + Returns a namedtuple of the parameters. + """ + return namedtuple("Parameters", self.keys())(**self.to_dict()) + + def update(self, other_params): + """ + Updates the parameters with the values from another + Parameters object or a dictionary. + + Parameters + ---------- + other_params : Parameters or dict + Parameters object or dictionary of parameters to update with. + """ + if isinstance(other_params, Parameters): + self._parameters.update(other_params.to_dict()) + elif isinstance(other_params, dict): + self._parameters.update(other_params) + else: + raise ValueError("Parameters must be a dict or a Parameters object") + + def __str__(self): + """ + Returns a simple string representation of the Parameters object. + """ + return f"Parameters({str(self.to_dict())})" + + def __repr__(self): + """ + Returns a detailed string representation of the Parameters object. + """ + return f"Parameters( _age_inv = {self._invariant_params}, _age_var = {self._varying_params}, | {self.to_dict()})" + + class Model: """ A class with special handling of parameters assignment. """ + def __init__(self): + if not hasattr(self, "parameters"): + self.parameters = {} + def assign_parameters(self, **kwds): """ Assign an arbitrary number of attributes to this agent. @@ -102,10 +308,6 @@ def __eq__(self, other): return NotImplemented - def __init__(self): - if not hasattr(self, "parameters"): - self.parameters = {} - def __str__(self): type_ = type(self) module = type_.__module__ @@ -1468,9 +1670,6 @@ def distribute_params(agent, param_name, param_count, distribution): return agent_set -Parameters = NewType("ParameterDict", dict) - - @dataclass class AgentPopulation: """ @@ -1478,7 +1677,7 @@ class AgentPopulation: """ agent_type: AgentType # type of agent in the population - parameters: Parameters # dictionary of parameters + parameters: dict # dictionary of parameters seed: int = 0 # random seed time_var: List[str] = field(init=False) time_inv: List[str] = field(init=False) diff --git a/HARK/tests/test_core.py b/HARK/tests/test_core.py index 6b61a80f8..0cda93381 100644 --- a/HARK/tests/test_core.py +++ b/HARK/tests/test_core.py @@ -4,12 +4,13 @@ import unittest import numpy as np +import pytest from HARK.ConsumptionSaving.ConsIndShockModel import ( IndShockConsumerType, init_idiosyncratic_shocks, ) -from HARK.core import AgentPopulation, AgentType, distribute_params +from HARK.core import AgentPopulation, AgentType, Parameters, distribute_params from HARK.distribution import Uniform from HARK.metric import MetricObject, distance_metric @@ -168,3 +169,97 @@ def test_create_agents(self): self.agent_pop.create_distributed_agents() self.assertEqual(len(self.agent_pop.agents), 12) + + +class test_parameters(unittest.TestCase): + def setUp(self): + self.params = Parameters(T_cycle=3, a=1, b=[2, 3, 4], c=np.array([5, 6, 7])) + + def test_init(self): + self.assertEqual(self.params._length, 3) + self.assertEqual(self.params._invariant_params, {"a", "c"}) + self.assertEqual(self.params._varying_params, {"b"}) + + def test_getitem(self): + self.assertEqual(self.params["a"], 1) + self.assertEqual(self.params[0]["b"], 2) + self.assertEqual(self.params["c"][1], 6) + + def test_setitem(self): + self.params["d"] = 8 + self.assertEqual(self.params["d"], 8) + + def test_update(self): + self.params.update({"a": 9, "b": [10, 11, 12]}) + self.assertEqual(self.params["a"], 9) + self.assertEqual(self.params[0]["b"], 10) + + def test_initialization(self): + params = Parameters(a=1, b=[1, 2], T_cycle=2) + assert params._length == 2 + assert params._invariant_params == {"a"} + assert params._varying_params == {"b"} + + def test_infer_dims_scalar(self): + params = Parameters(a=1) + assert params["a"] == 1 + + def test_infer_dims_array(self): + params = Parameters(b=np.array([1, 2])) + assert all(params["b"] == np.array([1, 2])) + + def test_infer_dims_list_varying(self): + params = Parameters(b=[1, 2], T_cycle=2) + assert params["b"] == [1, 2] + + def test_infer_dims_list_invariant(self): + params = Parameters(b=[1]) + assert params["b"] == 1 + + def test_setitem(self): + params = Parameters(a=1) + params["b"] = 2 + assert params["b"] == 2 + + def test_keys_values_items(self): + params = Parameters(a=1, b=2) + assert set(params.keys()) == {"a", "b"} + assert set(params.values()) == {1, 2} + assert set(params.items()) == {("a", 1), ("b", 2)} + + def test_to_dict(self): + params = Parameters(a=1, b=2) + assert params.to_dict() == {"a": 1, "b": 2} + + def test_to_namedtuple(self): + params = Parameters(a=1, b=2) + named_tuple = params.to_namedtuple() + assert named_tuple.a == 1 + assert named_tuple.b == 2 + + def test_update_params(self): + params1 = Parameters(a=1, b=2) + params2 = Parameters(a=3, c=4) + params1.update(params2) + assert params1["a"] == 3 + assert params1["c"] == 4 + + def test_unsupported_type_error(self): + with pytest.raises(ValueError): + Parameters(b={1, 2}) + + def test_get_item_dimension_error(self): + params = Parameters(b=[1, 2], T_cycle=2) + with pytest.raises(ValueError): + params[2] + + def test_getitem_with_key(self): + params = Parameters(a=1, b=[2, 3], T_cycle=2) + assert params["a"] == 1 + assert params["b"] == [2, 3] + + def test_getitem_with_item(self): + params = Parameters(a=1, b=[2, 3], T_cycle=2) + age_params = params[1] + assert age_params["a"] == 1 + assert age_params["b"] == 3 diff --git a/HARK/utilities.py b/HARK/utilities.py index e37159ed1..520dfc7dc 100644 --- a/HARK/utilities.py +++ b/HARK/utilities.py @@ -164,10 +164,11 @@ def construct_assets_grid(parameters): ) # Add in additional points for the grid: - for a in aXtraExtra: - if a is not None and a not in aXtraGrid: - j = aXtraGrid.searchsorted(a) - aXtraGrid = np.insert(aXtraGrid, j, a) + if aXtraExtra is not None: + for a in aXtraExtra: + if a is not None and a not in aXtraGrid: + j = aXtraGrid.searchsorted(a) + aXtraGrid = np.insert(aXtraGrid, j, a) return aXtraGrid diff --git a/examples/GenIncProcessModel/GenIncProcessModel.ipynb b/examples/GenIncProcessModel/GenIncProcessModel.ipynb index 8f8c22ca8..8d65cc8bd 100644 --- a/examples/GenIncProcessModel/GenIncProcessModel.ipynb +++ b/examples/GenIncProcessModel/GenIncProcessModel.ipynb @@ -187,10 +187,8 @@ " # Parameters for constructing the \"assets above minimum\" grid\n", " \"aXtraMin\": 0.001, # Minimum end-of-period \"assets above minimum\" value\n", " \"aXtraMax\": 30, # Maximum end-of-period \"assets above minimum\" value\n", - " \"aXtraExtra\": [\n", - " 0.005,\n", - " 0.01,\n", - " ], # Some other value of \"assets above minimum\" to add to the grid\n", + " # Some other value of \"assets above minimum\" to add to the grid\n", + " \"aXtraExtra\": np.array([0.005, 0.01]),\n", " \"aXtraNestFac\": 3, # Exponential nesting factor when constructing \"assets above minimum\" grid\n", " \"aXtraCount\": 48, # Number of points in the grid of \"assets above minimum\"\n", " # Parameters describing the income process\n", @@ -632,7 +630,7 @@ "jupytext": { "cell_metadata_filter": "collapsed,code_folding", "cell_metadata_json": true, - "formats": "ipynb,py:percent", + "formats": "ipynb", "notebook_metadata_filter": "all" }, "kernelspec": { diff --git a/examples/Journeys/Quickstart_tutorial/Quick_start_with_solution.ipynb b/examples/Journeys/Quickstart_tutorial/Quick_start_with_solution.ipynb index 094b0d85f..e917b0cd9 100644 --- a/examples/Journeys/Quickstart_tutorial/Quick_start_with_solution.ipynb +++ b/examples/Journeys/Quickstart_tutorial/Quick_start_with_solution.ipynb @@ -3366,10 +3366,10 @@ "prob_surv = 1 - prob_dead\n", "\n", "# The HARK argument need to be a list, thus convert it from numpy array\n", - "prob_surv_list = np.ndarray.tolist(prob_surv[:80])\n", + "prob_surv_list = np.ndarray.tolist(prob_surv[:79])\n", "\n", "income_profile = np.genfromtxt(\"productivity_profile.csv\", delimiter=\",\", skip_header=1)\n", - "income_profile_list = np.ndarray.tolist(income_profile[:80])\n", + "income_profile_list = np.ndarray.tolist(income_profile[:79])\n", "\n", "# Continue your solution" ] @@ -3445,10 +3445,10 @@ "\n", "prob_dead = np.genfromtxt(\"life_table.csv\", delimiter=\",\", skip_header=1)\n", "prob_surv = 1 - prob_dead\n", - "prob_surv_list = np.ndarray.tolist(prob_surv[:80])\n", + "prob_surv_list = np.ndarray.tolist(prob_surv[:79])\n", "\n", "income_profile = np.genfromtxt(\"productivity_profile.csv\", delimiter=\",\", skip_header=1)\n", - "income_profile_list = np.ndarray.tolist(income_profile[:80])\n", + "income_profile_list = np.ndarray.tolist(income_profile[:79])\n", "\n", "Ex_dictionary = {\n", " \"CRRA\": 2.0,\n", @@ -3457,7 +3457,7 @@ " \"LivPrb\": prob_surv_list,\n", " \"PermGroFac\": income_profile_list,\n", " \"cycles\": 1,\n", - " \"T_cycle\": 1,\n", + " \"T_cycle\": 79,\n", "}\n", "\n", "Ex_agent = PerfForesightConsumerType(**Ex_dictionary)\n", @@ -3478,7 +3478,7 @@ " \"pLvlInitMean\": 0.0,\n", " \"pLvlInitStd\": 0.05,\n", " \"PermGroFacAgg\": 1.0,\n", - " \"T_cycle\": 1,\n", + " \"T_cycle\": 79,\n", " \"T_sim\": 2000,\n", " \"T_age\": 80,\n", " \"BoroCnstArt\": 0.0,\n", diff --git a/examples/LifecycleModel/EstimationParameters.py b/examples/LifecycleModel/EstimationParameters.py index 01c5454fc..d3bb158fd 100644 --- a/examples/LifecycleModel/EstimationParameters.py +++ b/examples/LifecycleModel/EstimationParameters.py @@ -1,148 +1,468 @@ -''' +""" Specifies the full set of calibrated values required to estimate the SolvingMicroDSOPs model. The empirical data is stored in a separate csv file and is loaded in SetupSCFdata. -''' +""" + +import numpy as np + # --------------------------------------------------------------------------------- # - Define all of the model parameters for SolvingMicroDSOPs and ConsumerExamples - # --------------------------------------------------------------------------------- -exp_nest = 3 # Number of times to "exponentially nest" when constructing a_grid -aXtraMin = 0.001 # Minimum end-of-period "assets above minimum" value -aXtraMax = 20 # Maximum end-of-period "assets above minimum" value -aXtraHuge = None # A very large value of assets to add to the grid, not used -aXtraExtra = None # Some other value of assets to add to the grid, not used -aXtraCount = 8 # Number of points in the grid of "assets above minimum" - -BoroCnstArt = 0.0 # Artificial borrowing constraint; imposed minimum level of end-of period assets -CubicBool = True # Use cubic spline interpolation when True, linear interpolation when False -vFuncBool = False # Whether to calculate the value function during solution - -Rfree = 1.03 # Interest factor on assets -PermShkCount = 7 # Number of points in discrete approximation to permanent income shocks -TranShkCount = 7 # Number of points in discrete approximation to transitory income shocks -UnempPrb = 0.005 # Probability of unemployment while working -UnempPrbRet = 0.000 # Probability of "unemployment" while retired -IncUnemp = 0.0 # Unemployment benefits replacement rate -IncUnempRet = 0.0 # "Unemployment" benefits when retired - -final_age = 90 # Age at which the problem ends (die with certainty) -retirement_age = 65 # Age at which the consumer retires -initial_age = 25 # Age at which the consumer enters the model -TT = final_age - initial_age # Total number of periods in the model +exp_nest = 3 # Number of times to "exponentially nest" when constructing a_grid +aXtraMin = 0.001 # Minimum end-of-period "assets above minimum" value +aXtraMax = 20 # Maximum end-of-period "assets above minimum" value +aXtraHuge = None # A very large value of assets to add to the grid, not used +aXtraExtra = None # Some other value of assets to add to the grid, not used +aXtraCount = 8 # Number of points in the grid of "assets above minimum" + +# Artificial borrowing constraint; imposed minimum level of end-of period assets +BoroCnstArt = 0.0 +CubicBool = ( + True # Use cubic spline interpolation when True, linear interpolation when False +) +vFuncBool = False # Whether to calculate the value function during solution + +Rfree = 1.03 # Interest factor on assets +PermShkCount = ( + 7 # Number of points in discrete approximation to permanent income shocks +) +TranShkCount = ( + 7 # Number of points in discrete approximation to transitory income shocks +) +UnempPrb = 0.005 # Probability of unemployment while working +UnempPrbRet = 0.000 # Probability of "unemployment" while retired +IncUnemp = 0.0 # Unemployment benefits replacement rate +IncUnempRet = 0.0 # "Unemployment" benefits when retired + +final_age = 90 # Age at which the problem ends (die with certainty) +retirement_age = 65 # Age at which the consumer retires +initial_age = 25 # Age at which the consumer enters the model +TT = final_age - initial_age # Total number of periods in the model retirement_t = retirement_age - initial_age - 1 -CRRA_start = 4.0 # Initial guess of the coefficient of relative risk aversion during estimation (rho) -DiscFacAdj_start = 0.99 # Initial guess of the adjustment to the discount factor during estimation (beth) -DiscFacAdj_bound = [0.0001,15.0] # Bounds for beth; if violated, objective function returns "penalty value" -CRRA_bound = [0.0001,15.0] # Bounds for rho; if violated, objective function returns "penalty value" +# Initial guess of the coefficient of relative risk aversion during estimation (rho) +CRRA_start = 4.0 +# Initial guess of the adjustment to the discount factor during estimation (beth) +DiscFacAdj_start = 0.99 +DiscFacAdj_bound = [ + 0.0001, + 15.0, +] # Bounds for beth; if violated, objective function returns "penalty value" +CRRA_bound = [ + 0.0001, + 15.0, +] # Bounds for rho; if violated, objective function returns "penalty value" # Expected growth rates of permanent income over the lifecycle, starting from age 25 -PermGroFac = [ 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, - 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, - 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, 1.025, - 1.025, 1.01 , 1.01 , 1.01 , 1.01 , 1.01 , 1.01 , 1.01 , - 1.01 , 1.01 , 1.01 , 1.01 , 1.01 , 1.01 , 1.01 , 0.7 , # <-- This represents retirement - 1. , 1. , 1. , 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , 1. , 1. , 1. , 1. ] +PermGroFac = [ + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.025, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 1.01, + 0.7, # <-- This represents retirement + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, +] # Age-varying discount factors over the lifecycle, lifted from Cagetti (2003) -DiscFac_timevary = [1.064914 , 1.057997 , 1.051422 , 1.045179 , 1.039259 , - 1.033653 , 1.028352 , 1.023348 , 1.018632 , 1.014198 , - 1.010037 , 1.006143 , 1.002509 , 0.9991282, 0.9959943, - 0.9931012, 0.9904431, 0.9880143, 0.9858095, 0.9838233, - 0.9820506, 0.9804866, 0.9791264, 0.9779656, 0.9769995, - 0.9762239, 0.9756346, 0.9752274, 0.9749984, 0.9749437, - 0.9750595, 0.9753422, 0.9757881, 0.9763936, 0.9771553, - 0.9780698, 0.9791338, 0.9803439, 0.981697 , 0.8287214, - 0.9902111, 0.9902111, 0.9902111, 0.9902111, 0.9902111, - 0.9902111, 0.9902111, 0.9902111, 0.9902111, 0.9902111, - 0.9902111, 0.9902111, 0.9902111, 0.9902111, 0.9902111, - 0.9902111, 0.9902111, 0.9902111, 0.9902111, 0.9902111, - 0.9902111, 0.9902111, 0.9902111, 0.9902111, 0.9902111] +DiscFac_timevary = [ + 1.064914, + 1.057997, + 1.051422, + 1.045179, + 1.039259, + 1.033653, + 1.028352, + 1.023348, + 1.018632, + 1.014198, + 1.010037, + 1.006143, + 1.002509, + 0.9991282, + 0.9959943, + 0.9931012, + 0.9904431, + 0.9880143, + 0.9858095, + 0.9838233, + 0.9820506, + 0.9804866, + 0.9791264, + 0.9779656, + 0.9769995, + 0.9762239, + 0.9756346, + 0.9752274, + 0.9749984, + 0.9749437, + 0.9750595, + 0.9753422, + 0.9757881, + 0.9763936, + 0.9771553, + 0.9780698, + 0.9791338, + 0.9803439, + 0.981697, + 0.8287214, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, + 0.9902111, +] # Survival probabilities over the lifecycle, starting from age 25 -LivPrb = [ 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , - 1. , 1. , 1. , 1. , 1. , # <-- automatic survival to age 65 - 0.98438596, 0.98438596, 0.98438596, 0.98438596, 0.98438596, - 0.97567062, 0.97567062, 0.97567062, 0.97567062, 0.97567062, - 0.96207901, 0.96207901, 0.96207901, 0.96207901, 0.96207901, - 0.93721595, 0.93721595, 0.93721595, 0.93721595, 0.93721595, - 0.63095734, 0.63095734, 0.63095734, 0.63095734, 0.63095734] +LivPrb = [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, # <-- automatic survival to age 65 + 0.98438596, + 0.98438596, + 0.98438596, + 0.98438596, + 0.98438596, + 0.97567062, + 0.97567062, + 0.97567062, + 0.97567062, + 0.97567062, + 0.96207901, + 0.96207901, + 0.96207901, + 0.96207901, + 0.96207901, + 0.93721595, + 0.93721595, + 0.93721595, + 0.93721595, + 0.93721595, + 0.63095734, + 0.63095734, + 0.63095734, + 0.63095734, + 0.63095734, +] # Standard deviations of permanent income shocks by age, starting from age 25 -PermShkStd = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, -0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, -0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, # <-- no permanent income shocks after retirement -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +PermShkStd = [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.0, + 0.0, + 0.0, # <-- no permanent income shocks after retirement + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, +] # Standard deviations of transitory income shocks by age, starting from age 25 -TranShkStd = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, -0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, -0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0, # <-- no transitory income shocs after retirement -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +TranShkStd = [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.0, + 0.0, + 0.0, # <-- no transitory income shocs after retirement + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, +] # Age groups for the estimation: calculate average wealth-to-permanent income ratio # for consumers within each of these age groups, compare actual to simulated data -empirical_cohort_age_groups = [[ 26,27,28,29,30 ], - [ 31,32,33,34,35 ], - [ 36,37,38,39,40 ], - [ 41,42,43,44,45 ], - [ 46,47,48,49,50 ], - [ 51,52,53,54,55 ], - [ 56,57,58,59,60 ]] - -initial_wealth_income_ratio_vals = [0.17, 0.5, 0.83] # Three point discrete distribution of initial w -initial_wealth_income_ratio_probs = [0.33333, 0.33333, 0.33334] # Equiprobable discrete distribution of initial w -num_agents = 10000 # Number of agents to simulate -bootstrap_size = 50 # Number of re-estimations to do during bootstrap -seed = 31382 # Just an integer to seed the estimation +empirical_cohort_age_groups = [ + [26, 27, 28, 29, 30], + [31, 32, 33, 34, 35], + [36, 37, 38, 39, 40], + [41, 42, 43, 44, 45], + [46, 47, 48, 49, 50], + [51, 52, 53, 54, 55], + [56, 57, 58, 59, 60], +] + +initial_wealth_income_ratio_vals = [ + 0.17, + 0.5, + 0.83, +] # Three point discrete distribution of initial w +initial_wealth_income_ratio_probs = [ + 0.33333, + 0.33333, + 0.33334, +] # Equiprobable discrete distribution of initial w +num_agents = 10000 # Number of agents to simulate +bootstrap_size = 50 # Number of re-estimations to do during bootstrap +seed = 31382 # Just an integer to seed the estimation # ----------------------------------------------------------------------------- # -- Set up the dictionary "container" for making a basic lifecycle type ------ # ----------------------------------------------------------------------------- # Dictionary that can be passed to ConsumerType to instantiate -init_consumer_objects = {"CRRA":CRRA_start, - "Rfree":Rfree, - "PermGroFac":PermGroFac, - "BoroCnstArt":BoroCnstArt, - "PermShkStd":PermShkStd, - "PermShkCount":PermShkCount, - "TranShkStd":TranShkStd, - "TranShkCount":TranShkCount, - "T_cycle":TT, - "UnempPrb":UnempPrb, - "UnempPrbRet":UnempPrbRet, - "T_retire":retirement_t, - "T_age":TT+1, - "IncUnemp":IncUnemp, - "IncUnempRet":IncUnempRet, - "aXtraMin":aXtraMin, - "aXtraMax":aXtraMax, - "aXtraCount":aXtraCount, - "aXtraExtra":[aXtraExtra,aXtraHuge], - "aXtraNestFac":exp_nest, - "LivPrb":LivPrb, - "DiscFac":DiscFac_timevary, - 'AgentCount':num_agents, - 'seed':seed, - 'tax_rate':0.0, - 'vFuncBool':vFuncBool, - 'CubicBool':CubicBool - } - - -if __name__ == '__main__': +init_consumer_objects = { + "CRRA": CRRA_start, + "Rfree": Rfree, + "PermGroFac": PermGroFac, + "BoroCnstArt": BoroCnstArt, + "PermShkStd": PermShkStd, + "PermShkCount": PermShkCount, + "TranShkStd": TranShkStd, + "TranShkCount": TranShkCount, + "T_cycle": TT, + "UnempPrb": UnempPrb, + "UnempPrbRet": UnempPrbRet, + "T_retire": retirement_t, + "T_age": TT + 1, + "IncUnemp": IncUnemp, + "IncUnempRet": IncUnempRet, + "aXtraMin": aXtraMin, + "aXtraMax": aXtraMax, + "aXtraCount": aXtraCount, + "aXtraExtra": np.array([aXtraExtra, aXtraHuge]), + "aXtraNestFac": exp_nest, + "LivPrb": LivPrb, + "DiscFac": DiscFac_timevary, + "AgentCount": num_agents, + "seed": seed, + "tax_rate": 0.0, + "vFuncBool": vFuncBool, + "CubicBool": CubicBool, +} + + +if __name__ == "__main__": print("Sorry, EstimationParameters doesn't actually do anything on its own.") print("This module is imported by StructEstimation, providing calibrated ") print("parameters for the example estimation. Please see that module if you ")