IndShockConsumerTypeNumba

Since the last post I’ve worked on a numba version of IndShockConsumerType in IndShockNumbaModel.py. The class IndShockConsumerTypeNumba is the same as IndShockConsumerType, but now I use a “flat” numba optimized solvers called ConsIndShockNumbaSolverBasic and ConsIndSchockSolverNumba.

ConsIndShockNumbaSolverBasic

The method prepareToSolve implements the machinery of ConsIndShockSetup. Beyond the steps that have been described before to translate code from standard python to numba, this method didn’t need much alteration.

solveConsIndShockNumba in ConsIndShockNumbaSolverBasic

The method solve implements the machinery of ConsIndShockSolverBasic. I will describe the few modifications that were needed in detail.

The first hurdle was due to the fact that numba does not yet have optimized implementations of np.tile and np.insert. With the hopes that these methods are implemented in the future, I wrote two basic ‘placeholders’ that take the same parameters in the exact same format, and instead uses a combination of other numpy methods that numba does support. For example, the way np.insert is being used is to insert values into array before index 0, which can be done instead by converting values to an array, then appending array at the end. If and when numba implements these two methods, the code can be changed by adding np.- back to the call.

The next big hurdle was to replace the call to vPfuncNext, a HARK object, in calcEndOfPrdvP. After inspection, it appears that vPfuncNext is a MargValueFunc, which is itself an instance of LowerEnvelope. What MargValueFunc does is take a cFunc and apply the function c**-gam, so I can just do that myself, assuming I can calculate c first. This is where LowerEnvelope comes into place, since cFunc is an instance of LowerEnvelope and thus composed of several functions. Assuming I can get cFunc.x_list and cFunc.y_list (for now, will touch on this later), I can linearly interpolate cFuncNext given mNrmNext, using interp from interpolation.py.

Doing this, however, was not as easy as expected. I may have found a bug (or perhaps a feature request) on the method interp. Calling interp(x_list,y_list,values) where values is an ndarray (more than 1 dimension), returned only an array output (1 dimension) which appeared to only interpolate along the first value of axis 0. More clearly, if values.shape = (x,y), then output.shape = (x,). Clearly, this is not what I need, so to fix this issue, I flatten values, then reshape the output. The final code looks like this:

cFuncNext = interp(sn_cFunc_x_list, sn_cFunc_y_list, mNrmNext.flatten())
vPfuncNext = (cFuncNext ** -CRRA).reshape(mNrmNext.shape)

As I mentioned before, I assumed the existence of cFunc.x_list and cFunc.y_list, which are inputs to the solveConsIndShockNumba method. Since cFunc is an instance of LowerEnvelope, these attributes do not exist directly, therefore, I need to create them. For cFunc.x_list, I simply used self.aXtraGrid, which is the resource grid. For cFunc.y_list I unfortunately need to call cFunc(cFunc_x_list), which uses the internals of LowerEnvelope to linearly interpolate over cFunc_x_list. This linear interpolation is not numba optimized, and may be a bottleneck to the code.

Alan Lujan
Alan Lujan
PhD Candidate in Economics

My research interests include poverty and inequality, household finance, and computational economics.

Related