General Equilibrium Solver

The toolkit evaluates intermediate equations and general equilibrium conditions in HeteroAgentStationaryEqm_Case1_subfn, around lines 54-73

intermediateEqnsVec(gg)=real(GeneralEqmConditions_Case1_v3g(heteroagentoptions.intermediateEqnsCell{gg}, heteroagentoptions.intermediateEqnParamNames(gg).Names, Parameters));

GeneralEqmConditionsVec(gg)=real(GeneralEqmConditions_Case1_v3(GeneralEqmEqnsCell{gg}, GeneralEqmEqnParamNames(gg).Names, Parameters));

I am concerned that the trick real() can hide some mistakes or inconsistencies. It happened to me in a model where I have these intermediate equations:

heteroagentoptions.intermediateEqns.N_corp=@(L,N_noncorp) L-N_noncorp;
heteroagentoptions.intermediateEqns.K_corp=@(A,K_noncorp) A-K_noncorp;
heteroagentoptions.intermediateEqns.Y_corp=@(K_corp,N_corp,alpha,Z) Z*(K_corp^alpha)*(N_corp^(1-alpha));

In equilibrium it cannot happen that N_corp or K_corp are negative, but during the GE iterations, these two variables might be negative. This is a problem because there is a fractional power in Y_corp and the negative numbers would give complex results in matlab.
The toolkit prevents this from happening by taking real(), but wouldn’t it be better to remove this trick? This would force the user to become aware of the problem, otherwise the code might go on without converging…

In my case I fixed the problem by putting a zero floor to K_corp and N_corp:

heteroagentoptions.intermediateEqns.N_corp=@(L,N_noncorp) max(L-N_noncorp,1e-12);
heteroagentoptions.intermediateEqns.K_corp=@(A,K_noncorp) max(A-K_noncorp,1e-12);

Even better to modify the GE conditions for capital and labor as follows:

GeneralEqmEqns.CapitalMarket = @(r,K_corp,N_corp,alpha,delta,Z) ...
    CorpCapitalMarketResidual(r,K_corp,N_corp,alpha,delta,Z);

and then

function resid = CorpCapitalMarketResidual(r,K_corp,N_corp,alpha,delta,Z)

if ~isfinite(K_corp) || ~isfinite(N_corp) || K_corp <= 0 || N_corp <= 0
    resid = 1e6;
    return
end

resid = r - (alpha*Z*(K_corp/N_corp)^(alpha-1) - delta);

end %end function

The idea is that if either K_corp or N_corp are “bad” (i.e. negative or NaN etc) we do NOT evaluate the condition r - (alpha*Z*(K_corp/N_corp)^(alpha-1) - delta) since it would be pointless and maybe would break the code, but we return a large penalty, so hopefully the GE solver changes the GE variables to avoid these bad values. This second approach has a small downside: it is more verbose, since you have to write the GE conditions as separate m-files. But of course the toolkit does not care whether you pass a GE condition as a one-liner or as a separate function like you would do for ReturnFn.

It would be nice to get some feedback on this problem from other users: what would you do in this case? Do you think the real() shim is a good idea?

You used to need them because, e.g., otherwise while searching for the general eqm wage the optimization would try out a negative wage and cause an error to be thrown ending your optimization.

Nowadays though, this should all be avoidable by using heteroagentoptions.constrainpositive (and .constrain0to1, .constrainAtoB).

I have just deleted the use of real() from HeteroAgentStationaryEqm_Case1_subfn.m.

We will see over the next few months if this seems to work fine. If it is going okay I will remove real() from the other general eqm commands in August.

2 Likes

Yes, but sometimes you have acceptable values for the GE prices and still not admissible values for intermediate equations. The new options restrict the domain of the GE prices only (as it should be).

In my example above I had positive values for r and w but N_corp turned out to be negative, which makes (K_corp/N_corp)^alpha undefined (since K_corp/N_corp becomes negative and alpha is not integer).

In our Bruggeman 2021 example:

GEPriceParamNames={'r','w','tau_s','G','ybar'} % r,w positive
heteroagentoptions.intermediateEqns.N_corp=@(L,N_noncorp) L-N_noncorp; % This might be negative
heteroagentoptions.intermediateEqns.K_corp=@(A,K_noncorp) A-K_noncorp; % This might be negative
heteroagentoptions.intermediateEqns.Y_corp=@(K_corp,N_corp,alpha,Z) Z*(K_corp^alpha)*(N_corp^(1-alpha));

Clearly, if N_corp or K_corp are negative, Y_corp is undefined (complex number in Matlab)

N_corp =

   -0.4890

>> K_corp

K_corp =

    1.1665

>> Y_corp

Y_corp =

  -0.3316 + 0.5608i

For completeness, the small test that generated the example above:

clear,clc,close all

alpha = 0.33;
delta = 0.1;
Z = 1;
L=1;
A=3.5;
N_noncorp = 1.489;
K_noncorp = 2.3335;

f_N_corp=@(L,N_noncorp) L-N_noncorp; % This might be negative
f_K_corp=@(A,K_noncorp) A-K_noncorp; % This might be negative
f_Y_corp=@(K_corp,N_corp,alpha,Z) Z*(K_corp^alpha)*(N_corp^(1-alpha));

N_corp = f_N_corp(L,N_noncorp);
K_corp = f_K_corp(A,K_noncorp);
Y_corp = f_Y_corp(K_corp,N_corp,alpha,Z);

What is your suggestion based on this? Drop real() from intermediateEqn but keep real() on GeneralEqmEqn valuation?

Or I guess I can just further modify the heteroagentoptions.verbose=2 so that you see the intermediateEqn values before the GeneralEqmEqn ones are computed (rather than just at the end of the iteration as current)

1 Like

I don’t have any good suggestion, I’m afraid :slight_smile:

I would leave the toolkit code as it is now, without real() anywhere. It’s up to the user to take action is something goes bad.

2 Likes

Seems to be going fine. I have now removed real() from all the other stationary general eqm commands.

I will wait until October to see that everything is going fine with them. If yes, then I will go clean up the comments from the old behaviour, and then make the same changes to the calibrate/estimate commands. I need to implement parameter constraints for transition paths before I can make this change to transition paths.

1 Like