HARK
HARK copied to clipboard
Proposed constructed input framework
HARK models as basic as ConsIndShock use some "constructed inputs" for their solver, by which I mean objects that are passed to the solve_one_period
function but would not have been manually specified by the user. For example, IncShkDstn
is a complicated (time-varying) object that could (in principle) be manually created by a user and assigned to an instance of IndShockConsumerType
, but more reasonably would be constructed from "primitive parameters" like PermShkStd
, TrankShkStd
, UnempPrb
, etc. As-is, PermGroFac
is expected to be provided by the user, but it in principle it could be constructed on the AgentType
instance given some parametric description of the income growth profile.
Each AgentType
subclass has "baked in" constructors, which are called from (or simply part of) an update_X
method. The update
method itself is supposed to call all of those constructors, as a universal way to bring constructed inputs up-to-date with any changes made to the primitive parameters. It does so by explicitly calling each update_X
method, sometimes from its parent class.
As is, if a user wants to have a different way of constructing such an input, they can either a) do it themselves and then drop in the result into their instances; or b) make a trivial subclass of the AgentType
that overrides that constructor method. Neither of these are "nice", and there's no organized way for a new user to find out what the required "primitive inputs" are. I'm going to fix this, and this issue presents an overview of my proposal for doing so.
I propose that AgentType
instances have a new constructors
dictionary. The keys of this dictionary would be the names of inputs to the solve_one_period
function for that class; the value for each key would be a constructor function whose inputs name the primitiver (or at least more primitive) parameters that are used by that constructor; those parameters would live as attributes on the instance (or in the parameters
dictionary once that's fully implemented).
The top-level AgentType
would have a universal method (e.g. called construct
) that automatically runs some or all of the functions in the constructors
dictionary. If the user passes one or more strings to this method, it runs the constructors for the named parameters. If the user passes nothing to the method, it runs all of the constructors.
When a constructor is run by construct
, it tries to pull in the parameters named in its arguments (from the agents or its parameters
dictionary), passes them to the function, and then assigns the output to that key (in the parameters
dictionary or the agent itself, depending on what stage of development we're at).
If construct
fails to find the necessary primitive parameters, it flags that constructor and records the names of the missing parameters. It then proceeds through the other constructors that have been requested. After it finishes all of them, if at least one was flagged as incomplete, it tries again, because new data might be available. If construct
completes a "pass" with no constructors being successful, then it quits. The names of the "missing data" are stored or returned.
This structure makes it much easier and simpler for a user to change the construction method for a solver input, to make an input constructed, or to indicate that an input is not constructed (just delete its entry from constructors
!). E.g. if a user decided that the income process they want to use has binary permanent shocks (characterized by the probability and value of one of the shocks), they can just change one function name in their IndShockConsumerType
instances and provide parameters called (e.g.) LowPermShkVal
and LowPermShkPrb
rather than PermShkStd
, PermShkCount
, etc.
It also makes it easier for users to find out what primitive parameters / data are needed to successfully run construct
. We can write a very simple query method that lists all of the inputs for each constructor, and which ones are currently missing (as well as related methods for non-constructed solver inputs).
There are two kinks in the design that I need to work out. First, how to handle allowing the user to specify that some input is time-varying vs not time-varying-- and how to indicate that the solver will not function properly if some inputs are improperly converted to time-varying. The simplest solution is to enforce at the class level which solver inputs can be time-varying, and require that the user pass a constant list if they want it to be time-invariant for their purposes (maybe with a method to handle that automatically).
Second, how to handle situations where it's most natural to construct two solver inputs simultaneously-- they're biproducts of the same process. I can think of a slightly clunky and inelegant workaround, but my "slightly clunky" is others' "hideously bad design".