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

introducing DBlock, working it into the ATMC simulator, see #1371 #1411

Merged
merged 5 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Release Date: TBA

- Replace object-oriented solvers with single function versions. [1394](https://github.com/econ-ark/HARK/pull/1394)
- Object-oriented solver code has been moved to /HARK/ConsumptionSaving/LegacyOOsolvers.py, for legacy support of downstream projects.
- AgentTypeMonteCarloSimulator now requires model shock, parameter, and dynamics information to be organized into 'blocks'. The DBlock object is introduced. [#1411](https://github.com/econ-ark/HARK/pull/1411)

### Minor Changes

Expand Down
21 changes: 21 additions & 0 deletions HARK/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Tools for crafting models.
"""

from dataclasses import dataclass, field
from HARK.distribution import Distribution


Expand All @@ -28,3 +29,23 @@ class Control:

def __init__(self, args):
pass


@dataclass
class DBlock:
"""
Represents a 'block' of model behavior.
It prioritizes a representation of the dynamics of the block.
Control variables are designated by the appropriate dynamic rule.

Parameters
----------
...
"""

name: str = ""
description: str = ""
shocks: dict = field(default_factory=dict)
parameters: dict = field(default_factory=dict)
dynamics: dict = field(default_factory=dict)
reward: dict = field(default_factory=dict)
36 changes: 19 additions & 17 deletions HARK/models/fisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@
A model file for a Fisher 2-period consumption problem.
"""

from HARK.model import Control
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,)

model = {
"shocks": {},
"parameters": {
"DiscFac": 0.96,
"CRRA": CRRA,
"Rfree": 1.03,
"y": [1.0, 1.0],
"BoroCnstArt": None,
},
"dynamics": {
"m": lambda Rfree, a, y: Rfree * a + y,
"c": Control(["m"]),
"a": lambda m, c: m - c,
},
"reward": {"u": lambda c: c ** (1 - CRRA) / (1 - CRRA)},
}
block = DBlock(
**{
"shocks": {},
"parameters": {
"DiscFac": 0.96,
"CRRA": CRRA,
"Rfree": 1.03,
"y": [1.0, 1.0],
"BoroCnstArt": None,
},
"dynamics": {
"m": lambda Rfree, a, y: Rfree * a + y,
"c": Control(["m"]),
"a": lambda m, c: m - c,
},
"reward": {"u": lambda c: c ** (1 - CRRA) / (1 - CRRA)},
}
)
48 changes: 25 additions & 23 deletions HARK/models/perfect_foresight.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
from HARK.distribution import Bernoulli
from HARK.model import Control
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

model = {
"shocks": {
"live": Bernoulli(p=LivPrb),
},
"parameters": {
"DiscFac": 0.96,
"CRRA": CRRA,
"Rfree": 1.03,
"LivPrb": LivPrb,
"PermGroFac": 1.01,
"BoroCnstArt": None,
},
"dynamics": {
"y": lambda p: p,
"m": lambda Rfree, a, y: Rfree * a + y,
"c": Control(["m"]),
"p": lambda PermGroFac, p: PermGroFac * p,
"a": lambda m, c: m - c,
},
"reward": {"u": lambda c: c ** (1 - CRRA) / (1 - CRRA)},
}
block = DBlock(
**{
"name": "consumption",
"shocks": {
"live": Bernoulli(p=LivPrb),
},
"parameters": {
"DiscFac": 0.96,
"CRRA": 2.0,
"Rfree": 1.03,
"LivPrb": LivPrb,
"PermGroFac": 1.01,
"BoroCnstArt": None,
},
"dynamics": {
"y": lambda p: p,
"m": lambda Rfree, a, y: Rfree * a + y,
"c": Control(["m"]),
"p": lambda PermGroFac, p: PermGroFac * p,
"a": lambda m, c: m - c,
},
"reward": {"u": lambda c, CRRA: c ** (1 - CRRA) / (1 - CRRA)},
}
)
48 changes: 25 additions & 23 deletions HARK/models/perfect_foresight_normalized.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
from HARK.distribution import Bernoulli
from HARK.model import Control
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

model = {
"shocks": {
"live": Bernoulli(p=LivPrb),
},
"parameters": {
"DiscFac": 0.96,
"CRRA": CRRA,
"Rfree": 1.03,
"LivPrb": LivPrb,
"PermGroFac": 1.01,
"BoroCnstArt": None,
},
"dynamics": {
"p": lambda PermGroFac, p: PermGroFac * p,
"r_eff": lambda Rfree, PermGroFac: Rfree / PermGroFac,
"b_nrm": lambda r_eff, a_nrm: r_eff * a_nrm,
"m_nrm": lambda b_nrm: b_nrm + 1,
"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)},
}
block = DBlock(
**{
"shocks": {
"live": Bernoulli(p=LivPrb),
},
"parameters": {
"DiscFac": 0.96,
"CRRA": CRRA,
"Rfree": 1.03,
"LivPrb": LivPrb,
"PermGroFac": 1.01,
"BoroCnstArt": None,
},
"dynamics": {
"p": lambda PermGroFac, p: PermGroFac * p,
"r_eff": lambda Rfree, PermGroFac: Rfree / PermGroFac,
"b_nrm": lambda r_eff, a_nrm: r_eff * a_nrm,
"m_nrm": lambda b_nrm: b_nrm + 1,
"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)},
}
)
67 changes: 67 additions & 0 deletions HARK/models/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from HARK.distribution import Lognormal
import HARK.models.perfect_foresight as pfm
import HARK.models.perfect_foresight_normalized as pfnm
from HARK.simulation.monte_carlo import AgentTypeMonteCarloSimulator

from HARK.ConsumptionSaving.ConsIndShockModel import PerfForesightConsumerType

import unittest

PFexample = PerfForesightConsumerType()
PFexample.cycles = 0

SimulationParams = {
"AgentCount": 3, # Number of agents of this type
"T_sim": 120, # Number of periods to simulate
"aNrmInitMean": -6.0, # Mean of log initial assets
"aNrmInitStd": 0, # 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
"T_age": None, # Age after which simulated agents are automatically killed,
"LivPrb": [0.98],
}

PFexample.assign_parameters(**SimulationParams)
PFexample.solve()


class test_pfm(unittest.TestCase):
def setUp(self):
self.mcs = AgentTypeMonteCarloSimulator(
pfm.block,
{"c": lambda m: PFexample.solution[0].cFunc(m)},
# danger: normalized decision rule for unnormalized problem
{ # initial states
"a": Lognormal(-6, 0),
#'live' : 1,
"p": 1.0,
},
agent_count=3,
T_sim=120,
)

def test_simulate(self):
## smoke test
self.mcs.initialize_sim()
self.mcs.simulate()


class test_pfnm(unittest.TestCase):
def setUp(self):
self.mcs = AgentTypeMonteCarloSimulator( ### Use fm, blockified
pfnm.block,
{"c_nrm": lambda m_nrm: PFexample.solution[0].cFunc(m_nrm)},
{ # initial states
"a_nrm": Lognormal(-6, 0),
#'live' : 1,
"p": 1.0,
},
agent_count=3,
T_sim=120,
)

def test_simulate(self):
## smoke test
self.mcs.initialize_sim()
self.mcs.simulate()
19 changes: 8 additions & 11 deletions HARK/simulation/monte_carlo.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
TimeVaryingDiscreteDistribution,
)
from HARK.model import Aggregate, Control
from HARK.model import DBlock


def draw_shocks(shocks: Mapping[str, Distribution], conditions: Sequence[int]):
Expand Down Expand Up @@ -165,11 +166,9 @@ class AgentTypeMonteCarloSimulator(Simulator):

Parameters
------------
parameters: Mapping[str, Any]

shocks: Mapping[str, Distribution]

dynamics: Mapping[str, Union[Callable, Control]]
block : DBlock
Has parameters, shocks, dynamics

dr: Mapping[str, Callable]

Expand All @@ -189,14 +188,12 @@ class AgentTypeMonteCarloSimulator(Simulator):

state_vars = []

def __init__(
self, parameters, shocks, dynamics, dr, initial, seed=0, agent_count=1, T_sim=10
):
def __init__(self, block: DBlock, dr, initial, seed=0, agent_count=1, T_sim=10):
super().__init__()

self.parameters = parameters
self.shocks = shocks
self.dynamics = dynamics
self.parameters = block.parameters
self.shocks = block.shocks
self.dynamics = block.dynamics
self.dr = dr
self.initial = initial

Expand All @@ -205,7 +202,7 @@ def __init__(
self.T_sim = T_sim

# changes here from HARK.core.AgentType
self.vars = list(shocks.keys()) + list(dynamics.keys())
self.vars = list(self.shocks.keys()) + list(self.dynamics.keys())

self.vars_now = {v: None for v in self.vars}
self.vars_prev = self.vars_now.copy()
Expand Down
Loading
Loading