econ-ark/HARK

replace AgentType.sim_one_period with a generic Monte Carlo simulator that consumes equations

Opened this issue · 9 comments

This can more explicitly take in equations, and be removed to a HARK.simulation.monte_carlo module

https://github.com/econ-ark/HARK/blob/master/HARK/core.py#L432-L479

This will help with the whiteboard problem #889

cf. #1292

The FrameAgentType was an attempt at this. The problem with it, noted by @mnwhite , is that the configuration of the FrameAgentType is too complex.

https://github.com/econ-ark/HARK/blob/master/examples/FrameAgentType/FrameModels.ipynb

Something simpler, like a dictionary of lambdas, would be easier to read and write.

Here's the off-the-top-of-my-head draft of a format to specify the mechanics of ConsIndShock:

inter = [aNrm, pLvl]
control = [cNrm]
discrete = []
exog = [psi, theta]
param = [gamma, omega, R, J]

dynamics = [
G = gamma * psi,
Rnrm = R / G,
bNrm = Rnrm * aNrm,
mNrm = bNrm + theta,
cNrm = policy(mNrm),
aNrm = mNrm - cNrm,
die(omega),
old = j >= J,
die(old)
]

auxiliary = [
pLvl = G * pLvl,
mLvl = mNrm * pLvl,
cLvl = cNrm * pLvl,
aLvl = aNrm * pLvl,
MPC = policy.der(mNrm)
]

The simulator should be able to take a statement like this and implement it. The inter variables specify what must exist for an agent when it is initialized. The control variables specify the variables that the simulator should expect to exist, probably named like cNrmFunc or stored in policy['cNrm'].

The discrete variables are those that the code should expect to be whole numbers, not continuous values, which might matter for evaluating policy(d, mNrm) where d is discrete.

The exog variables name variables that come from some process exogenous to the agent-- maybe RVs, maybe chosen by someone else, maybe an aggregate. The code just needs to know to expect to be given some way to get or generate things named in exog, which will not be found within dynamics.

Variables named in param are parameters to be pulled from the agent's data, common to a "type". Whether or not each one is time/age-varying is irrelevant for the mechanics of the model. Each instance of an AgentType using this model can specify its own vary list to indicate how parameters should be looked up. In our standard ConsIndShock lifecycle model, omega and gamma would be in vary, while R and J would not.

Note what's not here: state variables. It might be that we need to name the variables that are used as "states" in the sense of "inputs to a policy function", but it might be enough to have them implicit in the dynamics statement.

The statements in dynamics should be self-explanatory. It's a start to finish description of what happens to each agent in one period. The parameter J is the maximum age beyond which an agent can't live.

The contents of auxiliary are optional variables that can be computed along the way, but don't need to be as a required part of simulating the model. Each of these lines would be executed if and only if the accompanying variable is requested in track, with dependencies automatically generated and handled.

There are some additional complications for things like jointly determined variables, but those don't appear in any of our models so far.

Oh, and j is used to mean "age". There would also be t for absolute time, if needed.

Ok, I think it's possible to move incrementally in this direction.

The hardest part is the Python object for equations, I think.

I've got a few aesthetic quibbles but let's start getting the functionality in.

I think I can get part of the way there with #1292 and refactoring the Frame code, as discussed.

dynamics = [
G = gamma * psi,
Rnrm = R / G,
bNrm = Rnrm * aNrm,
mNrm = bNrm + theta,
cNrm = policy(mNrm),
aNrm = mNrm - cNrm,
die(omega),
old = j >= J,
die(old)
]

As we've discussed, one tricky part about this proposal is the syntax for these sorts of 'equations'.
Without a symbolic library, it's hard to do this very cleanly in Python.

This commit is the most lightweight way I can think of doing this without a symbolic library:
d09eb30

It is possible to use inspect.signature to pull out the parameter names and bind them in simulation.
https://docs.python.org/3/library/inspect.html#inspect.signature

Got it. Please see #1296 for an example implementation.