The toolkit accepts z_gridvals as inputs (joint grids) but what about d_grivals?
The reason for my question is the following.
In a model with entrepreneurs, in the spirit of Bruggeman (2021), we may assume that workers choose labor supply whereas entrepreneurs supply a fixed amount of labor \bar{l}. In this setting the state variables are (a,z) and the decision variables are d=(l,e) where l \in \{l_1,..,l_{nl} \} is labor supply and e \in \{0,1\} is occupational choice (0 means worker, 1 means entrepreneur). If I pass to the toolkit d as a stacked column vector,
d_grid = [l_grid; e_grid];
then the code will loop over all n_l \times 2 combinations of l and e, which is useless if e=1 (since entrepreneurs do not choose labor). Can I define a joint-grid over d as an array with n_l+1 rows and two columns, where the first column is l and the second column is e,
The answer should be yes, but currently the answer is no.
Back when I first coded everything I only really knew of stacked-grids (d_grid). I later discovered joint-grids (d_gridvals) when doing lots of work on more advanced exogenous shock processes. I went and recoded everything so that you can use z_gridvals (same for i.i.d. ‘e’ and semi-exogenous ‘semiz’).
I would like to recode d so that you can use d_gridvals at some point, and even a_gridvals. But I have not yet done so.
If you tell me you are starting a project where you want d_gridvals, I will bump it way up my to-do list otherwise, it will be a while
[Note: joint-grids nest everything possible with stacked-column grids (d_gridvals nests d_grid), so the joint-grid approach is just more flexible and powerful. It actually also works nicer with how everything is done on gpu using arrayfun().]
PS. In some places I have already moved some deep internals over to d_gridvals, but only in a few places, would need to be all places.
I do have a project that would benefit from it (the model is similar to Bruggeman 2021).
Replication of Bruggeman (2021) ofc.
It would be interesting to test the improvement in run times. I think for Bruggeman (2021) would be substantial, since n_l=301. In my project I use a smaller grid (in part because the code would be too slow otherwise).
Considering the simplest algorithm, without interpolation, the current return matrix R has size [N_d,N_a,N_a,N_z] over (d,a’,a,z). In Bruggeman (2021), N_z = 25, N_a=480 and N_d=301*2. This is huge and I have to set lowmemory=1. With the modification proposed, N_d = 301+1, right?
So yes, it you have some time, it would be great to include this improvement.
Alternatively, I can try to do a PR at least for the subset I am interested in: infinite horizon. I think I would have to change some of the functions “Createreturnmatrix”, right?
Correct. Some subtle changes when you turn on grid interpolation layer, and again for transition path using divide-and-conquer, but broadly remains the same kind of savings. Because of how the GPU parallelizes this size of savings will appear in memory use, but the runtime gains will likely be notably smaller for most users (depends on how many grid points vs number of cores in your gpu; gpu specs used to tell you this, but now they tend to just describe the FLOPS instead although that should be proportional). Of course, if reduced memory use means you can run lowmemory=0 instead of needing lowmemory=1 the runtime gains will be huge [in FHorz roughly proportional to prod(n_z), but in InfHorz will be an order of magnitude or more].
I will see if I can do it for the InfHorz commands later this week.
Should now work using d_gridvals in place of d_grid for InfHorz (I can run all the stationary eqm code of B2021, and at least the main transtion path eqm code of B2021).
Runtime gains for B2021 were modest, about 8% faster for value fn iteration, and 27% faster for value fn iteration on transition path. Other commands any difference was too small to register.
On the other hand, it cuts memory. This can make no difference to code, or it can be the difference between not running due to out-of-memory and running, or if it means lowmemory=1 to lowmemory=0 then can be over an order of magnitude speed up.
Turned out to be pretty easy as for some time I’ve been rewriting some of the deeper internal commands to accept d_gridvals, and so other than cosmetic changes to the surface codes I only had to meaningfully change one family of commands to add handling d_gridvals.
PS. No current plans to do this for FHorz. Obviously at some stage I will, but not presently planning it any time soon.
Policy still has the same size: [length(n_d)+length(n_aprime),N_a,N_z]
Note: in basic models n_aprime=n_a
But the interpretation/content of Policy(1:length(n_d),:,:) has changed when you use joint-grid.
With a standard stacked-column for d_grid, and (say) n_d=[20,10], the content of Policy(1,:,:) will be the indexes for the first decision variable, so numbers from 1 to 20 [don’t have to cover all numbers from 1 to 20, only those that represent optimal decisions, but all values will of course be in that range]. Policy(2,:,:) will be the indexes for the second decision variable.
In the interest of keeping this example as simple as possible I am going to do something that would be stupid in practice, and use a joint-grid that is identical to the stacked-column grid. [The whole reason to use a joint-grid is to reduce the number of grid points.]
With a joint-grid on d_gridvals, and n_d=[200,1] (so there are 200 grid ‘points’, each of which represents two values, one for the first decision variable and one for the second variable, and size(d_gridvals)=[200,2]) the interpretation/contents of Policy look quite different (even if they represent the exact same policies). Now, Policy(1,:,:) contains the joint-indexes which will take values from 1 to 200 (again, only the ones that are optimal), while the Policy(2,:,:) will just contain ones.
If we compare the two, the Policy(1,:,:) from the joint-grid will be equal to Policy(1,:,:)+n_d(1)*(Policy(2,:,:)-1) from the stacked-column grid. That is, Policy stores the joint-index for decision variables when you use joint-grid d_gridvals, and Policy stores the vector-index for decision variables when you use stacked-column grid d_grid.
One obvious question is why keep the whole Policy(2,:,:) that just contains a whole lot of ones when using joint-grids? The answer is simply that this keeps the exact same shape of Policy (first dimension is still length(n_d)+lenght(n_aprime)) and so was much easier to implement. In an ideal world I would probably drop all these ones, but that would have required a much larger rewrite of all the codes and has only miniscule performance implications (both for runtime and memory).
All of this brings me to @aledinola second post. The implication of the above is probably now obvious. If you use joint-grid d_gridvals instead of stacked-column d_grid you will get the exact same V, StationaryDist, AllStats and PolicyValues, but the Policy will look different (it will be the exact same optimal policies, just encoded differently). Which is so say, what you saw was not an error, it is the intended behaviour.
PS. The contents of Policy(length(n_d)+1:length(n_d)+length(n_aprime),:,:) remain unchanged regardless of which type of grid you use for the decision variables.