diff --git a/Documentation/CHANGELOG.md b/Documentation/CHANGELOG.md index dbcbf0103..55f365c7b 100644 --- a/Documentation/CHANGELOG.md +++ b/Documentation/CHANGELOG.md @@ -36,6 +36,7 @@ Release Date: TBD - Expands functionality of Cobb-Douglas aggregator for CRRA utility. [#1363](https://github.com/econ-ark/HARK/pull/1363) - Adds a new function for using Tauchen's method to approximate an AR1 process. [#1521](https://github.com/econ-ark/HARK/pull/1521) - Adds additional functionality to the CubicHermiteInterp class, imported from scipy.interpolate. [#1020](https://github.com/econ-ark/HARK/pull/1020/) +- Adds an example, ModelConfiguration, which demonstrates how a consumption saving "true" model, approximations, solution paramaters, and simulation parameters can be defined in one YAML file. [1463](https://github.com/econ-ark/HARK/pull/1463) ### 0.15.1 diff --git a/HARK/models/fisher.py b/HARK/models/fisher.py index aae27cbf9..fd5d94c4b 100644 --- a/HARK/models/fisher.py +++ b/HARK/models/fisher.py @@ -4,14 +4,10 @@ from HARK.model import Control, DBlock -# This way of distributing parameters across the scope is clunky -# Can be handled better if parsed from a YAML file, probably -# But it would be better to have a more graceful Python version as well. -CRRA = (2.0,) calibration = { "DiscFac": 0.96, - "CRRA": CRRA, + "CRRA": (2.0,), "Rfree": 1.03, "y": [1.0, 1.0], "BoroCnstArt": None, @@ -25,6 +21,6 @@ "c": Control(["m"]), "a": lambda m, c: m - c, }, - "reward": {"u": lambda c: c ** (1 - CRRA) / (1 - CRRA)}, + "reward": {"u": lambda c, CRRA: c ** (1 - CRRA) / (1 - CRRA)}, } ) diff --git a/HARK/models/perfect_foresight.py b/HARK/models/perfect_foresight.py index 8a54bfb2d..971c2d9ee 100644 --- a/HARK/models/perfect_foresight.py +++ b/HARK/models/perfect_foresight.py @@ -1,16 +1,11 @@ from HARK.distributions import Bernoulli from HARK.model import Control, DBlock -# This way of distributing parameters across the scope is clunky -# Can be handled better if parsed from a YAML file, probably -# But it would be better to have a more graceful Python version as well. -LivPrb = 0.98 - calibration = { "DiscFac": 0.96, "CRRA": 2.0, "Rfree": 1.03, - "LivPrb": LivPrb, + "LivPrb": 0.98, "PermGroFac": 1.01, "BoroCnstArt": None, } @@ -19,7 +14,7 @@ **{ "name": "consumption", "shocks": { - "live": Bernoulli(p=LivPrb), + "live": Bernoulli(p=calibration["LivPrb"]), }, "dynamics": { "y": lambda p: p, diff --git a/HARK/models/perfect_foresight_normalized.py b/HARK/models/perfect_foresight_normalized.py index 2b63dec3c..73d12b61b 100644 --- a/HARK/models/perfect_foresight_normalized.py +++ b/HARK/models/perfect_foresight_normalized.py @@ -1,17 +1,11 @@ from HARK.distributions import Bernoulli from HARK.model import Control, DBlock -# This way of distributing parameters across the scope is clunky -# Can be handled better if parsed from a YAML file, probably -# But it would be better to have a more graceful Python version as well. -CRRA = (2.0,) -LivPrb = 0.98 - calibration = { "DiscFac": 0.96, - "CRRA": CRRA, + "CRRA": (2.0,), "Rfree": 1.03, - "LivPrb": LivPrb, + "LivPrb": 0.98, "PermGroFac": 1.01, "BoroCnstArt": None, } @@ -19,7 +13,7 @@ block = DBlock( **{ "shocks": { - "live": Bernoulli(p=LivPrb), + "live": Bernoulli(p=calibration["LivPrb"]), }, "dynamics": { "p": lambda PermGroFac, p: PermGroFac * p, @@ -29,6 +23,6 @@ "c_nrm": Control(["m_nrm"]), "a_nrm": lambda m_nrm, c_nrm: m_nrm - c_nrm, }, - "reward": {"u": lambda c: c ** (1 - CRRA) / (1 - CRRA)}, + "reward": {"u": lambda c, CRRA: c ** (1 - CRRA) / (1 - CRRA)}, } ) diff --git a/examples/ModelConfiguration/consumer_example.py b/examples/ModelConfiguration/consumer_example.py new file mode 100644 index 000000000..8367c3c11 --- /dev/null +++ b/examples/ModelConfiguration/consumer_example.py @@ -0,0 +1,84 @@ +from HARK.distribution import Bernoulli, Lognormal, MeanOneLogNormal +from HARK.model import Control + +""" +This is an example of what a full configuration looks like for: + +(a) a true model + +(b) approximations made to it to enable efficient computation + +(c) additional parameters needed to solve and simulate the model. + +This file shows the model configuraiton in Python. + +Another file will show a model configuration in YAML, which gets parsed into +a data structure much like this one. +""" + +model_config = { + "agents": {"consumer": {"count": 10}}, + "calibration": { + "DiscFac": 0.96, + "CRRA": 2.0, + "R": 1.03, # note: this can be overriden by the portfolio dynamics + "Rfree": 1.03, + "EqP": 0.02, + "LivPrb": 0.98, + "PermGroFac": 1.01, + "BoroCnstArt": None, + "TranShkStd": 0.1, + }, + "blocks": [ + { + "name": "consumption normalized", + "shocks": { + "live": [Bernoulli, {"p": "LivPrb"}], + "theta": [MeanOneLogNormal, {"sigma": "TranShkStd"}], + }, + "dynamics": { + "b": lambda k, R, PermGroFac: k * R / PermGroFac, + "m": lambda b, theta: b + theta, + "c": Control(["m"]), + "a": lambda m, c: m - c, + }, + "reward": {"u": lambda c, CRRA: c ** (1 - CRRA) / (1 - CRRA)}, + }, + { + "name": "portfolio", + "shocks": { + "risky_return": [ + Lognormal.from_mean_std, + {"mean": "Rfree + EqP", "std": 0.1}, + ] + }, + "dynamics": { + "stigma": Control(["a"]), + "R": lambda stigma, Rfree, risky_return: Rfree + + (risky_return - Rfree) * stigma, + }, + }, + { + "name": "tick", + "dynamics": { + "k": lambda a: a, + }, + }, + ], + "approximation": { + "theta": {"N": 5}, + "risky_return": {"N": 5}, + }, + # "workflows": [ + # {"action": "solve", "algorithm": "vbi"}, + # { + # "action": "simulate", + # "initialization": { # initial values # type: ignore + # "k": Lognormal(-6, 0), + # "R": 1.03, + # }, + # "population": 10, # ten agents + # "T": 20, # total number of simulated periods + # }, + # ], +} diff --git a/examples/ModelConfiguration/consumer_example.yaml b/examples/ModelConfiguration/consumer_example.yaml new file mode 100644 index 000000000..2de7621ad --- /dev/null +++ b/examples/ModelConfiguration/consumer_example.yaml @@ -0,0 +1,90 @@ +model: &consumer_portfolio + agents: + consumer: + count: 100 # a model with 100 consumers + + calibration: + # - DiscFac: 0.96 put this in the solution section + - CRRA: 2.0 + - R: 1.03 # note: this can be overriden by the portfolio dynamics + - Rfree: 1.03 + - EqP: 0.02 + - PermGroFac: 1.01 + - TranShkStd: 0.1 + + blocks: + - &cons # block definition begins with name of the block. Alias + name: consumption_normalized + agent: consumer + shocks: + theta: !MeanOneLogNormal + sigma: TranShkStd + + dynamics: + b: k * R / PermGroFac + m: b + theta, + c: !Control + inset: m + constraints: + - c < m + a: m - c + reward: + u: c ** (1 - CRRA) / (1 - CRRA)} + + - &port + name: portfolio + agent: consumer + shocks: + risky_return: !Lognormal + mean: Rfree + EqP + std: 0.1 + + dynamics: + stigma: Control(["a"]), + R: Rfree + (risky_return - Rfree) * stigma + + structure: # the sequence of blocks + - *cons + - *port + - twist: # shorthand fo renaming a variable + m: k + - tick # pass discrete time step + # defaults to infinite horizion problem + +approximation: &approx # right syntax? + theta: # to discretize a continuous probability distribution + N : 5 + method: equiprobable + risky_return: + N: 5 + method: gauss-hermite + m: !Linspace # grid over continuous state space + start: 0 + stop: 10 + num: 25 + + +strategy: # these configure constructors/solvers of decision rules + # for model control variables + - &test_policy_profile + model: *consumer_portfolio + method: hard_rule + rules: + - c : m / 2 + - stigma : a / (5 + a) + + - &approx_best_policy_profile + model: *consumer_portfolio + method: policy_iteration + approximation: *approx + discount: 0.98 # A discount factor applied at each tick + +simulation: !MonteCarlo + initialization: + k: !Lognormal + mean: 1 + sigma: 0 + R: 1.03 + approximation: *approx # use the same shock discretization + policy_profile: *approx_best_policy_profile + periods: 100