Skip to content

Solving the Huggett (1996) Model

First, let's review the model setup.

Goals

  1. Code can be modified easily.
    • In a research project, you want to try different functional forms, calibration targets, ...
    • Model objects are OOP objects.
  2. 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.
  3. Code still runs in a couple of years.
    • This is where package management is important.
    • Fix versions of dependencies.
  4. 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

Building Blocks

For each building block, we

  • make a struct
  • write a function that initializes the struct for testing
  • write tests

Demographics

This knows parameters such as life-span, retirement age. Easy.

Endowments

Holds the efficiency grid with transition probabilities.

Needs to be able to give probability distribution over tomorrow's labor efficiencies.

Capital grid

We keep that very simple: the same linear grid for each age. We can always swap out the implementation later by defining a new CapitalGrid object.

Budget

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).

Solution

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 Vector of functions).

So we define object that hold a WorkerSolution and a RetiredSolution.

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

The 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 init_test_object() function.

Tips

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.

Note:

  • 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.