Transition path with demographic transition

Hi Robert,

Thank you for providing this toolkit and the forum. It’s a great community!

Question: Can the transition module in the toolkit deal with the demographic transition?

In particular, if we consider a transition path similar to CK1999 but with population aging, then the “mew_j” i.e., the “AgeWeightParamNames” option in TransitionPath_Case1_Fhorz changes for each period along the transition path. However, it seems like there’s no place to input this changing mew_j along the transition path.

A similar case is, for example, a reform of delaying retirement age that happens some time in the transition path. Then the “I_j”, i.e., the indicator of working or not, along the transition changes. However, the setting of “ParamPath” seems to only allow for the aggregate parameter sequence along the transition, e.g., the alpha in the Aiyagarri model or the retirement age in my example. But the “I_j” changes too along the transition if there is a reform of the retirement age. It is possible to change the individual level parameter for each period along the transition path?

Thanks!
Simon :smiley:

1 Like

Hi Simon,

short answer is yes and yes, can do both :smiley:

I will begin with how to do a ‘reform of delaying retirement age that happens some time in the transition path’. I take it that the I_j you refer to relates to the examples based on Imrohoroglu, Imrohoroglu & Jones (1995) and Conesa & Kreuger (1999) [for anyone else, there is an indicator variable that depends on age, so a vector, and is one when working age and zero when retired]. From VFI Toolkit perspective this is not the cleanest way to do a retirement age, it is just because that is what those papers did. Better is to just use a vector-valued-parameter that is age (could be model period, or the actual real world age that corresponds to) which I will call ‘agej’ and then a scalar-parameter that is retirement age that I will call ‘Jr’. Retirement will occur when agej>=Jr, and inside the return function you can just use an ‘if agej<Jr’—‘else’—‘end’ statement. This is explained and an example is given in my Intro to Life-Cycle Models, specifically LifeCycleModel2 introduces retirement.

Because retirement is now controlled by the parameter Jr, you can easily set up the transition. Just put in ParamPath.Jr the actual transition that you want to model. For example imagine Jr=38, and I want to model a reform that announces that the retirment age will increase to 39 in 5 years time, and then to 40 in another 5 years. I would set ParamPath.Jr=[38*ones(1,5), 39*ones(1,5), 40*ones(T-10)] where T is the number of periods of the transition (needs to be large enough to reach the new stationary equilibrium, but short so as to reduce run time).

It is possible to do the demographic transition as well, although it is very slightly more advanced. But there are no examples at the moment that do what is required. I will try and knock one up over the next few days to show how it works.

I should mention that transition paths in OLG can be reasonably fast if you can set the transpathoptions.fastOLG=1 but this does require a powerful gpu (as of 2022). You can solve them with transpathoptions.fastOLG=0 on the same gpu requirements as are needed for the stationary general equilibrium, but this is not as fast.

Thanks for your detailed reply, Robert!

I was too focused on the I_j and forget the way to change the budget constraint according to Jr. Thanks for your reminder. Once the I_j issue is captured by the Jr, it is easy to set the sequence of ParamPath.Jr as you mentioned.

As for the demographic transition, my idea is to add at least one more loop to the “AgeWeightParamNames” part in the transition module such that it can input the entire demographic matrix, for example, a J*T matrix. But this may cause some extra dimension issues in other sub-modules called by the transition module. I’ll try my way and look forward to your solution. Thanks for your time again.

You mentioned in another post that the transpathoptions.fastOLG=1 can be used by GPU with about 12+gb of GDDR ram and you are using NVIDIA Tesla A100 for testing. Otherwise, there comes the out-of-memory issue. There is only 8 GB in my GPU. Thanks for your suggestion, but my workstation does not support this fast option. What a pity haha!

Best

You have almost described how to do the demographic transition, namely put in a ParamPath.mewj where mewj is a J-by-T matrix (finite horizon-by-transition length). VFI Toolkit will automatically realise this is an ‘age dependent parameter’ across the transition and handle everything appropriately.

Because in this case the particular age-dependent parameter is the age weights you also need to tell it that AgeWeightParamNames={‘mewj’}.

There is no need for any ‘loop’. VFI Toolkit handles the rest internally.

Note that you could do the ParamPath.I_j as a J-by-T matrix. It would work, but is just rather clunky/ugly.

Note however that the above approach of changing mewj is problematic in the sense that while everyone understands that the demographics change, noone thinks their own conditional survival probabilities will change. In that sense it is no longer rational expectations. (VFI Toolkit would solve it, but it is not really a properly constructed model; not that all models must have rational expectations, but I am sure you understand what I mean.)

To do things properly we therefore need to change sj, and n, and then make the appropriate changes to mewj. I will create an example based on this but I find it reassuring that you were able to intuit the main idea around how it will work (a J-by-T matrix). Means the toolkit design makes sense :slight_smile:

One other thing. In your original post you refer to ‘aggregate parameters’ like that in the Cobb-Douglas production function. I feel like a quick clarification will be useful. VFI Toolkit does not understand any distinction between micro and macro parameters, they are all just parameters (stored in a structure that I typically call Params). You then tell it which parameters to use where when you do things like giving the names of all the inputs to the ReturnFn, or the inputs to the FnsToEvaluate, or to AgeWeightParamNames. For example, the parameters that you name in GEPriceParamNames are understood to be the ones that are chosen to satisfy the general equilibrium equations. From the perspective of the code you can send any parameter anywhere.

Thanks Robert!

Great, I will try this way and see what happens.

Haha, this is the way I wrote before I created this post yesterday. I do find it ugly. The transition does work. But since I don’t monitor the step-by-step computation in the transition module, I’m just not sure whether the transition module can read the ParamPath.I_j (J by T) matrix appropriately. Given you confirm it, I feel relieved. In addition, the summation module, i.e., EvalFnOnTransPath_AggVars_Case1_FHorz will come into a dimension issue after introducing the J-by-T ParamPath.I_j. It report error in

EvalFnOnTransPath_AggVars_Cases1_FHorz. (line 62)
ParamPath(:,ii)=gpuArray({aramPathStruct.(ParamPathNames{ii}));

The cause of the error is easy to identify. When the ParamPathNames is the ParamPath.I_j , the dimension of the left-hand side is T-by-1 while that of the right-hand side is T-by-J. I may have not fully utilized the advantage of the toolkit and will change the RetureFn according to Jr. Therefore the T-by-J I_j is not a problem for me. But I guess in the summation module, the dimension of ParamPath should be slightly modified such that it can allow for the aggregation of the non-T-by-1 parameters along the transition.

That’s true. In many papers, they just fix the survival probability to the initial level, which is definitely inappropriate. But there do exist other papers that input the survival probability from the data or projection of UN Population Prospects along the transition, for example, Song et al. 2015AEJ-macro. Even some papers allow for endogenous survival probability to capture the personal health investment. But that is another topic.

The toolkit design does a good job of allowing for demographic change. Looking forward to your example.

Thanks for your clarification. I understand this design in the toolkit. I refer to ‘aggregate parameters, e.g., alpha, is just want to make a distinction between the cohort-level parameter, e.g., the I_j or exogenous age-dependent labor productivity, and the aggregate level parameter. I am sure you understand what I mean. :smiley:

One quick question before wrapping up, could I divide the computer memory to the GPU memory in order to support the transpathoptions.fastOLG=1 option?

Best,

I expect the error is with EvalFnOnTransPath_AggVars_Case1_FHorz, I will take a look and fix it on Monday. The J-by-T matrices in transitions is a new feature (implemented at start of this year). I normally like to create a few examples and a replication involving new features before I publicize them. Helps as a form of quality assurance.

It is not possible to use cpu memory with the gpu (the gpu uses ‘GDDR’ while the cpu uses ‘DDR’, they are different concepts, and located in physically different places in your computer). What I typically do is write code on a computer with a ‘standard gpu’ and use small grids, then once I feel like I have things how I want them I run a final version with larger grids on a server with a more powerful gpu (if you work at a university they probably have a high-performance computer/server/cluster of some kind with high-end gpus that you can get access to and send your final codes there to run).

Endogenous survival is another way of saying endogenous exit. VFI Toolkit can do this for infinite horizon problems, but not for finite horizon. It cannot do it full-stop for transitions. I will happily implement it if I get the impression it is a feature people want to see (I implement most features because I think they are interesting, but there are hundreds of things I could add so I also respond to what other people seem interested in seeing). Obviously the same code would be relevant to suicide (which from the models perspective is just the alternative to the endogenous survival; not how I would personally think about it but one view). There is probably an interesting macro paper about deaths-of-despair in there somewhere.

As you say, you and I both recognise the intelligence of the distinction between aggregate and micro parameters. I just wanted to clarify that this is not how the toolkit thinks, it isn’t as smart as us :wink:

1 Like

Thanks Robert!

I fully understand it. Writing a toolkit is huge work. Thanks for your effort.

Got it. True, this is just for testing. The final project will be run on the server.

The Intro to Life-Cycle Models project is already a very good one to introduce more and more features into a benchmark OLG model. True, a similar code can apply to the decision of extending life or shortening it. They are just the two sides of a mirror.

Thanks again and look forward to your example.
Have a great weekend. :wink:

1 Like

Hi Robert, I have just solved this problem and the demographic transition.

I can send my solution to your email. However, my solution may be clunky and not general enough for a toolkit. :sweat_smile:

Example of Demographic Transition in an OLG: GitHub - robertdkirkby/demographictransitionOLG: Example of demographic transition in OLG model

Starts with using data on the initial distribution of ages (mewj) a path on the ‘growth rate of the population of age 1’ (n) and a path on the conditional survival probabilites (sj). It uses UpdateAgeWeights() to calculate the implied path for the distribution of ages.

By simply putting these into ParamPath, e.g. ParamPath.sj as a N_j-by-T matrix, and then running the transition path command everything is then handled automatically :smiley:

Near the bottom of the script it plots the ‘demographic pyramids’ for both the initial and final age-distributions of agents. At the very bottom it contains a kind-of-pointless graph that just illustrates that mewj is now encoded in the agent distributions.


Phew, that turned into a bit of a mission!

First, I just broke all the ‘EvalOnTransPath’ commands (the old ones still exists as _Direct). Transition paths are now treated in line with stationary general equilibrium: (i) solve for the prices (TransPath commands), (ii) solve for V and Policy, (iii) solve for agent distribution, (iv) EvalOnTransPath, now using PolicyPath and AgentDistPath as inputs. [I updated the GL2017 and CK1999 examples to this. See the ‘topic’ dedicated to explaining the change on this forum.]

In the end I realised it was better to create a seperate function to create the age weights, rather than create and destroy them inside the TransPath command because the user will almost always be interested in seeing them, plus you might need them later for other things. Hence there is now an UpdateAgeWeights() command.

This is the first example to show off the feature of using J-by-T matrices in ParamPath which VFI Toolkit automatically interprets as being a transition on an age-dependent parameter and parses things appropriately internally.

This is also the first example to show off the feature of ‘_tminus1’ to access lagged variables when calculating transition paths (there is also ‘_tplus1’). Specifically it uses the ‘_tminus1’ value of the AggVar that is the accidental bequests (bequests are left at the end of last period, and received before the beginning of this period). You just use the same name of the variable, but with _tminus1 on the end of it, so AccidentalBeqLeft is the variable, and AccidentalBeqLeft_tminus1 gives you the last period value. VFI Toolkit can handle both _tminus1 and _tplus1, for any variable in PricePath or ParamPath, and can handle _tminus1 for any variable in AggVars. This is already implemented in both the FHorz and InfHorz versions of the transition path commands.

The good news is I now feel like transition paths cover most of the major features, so I am unlikely to break anything again (the only thing that I broke was EvalOnTransPath commands). That change to EvalOnTransPath commands is something I have felt like I should bite the bullet on for a while.

I can send my solution to your email. However, my solution may be clunky and not general enough for a toolkit.

I’m guessing you looped over T (transition time periods) and for each of them just used the standard evaluate on agent distribution commands? That is essentially what I just do internally anyway :wink: just with more cleaning and reorganising. There is no shortage of low-hanging fruit in terms of improving the internal functioning of these transition path commands, but I feel like first is getting them all up and running smoothly, and later I can think about refining them.

Robert, thanks so much!

It’s a huge load for a week. Thanks for your time and effort! :clap:

I run the example and find, first, in the UpdateAgeWeights.m, the line 33 should be "mewj=[mewj1; sj(1:end-1,1:siez(sj,2)).*mewjlag(1:end-1)']; ". Otherwise, it reports “Arrays have incompatible sizes for this operation”. I understand you want to project a population matrix along the transition path. Maybe this works in your Matlab versions. My Matlab version is 2021b. Not a big deal. :wink:

[quote=“robertdkirkby, post:10, topic:172”]
This is also the first example to show off the feature of ‘_tminus1’ to access lagged variables when calculating transition paths (there is also ‘_tplus1’).
[/quote]. Got it. True, this is the first example that I learn about this advantage of the Toolkit. However, in line 440, the transition module converges in one iteration, which should not be the case in fact. Is there anything wrong for the transition computation? My toolkit version is the lastest one from GitHub. BTW, there’s a small typo in line 419, i.e., the name of the AccidentalBeqLeft. It should be AccidentalBeqLeft_tminus1. I’ve fixed it.

[quote=“robertdkirkby, post:11, topic:172”]
I’m guessing you looped over T (transition time periods) and for each of them just used the standard evaluate on agent distribution commands?
[/quote] The similar logic as what you said. The original internal function can only deal with the T-by-1 ParamsPath when doing the summation work. I just add a few lines to tell the internal functions what to do when the second dimension of the ParamsPath is greater than 1.

Simon

in the UpdateAgeWeights.m, the line 33 should be "mewj=[mewj1; sj(1:end-1,1:size(sj,2)).*mewjlag(1:end-1)'];

This appears for me as line 21 of UpdateAgeWeights_raw.m, and this is where it is on the version on github. I can see you refer to transposing mewjlag but I didn’t have to (just ran the DemogTransOLG.m from scratch to here and it worked fine). Pointless aside, rather than 1:size(sj,2), you would be better with just 1:end. Given this was about transposing matrices the version of Matlab should be irrelevant (mine is currently 2021a). In UpdateAgeWeights it already checks the sizes of sj and mewjlag and transposes them if needed to ensure that sj is N_J-by-T, n is 1-by-T, and mewjlag is N_j-by-1 and gives an error message if they are not so there should not be any issues there. Since I cannot reproduce this error I am not going change anything.

there’s a small typo in line 419, i.e., the name of the AccidentalBeqLeft. It should be AccidentalBeqLeft_tminus1.

Line 419 of DemogTransOLG.m? That line says transpathoptions.GEnewprices=3 in the version on github. I am not sure what you refer to so have not changed it. Please let me know where exactly you meant.

However, in line 440, the transition module converges in one iteration, which should not be the case in fact.

I set the transpathoptions.tolerance=5*10^(4)!!! hahaha! :sweat_smile:, should of course be 10^(-4). Have updated the DemogTransOLG. Obviously the distance was less than 50000 on the first iteration so it just stopped.

Thanks, Robert.

It’s Okey. We both see know the methods to project the current and future population.

Sorry for the confusion. It refers to “transpathoptions.initialvalues.AccidentalBeqLeft=Params_initial.AccidentBeq/(1+Params_initial.n);”. When running the transition, it reports “Failed To Find Parameter AccidentalBeqLeft_tminus1” and repeats forever unless to terminate the program. In the definition of TransPathGeneralEqmEqns.bequests , it is the “AccidentalBeqLeft_tminus1” that inside the bracket. I define the AccidentalBeqLeft_tminus1 into the structure variable Params,then the wrong message is gone.

Haha, I saw it yesterday and just want to confirm it with you. :wink:

I got two more small questions. No need for extra example codes which are big missions. I just try to describe the solutions in my mind and see what potential problems would come across using the current version of the toolkit from your view.

The first question is that, if we want to input the TFP growth rate into the transition such that the aggregate variables produced along the transition can match the real-world data, the only thing to do is just set the TFP growth rate to be another ParamsPath. Am I right?
Of course, in the ReturenFn.m, the prime should be multiplied by 1+TFPgrowthrate depending on the form of budget constraints, just like your example in the OLG package, model 22.

Second, if we try to endogenies the retirement decision, according to my understanding of this toolkit, I should change the decision grid to, for example, n_d=[51,2], the first 51 refers to the labor grid, the second 2 refer to retirement decision grid, say, retire or not. Then with appropriate setting for the ReturnFn, GE condition, etc, the toolkit can deal with this extension for both the steady state and transition path. Am I right?

Thanks.
Simon

The first question is that, if we want to input the TFP growth rate into the transition such that the aggregate variables produced along the transition can match the real-world data, the only thing to do is just set the TFP growth rate to be another ParamsPath. Am I right? Of course, in the ReturnFn.m, the prime should be multiplied by 1+TFPgrowthrate depending on the form of budget constraints, just like your example in the OLG package, model 22.

Roughly, yes. The trick is that the model has to be ‘divided by 1+g’ so that it becomes stationary and can then be solved. What we can then do is think of assets as being ‘assets per technology unit’ (like you would in a Solow Growth model). When solving it we then just solve as normal but with the aprime*(1+g) in the ReturnFn as you mention (and in anything else relevant, e.g. if you wanted to have a function that was consumption). The solution to the model is then in ‘assets per technology unit’ terms, and you could always reflate them by multiplying everything by (1+g) to get it back into actual assets. The example using Imrohoroglu, Imrohoroglu & Joines (1995) includes adding deterministic growth as an extension (they do not do transitions).

Second, if we try to endogenies the retirement decision

To endogenise the retirement decision. You are close. It needs to be an additional endogenous state, so that your decisions actually can depend on whether or not you are retired (and you can choose to retire because you choose aprime). So you would have n_a=[101,2] (I made up 101 points for assets, but you get the idea). Notice that in principle this also allows choosing to exit retirement, but if you don’t want this it is easy to set just putting into the ReturnFn something like “if a=retired and aprime=not-retired then F=-Inf”.

Thanks Robert.

I got the idea and read your replication in IIJ (1995). Since in the IIJ example there’s no transition, the only concern in my mind is that, the value of the technology term, e.g., A_t, may become extremely large after a long time transition, especially for those developing countries with fast growth. But I think I make it clear, most of the variables of interests are ratio, like, K/Y ratio, both terms consists of A_t. The explosive value of A_t does not matter.

You remind me that there is another state for the assets of retired and not-retired agents, not only the labor state.

True, even in the recent paper, e.g., Wu & Krueger (2021), they allow the agent to make this retirement or retirement-exit decision every period, which is sort of wired. But in computation, this setting is easier to implement. Thanks for your suggestion, now I see it.

Best

But I think I make it clear, most of the variables of interests are ratio, like, K/Y ratio, both terms consists of A_t. The explosive value of A_t does not matter.

Yeah exactly. Like in the Solow model, while Y, K and L all ‘explode’, there is a balanced growth path and so we convert everything to ratios, like Khat=K/(AL), and can then find a solution in the model in ratios. Once that is done if we do want the original K it is just our solution Khat multiplied by AL which contains the growth.

Note that many papers do not do this in the model though. Because if all of your data is already removing these trend (either because you filter the data in some way or because you are looking at ratios or shares in the data) then you don’t need these trends in the model.


Normally if you have an irreversible retirement decision that is something you can take advantage of computationally to speed things up (one less decision to consider once you enter the retirement state). Because of how VFI Toolkit works it is unable to take advantage of this (toolkit is not smart enough), so modeling the decision to exit retirement adds zero computational difficulty when using the toolkit (whereas it would if you hand-craft the codes).

I see it. Thanks for your patience and help, Robert. :smiley:

Good evening Robert,

  When running the replciation program of RestucciaUrrutia2004, in the calculation module for AggVars, it reports

Unrecognized function or variable ‘ValuesOnSSGrid_Case2’.

Error in EvalFnOnAgentDist_AggVars_FHorz_Case2_AgeDepGrids (line 48)
Values=reshape(ValuesOnSSGrid_Case2(FnsToEvaluateFn{i}, FnToEvaluateParamsVec,PolicyValuesPermute,n_d,n_a,n_z,a_grid,z_grid,2),[N_a*N_z,1]);

Error in EvalFnOnAgentDist_AggVars_FHorz_Case2 (line 9)
AggVars=EvalFnOnAgentDist_AggVars_FHorz_Case2_AgeDepGrids(StationaryDist, PolicyIndexes, FnsToEvaluateFn, Parameters,FnsToEvaluateParamNames, n_d, n_a, n_z, N_j, d_grid, a_grid, z_grid, options, AgeDependentGridParamNames);

Error in RestucciaUrrutia2004 (line 290)
AggVars=EvalFnOnAgentDist_AggVars_FHorz_Case2(StationaryDist, Policy, FnsToEvaluate, Params, FnsToEvaluateParamNames, n_d, n_a, n_z,N_j, d_gridfn, a_gridfn, z_gridfn, simoptions, AgeDependentGridParamNames);

 I doublecheck that there is no a variable ValuesOnSSGrid_Case2 generated internally. Since the problem persists after I test it using both the lastest version of the toolkit and the version that you released with this replication file, I guess there may be a function of this name missing in the toolkit.

 Another question related to this issue is that,  when I try to add one more endogenous state of asset like what you mentioned, 

the computation for policy function, stationary distribution is Okey. However, in the module for AggVars, it reports
Error using gpuArray/subsref
Index exceeds matrix dimension.

Error in PolicyInd2Val_FHorz_Case1 (line 69)
PolicyValues(l_d+end,:)=temp_a_grid(PolicyIndexes(l_d+end,:));

Error in EvalFnOnAgentDist_AggVars_FHorz_Case1 (line 147)
PolicyValues=PolicyInd2Val_FHorz_Case1(PolicyIndexes,n_d,n_a,n_z,N_j,d_grid,a_grid);

Error in EndoRetireTest (line 199)
AggVars=EvalFnOnAgentDist_AggVars_FHorz_Case1(StationaryDist_init, Policy_init, FnsToEvaluate, Params, , n_d, n_a, n_z,N_j, d_grid, a_grid, z_grid);

That why I go back to read the only replication code, the RU2004, with more than one endogenous states and then come across the first problem. I check context of the function PolicyInd2Val_FHorz_Case1 and understand that you allow for the first endogenous state variable, say, asset, to fit into its corresponding policy value. So do the rest endogenous state variables. But in my test, l_d=1, and the “end” means 3. Imagine a one decision variable, say, labor, and two endogenous state variables, say, asset and retirement state, than the max row dimension for PolicyValues or PolicyIndex is 3. That is why it reports “Index exceeds matrix dimension.”

My understanding is that it should be " PolicyValues(l_d+end-1,:)=temp_a_grid(PolicyIndexes(l_d+end-1,:));", then it can capture the rest endogenous state variables other than asset.

To make it clear and in order to reproduce this problem, I attach the link of my short sample code as follows.

Best,

For how to do two endogenous states check out: Two Endogenous States (a variables)

The Restuccia & Urrutia (2004) does something much more complicated, namely ‘age dependent grids’. Notice that there are two time periods, and that the problem is very different in each period, especially as the states (and from memory even the number of states) are completely different. ‘age-dependent grids’ is how VFI Toolkit handles this but it is quite complicated and not widely implemented (for example, you cannot use it in transition paths). Essentially after that replication I decided ‘age-dependent grids’ were not a feature I wanted to focus on (I would like to in some hypothetical future, but not at present).

If you desperately want Restuccia & Urrutia (2004) I am happy to clean it up. But if you are just interested in how to do two endogenous states check that example I mentioned earlier, or I can create an example in a life-cycle model from scratch (will not take me long, much faster than RU2004).

[It should be that if you download VFI Toolkit as the historical version that coincides with date of the RU2004 replication then it works. I reran all the replications as part of my paper Quantitative Economics: Lessons Learned from Fourteen Replications, so I guess just use VFI Toolkit from the date at which the published version became available online. Updating all the replications to use version 2 of VFI Toolkit is on my wishlist, but ]