Adding new decisions to LifeCycleModel35semiz

I’m interested in adding a decision to install rooftop solar on top of a house in LifeCycleModel35semiz.

I’ve added installpv as a binary choice and solarpv as a scalable generation asset (scalable from 0 to 30 kWh).

n_d=[11,101,5,2]; % Decisions: riskyshare, savings, buyhouse (note, SemiExoStateFn hardcodes that buyhouse is 5 points), installpv
n_a=[3,21,7]; % Endogenous housing, asset, and solarpv holdings (0-30kW generation)

I’m now stuck on the proper definition of refine_d

vfoptions.refine_d=[0,1,1,1]; % tell the code how many d1, d2, d3 and d4 there are

If I add to d1 I get a message that riskyasset+semiz with d1 not yet implemented.

If I add to d4, I get an error about incompatibly-shaped arrays. I do pass installpv to the SemiExoStateFn (mainly to keep the price of the house from falling when installing PV). And I use solarpv in the return function (where it adds to income, like an asset).

Do I need to wait for the new feature or should I reshape what I’m doing to better fit with the existing system?

What kind of endogenous state is solarpv supposed to be? In any case this won’t work with current toolkit implementations.

My impression: you don’t need riskyasset for anything (do you?). I would set n_a=[21,3,7] as two standard endogenous states for ‘assets’ and ‘house’ and one experienceasset for ‘solarpv’. n_d=2, as the ‘installpv’ (the action space will include next period ‘assets’ and next period ‘house’). This should run okay, but would not currently work for using divide-and-conquer or grid interpolation layer on the ‘assets’ (can be implemented).

2 Likes

Thanks for the D-C tip and the experienceasset reminder. I’ll adapt accordingly. And indeed the riskyasset probably is a distraction–I’m much more interested in the housing and solar questions (including the stochastic behavior of housing prices and energy prices). Risky assets are a dimension I don’t need to interrogate right now.

1 Like

If we want to have both renters and homeowners, I think n_d should be [5, 2] (buyhouse and installpv), no?

If ‘house’ is a standard endogenous state, then it is something you can choose the ‘next period house’ for, and does not need to appear in ‘d’ (if it is in ‘a’, and a standard endogenous state, then it appears as ‘aprime’).

I was working from the semiz example. In that case should it be as I said?

Yes, if it is a semi-exogenous shock then the decision variable that controls it needs to be in n_d.

But why do you want house as a semi-exogenous shock, not as an endogenous state? Not that you can’t, but that it is only worthwhile if you have a reason for wanting to do so.

I like the pattern you created of fluctuating housing prices, so am building off that. I could simplify down to LifeCycleModel35 (without semiz). Thanks for confirming!

I note that ValueFnIter_FHorz_RiskyAsset_semiz cleverly skips past the (d,u) inputs when it is sussing out aprimeFnParamNames (line 15).

This means that at line 145, when it calls ValueFnIter_FHorz_RiskyAsset_nod1_semiz_raw the only aprime parameter name is r, which is in Parameters.

However, ValueFnIter_FHorz_ExpAssetSemiExo only strips out d3 and a2 parameter names, leaving the a1 parameters to be found by CreateVectorFromParams. But a and h (the a1 parameters) are not in Params, so the program triggers a warning that the parameters are not found.

Why would we not skip past the a1 parameter names, too, and not just the d3 and a2 parameter names?

I think I can answer my own question: experience asset aprime functions are their own things and do not cover other assets generally. So it drives what could be considered an a2prime function. Yes?

If so, then we have a kind of a name clash, with vfoptions.experienceasset depending upon vfoptions.aprime being set, but then we cannot have a proper aprime function in the event we want to do more complex aprime-y stuff with a1.

Currently it is not possible to have more than one endogenous state that uses an ‘aprimeFn’ setup. I think this is what you are observing.

In principle it would be easy enough to have ‘aprime1Fn’ and ‘aprime2Fn’, or whatever they would be called, but this has not yet been done anywhere so what they would be called is up in the air.

1 Like

I’m puzzling over code in EvaluateFnOnAgentDist\FHorz\LifeCycleProfiles_FHorz_Case1.m and specifically what happens after consolidating semiz, z, and e (via the call to CreateGridvals_FnsToEvaluate_FHorz). When all this become part of a z_gridvals_J then the decision function that’s sitting in refine_d(4) should be removed from consideration as well. I think this is the easy way to do it:

% Figure out l_daprime from PolicyValues
l_daprime=size(PolicyValues,1);
if simoptions.experienceasset || simoptions.experienceassetu
    if isfield(simoptions, 'refine_d')
        if length(simoptions.refine_d)==4
            % adjust if there are experienceassets that were also semiz
            l_daprime=l_daprime-simoptions.refine_d(4);
        end
    else
        error("cannot use experienceasset or experienceassetu without simoptions.refine_d");
    end
end

I’ll create a pull request if this is not blatantly wrong.

I’m puzzling over code in EvaluateFnOnAgentDist\FHorz\LifeCycleProfiles_FHorz_Case1.m and specifically what happens after consolidating semiz, z, and e (via the call to CreateGridvals_FnsToEvaluate_FHorz). When all this become part of a z_gridvals_J then the decision function that’s sitting in refine_d(4) should be removed from consideration as well. I think this is the easy way to do it:

Why would you want to do this though? We still want all the d variables, and for evaluating functions on agent dist the content of refine_d is irrelevant.

The problem I’m trying to solve is that without adjusting l_daprime, the now-integrated semiz variable is gone, and when we call arrayfun to calculate the return matrix, the unadjusted l_daprime value requests more da vars than we have to offer. The call to arrayfun happens very soon after this point in the code.

Maybe my mistake is that I need two aprimes, one for the experience asset and one for the semiz variable? Here’s the error message:

Error using EvalFnOnAgentDist_Grid (line 436)
gpuArray/arrayfun encountered an issue while compiling '@(installpv,buyhouse,hprime,a,h,solarpv,pbefore,pafter,yearsowned,olddownpayment,z)a'.

Error in LifeCycleProfiles_FHorz_Case1 (line 476)
                Values(:,:,jj-j1+1)=EvalFnOnAgentDist_Grid(FnsToEvaluate{ff}, FnToEvaluateParamsCell,PolicyValuesPermuteJ(:,:,:,jj),l_daprime,n_a,n_z,a_gridvals,z_gridvals_J(:,:,jj));
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error in ElectrifyHousing (line 369)
AgeConditionalStats=LifeCycleProfiles_FHorz_Case1(StationaryDist,Policy,FnsToEvaluate,Params,[],n_d,n_a,n_z,N_j,d_grid,a_grid,z_grid,simoptions);
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Caused by:
    @(installpv,buyhouse,hprime,a,h,solarpv,pbefore,pafter,yearsowned,olddownpayment,z)a takes at most 11 inputs. 12 were supplied.

If I had both the aprime for installpv and hprime for buyhouse then my function and the parameters would agree (I think).

What is the action space of your model (what are n_d, n_a, n_semiz, n_z, n_e, and if you are using any non-standard endogenous states what are those)?

Have you watched the ‘workshop’ videos? They give an explanation of the action space. That will help you understand what the inputs should be for ReturnFn and FnsToEvaluate.

I haven’t watched them yet. I will.

I have:

n_d=[5,2]; % Decisions: buyhouse (note, SemiExoStateFn hardcodes that buyhouse is 5 points), installpv
n_a=[21,3,5]; % Endogenous asset, housing, and solarpv (0-40kW generation) 
n_semiz=[5,5,ceil(30/p5),3]; % Semi-exog: house prices before/after purchase, years since purchase (one minus this is the 30y duration of mortgages in model periods), and downpayment
n_z=7; % Exogenous labor productivity units shock

There's no n_e

% Set up d for VFI Toolkit (is the two decision variables)
d_grid=[buyhouse_grid; installpv_grid];

% experienceasset is last
a_grid=[asset_grid; house_grid; solarpv_grid];

The idea is that after the install decision, the solarpv asset value becomes non-zero, and it happily creates revenue offsets for electricity costs. Once installed, the solarpv cannot be removed. But it can be upgraded by spending more, up to a limit.

Action space will be: (d1,d2,a1prime,a2prime,a1,a2,a3,semiz1,semiz2,semiz3,semiz4,z,…)
VFI Toolkit will interpret these to be the first inputs in ReturnFn and FnsToEvaluate, and assume anything after this as a parameter which it will look for in the Params structure.
(action space is figured out by looking at n_d,n_a, etc. and vfoptions/simoptions)

[a3prime is missing because it is an experienceasset so cannot be chosen directly, instead is determined based on the aprimeFn]

The idea is that after the install decision, the solarpv asset value becomes non-zero, and it happily creates revenue offsets for electricity costs. Once installed, the solarpv cannot be removed. But it can be upgraded by spending more, up to a limit.

This will all be easy enough to control by setting up aprimeFn and ReturnFn appropriately.

1 Like

Thanks for the clues (and the videos)! This is the magic spell (from OLG/ImrohorogluImrohorogluJoines2003_PhiaprimeFn.m) I thought I was looking for:

n_a=[n_a1,n_a2,n_a3]; % Because of how evaluating fns on gpu works I had to split this vector in three, can now put it back together.
Phi_aprime=sub2ind_homemade(n_a,[kprime_c,ebarprime_c,bprime_c]);

And now I think I understand why multiple aprimeFn functions are not needed: because aprimeFn can see the complete action space (if one sets it up correctly).

However, I also see that IIJ2003 doesn’t use Phi_aprime anywhere I can see, so there’s that. I will see if I can get an aprimeFn that agrees enough with my ReturnFn to give me sensible results.

And thinking more about it, `refine_d` might just interrupt this line of thinking. Hmm.

I was looking at this thread: Mandatory Saving .

And I’ve been thinking about how I can or cannot control my installpv decision. From what I can tell, the experienceasset is very limited in what it can see as far as the action space is concerned. It can see its decision variable (installpv) and its own asset value (solarpv), and of course it can see static values in Params, but it cannot see the housing decisions (buyhouse), so it tries to install PV independently of whether the agent yet owns a house.

I can make the agent reject any state that accumulates solarpv before a house is purchased, but that makes the solarpv asset kind of meaningless.

Alas, I don’t have the credentials needed to access the Zhou paper, so I have only a superficial understanding of what the action space really looks like in that example. But if I do understand correctly, I should ditch the idea of making installpv its own binary decision variable, and consolidate everything into buyhouse where a new value of 5 in a 6-member list could be “installpv in a house that you own”. In which case the experienceasset could test the status of home ownership before deciding to install.

You can just use the ReturnFn to give -Inf whenever someone tries to install solarpv but owns zero house. But let them install solarpv if either they own a house or they choose buyhouse.