Solving the Huggett (1996) Model¶
First, let's review the model setup.
- Code can be modified easily.
- In a research project, you want to try different functional forms, calibration targets, ...
- Model objects are OOP objects.
- Code can be maintained easily.
- In a research project, it may take years from first submission to accepted paper.
- Code needs to be readable.
- Structure is important. It is not enough for the code to run.
- Small functions that do one thing.
- Locally trivial.
- Code still runs in a couple of years.
- This is where package management is important.
- Fix versions of dependencies.
- Code is correct.
- Everything needs to be tested.
- Tests run automatically after code is modified.
- For maintanability: test the small functions.
Strategy: Writing the Code¶
This will be a combination of bottom-up and top-down.
Typically, we start with the model elements: demographics, preferences, ...
We make a
struct for each block that holds the parameters and can do some basic things. Examples:
- the utility function computes utility (we have already done that)
- the endowments can draw labor endowments at birth and simulate them over time
Once we have most of these building blocks in place, we start to write the solution algorithm.
- some of the building block code will still be "scaffolding" at this stage
- we don't know yet how we need the details to work, so we defer them
For each building block, we
- make a
- write a function that initializes the
- write tests
This knows parameters such as life-span, retirement age. Easy.
Holds the efficiency grid with transition probabilities.
Needs to be able to give probability distribution over tomorrow's labor efficiencies.
We keep that very simple: the same linear grid for each age. We can always swap out the implementation later by defining a new
Needs to be able to compute \(k'\) from consumption and the other way around.
Also holds the age efficiency profile (it's simpler here than in endowments).
This is a tricky one: how do we want to store the solution to the model?
These are policy functions. The easiest approach seems to store the continuous approximations of these functions:
- during the retirement phase: \(k' = G(k, t)\) and \(c = H(k, t)\)
- during the work phase: we have one of these for each efficiency grid point (a
So we define object that hold a
WorkerSolution and a
Note: There will probably be quite a bit of overlap between the two that we figure out as we go along. So we will likely make these subtypes of
AbstractSolution at some point.
Model now simply collects all the objects in a container.
We need to be able to construct a model for testing. So each objects gets a
Writing code is an iterative process. You try something. It gets complicated. You redesign.
Writing code is procrastination. You write high level code. When it gets complicated, you just push the complicated bits into functions to be written later. Procrastination is good.
Update your tests as you write code. Your tests should always run.
Sometimes you need to write "stubs" -- functions that "pretend" to do something complicated that you have not written yet.
Refactor incrementally. Refactoring means restructuring code that already works, but is not optimally structured. Do this in tiny steps and make sure your tests are always running.
Writing the Solution Algorithm¶
Now we switch to top-down. The solution is simply backward induction:
function solve_model(m :: Model) sol = initialize_solution(); solve_last_period!(m, sol); for t = T : -1 : 1 if isretired(m, t) solve_retire_period!(m, sol, t); else solve_work_period!(m, sol, t); end end end return sol
Note the procrastination. All the difficult stuff is hidden in function calls. Procrastination is good.
Note that the code is entirely self-explanatory. There is no need for comments.
Next we write the code for each function, starting with
solve_last_period. That's actually the same code as for our permanent income model, so we just copy and paste it.
- The moment you hear "copy and paste" you should think: "I need to package that code so I can reuse it."
- This could (and probably should) be done with the PIH code. But getting this right so it is reasonably general would be quite a bit of work. So we won't do it for this class.