From e68079d57cda4d1ccfc7a88548f865dad416df3c Mon Sep 17 00:00:00 2001 From: sb Date: Mon, 20 May 2024 15:06:44 -0400 Subject: [PATCH 1/8] new model dynamics can be expressed as a math string; these get parsed into a function. see #1373 --- HARK/model.py | 6 ++++++ HARK/models/consumer.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/HARK/model.py b/HARK/model.py index 46f695523..879b5c14e 100644 --- a/HARK/model.py +++ b/HARK/model.py @@ -4,6 +4,7 @@ from dataclasses import dataclass, field from HARK.distribution import Distribution +from HARK.parser import math_text_to_lambda from typing import List @@ -50,6 +51,11 @@ class DBlock: dynamics: dict = field(default_factory=dict) reward: dict = field(default_factory=dict) + def __post_init__(self): + for v in self.dynamics: + if isinstance(self.dynamics[v], str): + self.dynamics[v] = math_text_to_lambda(self.dynamics[v]) + def get_shocks(self): return self.shocks diff --git a/HARK/models/consumer.py b/HARK/models/consumer.py index ede5184b2..9a4d2aa0b 100644 --- a/HARK/models/consumer.py +++ b/HARK/models/consumer.py @@ -53,7 +53,7 @@ "b": lambda k, R: k * R / PermGroFac, "m": lambda b, theta: b + theta, "c": Control(["m"]), - "a": lambda m, c: m - c, + "a": 'm - c' }, "reward": {"u": lambda c, CRRA: c ** (1 - CRRA) / (1 - CRRA)}, } From 7e236420cede03c723828d1cb920721f76251502 Mon Sep 17 00:00:00 2001 From: sb Date: Mon, 20 May 2024 16:41:42 -0400 Subject: [PATCH 2/8] ruff fix --- HARK/models/consumer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HARK/models/consumer.py b/HARK/models/consumer.py index 9a4d2aa0b..b9992de91 100644 --- a/HARK/models/consumer.py +++ b/HARK/models/consumer.py @@ -53,7 +53,7 @@ "b": lambda k, R: k * R / PermGroFac, "m": lambda b, theta: b + theta, "c": Control(["m"]), - "a": 'm - c' + "a": "m - c", }, "reward": {"u": lambda c, CRRA: c ** (1 - CRRA) / (1 - CRRA)}, } From ba35d5af740044e143fa26e10ed57a46ebbf0d28 Mon Sep 17 00:00:00 2001 From: sb Date: Mon, 20 May 2024 16:43:06 -0400 Subject: [PATCH 3/8] adding the parser module --- HARK/parser.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 HARK/parser.py diff --git a/HARK/parser.py b/HARK/parser.py new file mode 100644 index 000000000..22072c470 --- /dev/null +++ b/HARK/parser.py @@ -0,0 +1,28 @@ +from sympy.utilities.lambdify import lambdify +from sympy.parsing.sympy_parser import parse_expr + +class Expression(): + + def __init__(self, text): + self.txt + self.expr = parse_expr(text) + self.npf = self.func() + + # first derivatives. + self.grad = { + sym.__str__() : + self.expr.diff(sym) + for sym + in list(self.expr.free_symbols) + } + + def func(self): + return lambdify(list(self.expr.free_symbols), self.expr, "numpy") + +def math_text_to_lambda(text): + """ + Returns a function represented by the given mathematical text. + """ + expr = parse_expr(text) + func = lambdify(list(expr.free_symbols), expr, "numpy") + return func \ No newline at end of file From 217108499b3a58435f02e2b71cfd7d0f79fbf04f Mon Sep 17 00:00:00 2001 From: sb Date: Mon, 20 May 2024 16:50:59 -0400 Subject: [PATCH 4/8] ruff fix --- HARK/parser.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/HARK/parser.py b/HARK/parser.py index 22072c470..61fcba539 100644 --- a/HARK/parser.py +++ b/HARK/parser.py @@ -1,8 +1,8 @@ from sympy.utilities.lambdify import lambdify -from sympy.parsing.sympy_parser import parse_expr +from sympy.parsing.sympy_parser import parse_expr -class Expression(): +class Expression: def __init__(self, text): self.txt self.expr = parse_expr(text) @@ -10,19 +10,17 @@ def __init__(self, text): # first derivatives. self.grad = { - sym.__str__() : - self.expr.diff(sym) - for sym - in list(self.expr.free_symbols) + sym.__str__(): self.expr.diff(sym) for sym in list(self.expr.free_symbols) } def func(self): return lambdify(list(self.expr.free_symbols), self.expr, "numpy") + def math_text_to_lambda(text): """ Returns a function represented by the given mathematical text. """ expr = parse_expr(text) func = lambdify(list(expr.free_symbols), expr, "numpy") - return func \ No newline at end of file + return func From 75fdb070a6892e6d00e6e99b01ec5d2d17510676 Mon Sep 17 00:00:00 2001 From: sb Date: Sat, 22 Jun 2024 09:33:23 -0400 Subject: [PATCH 5/8] ruff --- HARK/model.py | 2 ++ HARK/tests/test_model.py | 1 + 2 files changed, 3 insertions(+) diff --git a/HARK/model.py b/HARK/model.py index 4cab86141..054ee5e19 100644 --- a/HARK/model.py +++ b/HARK/model.py @@ -14,6 +14,7 @@ from HARK.parser import math_text_to_lambda from typing import Any, Callable, Mapping, List, Union + class Aggregate: """ Used to designate a shock as an aggregate shock. @@ -249,6 +250,7 @@ def mod_dvf(shock_value_array): return arrival_value_function + @dataclass class RBlock: """ diff --git a/HARK/tests/test_model.py b/HARK/tests/test_model.py index 728749cf7..f5b2db434 100644 --- a/HARK/tests/test_model.py +++ b/HARK/tests/test_model.py @@ -72,6 +72,7 @@ def test_arrival_value_function(self): av({"k": 1, "R": 1.05, "PermGroFac": 1.1, "theta": 1, "CRRA": 2}) + class test_RBlock(unittest.TestCase): def setUp(self): self.test_block_B = model.DBlock(**test_block_B_data) From 9287eff50bb012613b5a042ec2d5769d6e54189d Mon Sep 17 00:00:00 2001 From: sb Date: Sat, 22 Jun 2024 09:36:41 -0400 Subject: [PATCH 6/8] update changelog for #1427 --- Documentation/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/CHANGELOG.md b/Documentation/CHANGELOG.md index 284a08933..49b860a50 100644 --- a/Documentation/CHANGELOG.md +++ b/Documentation/CHANGELOG.md @@ -8,13 +8,14 @@ For more information on HARK, see [our Github organization](https://github.com/e ## Changes -### 0.15.2 (in development) +### 0.16.0 (in development) Release Date: TBD #### Major Changes -none +- Allows structural equations in model files to be provided in string form [#1427](https://github.com/econ-ark/HARK/pull/1427) +- Introduces `HARK.parser' module for parsing configuration files into models [#1427](https://github.com/econ-ark/HARK/pull/1427) #### Minor Changes From fd62ccbd55735f9efc9a8e54ddd7aee079327eb7 Mon Sep 17 00:00:00 2001 From: sb Date: Sat, 22 Jun 2024 09:50:44 -0400 Subject: [PATCH 7/8] adding back in PermGroFac into normalized b equation --- HARK/models/consumer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HARK/models/consumer.py b/HARK/models/consumer.py index b9992de91..148c81457 100644 --- a/HARK/models/consumer.py +++ b/HARK/models/consumer.py @@ -50,7 +50,7 @@ "theta": MeanOneLogNormal(sigma=TranShkStd), }, "dynamics": { - "b": lambda k, R: k * R / PermGroFac, + "b": lambda k, R, PermGroFac: k * R / PermGroFac, "m": lambda b, theta: b + theta, "c": Control(["m"]), "a": "m - c", From a9b04072b865f0bc2b3400425479f89933f9a3bf Mon Sep 17 00:00:00 2001 From: sb Date: Sat, 22 Jun 2024 10:02:16 -0400 Subject: [PATCH 8/8] docstring for dynamics param to DBlock, mentioning string option --- HARK/model.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/HARK/model.py b/HARK/model.py index 054ee5e19..1816460fd 100644 --- a/HARK/model.py +++ b/HARK/model.py @@ -144,7 +144,16 @@ class DBlock: Parameters ---------- - ... + shocks: Mapping(str, Distribution) + A mapping from variable names to Distribution objects, + representing exogenous shocks. + + dynamics: Mapping(str, str or callable) + A dictionary mapping variable names to mathematical expressions. + These expressions can be simple functions, in which case the + argument names should match the variable inputs. + Or these can be strings, which are parsed into functions. + """ name: str = ""