Krusell and Smith (1998): Heterogeneous Agent Models with Aggregate Uncertainty
The original Krusell and Smith (1998) algorithm can be implemented with the toolbox, by transforming the optimization problem of the households to a system of first order conditions and complementarity-slackness conditions.
Though the toolbox is not designed for solving the equilibrium of this type of model directly, since the decision problem is characterized by an equation system (the Euler equation
where \(\lambda_t\) is the Lagrange multiplier on the borrowing constraint, and the complementary-slackness condition, \(\lambda_t k_{t+1}=0\)) with state transition functions, it readily fits in the toolbox’s general framework. One just needs an extra fixed-point loop to update the equilibrium object, which can be coded in MATLAB. In Krusell and Smith (1998), the equilibrium objects are the aggregate state transition coefficients.
The model can be solved within 100 lines of GDSGE code plus 100 lines of MATLAB code. We present the heterogeneous-beta version of the model below.
The gmod File and MATLAB File
ks1998.gmod
(you also need the file for grid points from Krusell and Smith (1998) points.kgd
)
1% Toolbox options
2INTERP_ORDER = 4; EXTRAP_ORDER = 4;
3SIMU_RESOLVE = 0; SIMU_INTERP = 1;
4SaveFreq = inf; PrintFreq = 100; TolEq = 1e-5;
5% Parameters
6parameters alpha delta phi kMin;
7alpha = 0.36; % capital share in production
8delta = 0.025; % depreciation rate
9% phi_k_1 phi_k_0 phi_l_1 phi_l_0; for z in {bad, good}
10phi = [
11 0.96053 0.095014 0 -1.2228
12 0.96072 0.099212 0 -1.1583
13 ]';
14% States, use the ones as in Krusell and Smith (1998)
15var_state kbar k;
16kbar = [11.1 11.52 11.94 12.36];
17k = [0,1]; % A place holder, will be overwritten
18kMin = k(1); % A place holder, will be overwritten
19% Shock process in Krusell and Smith (1998)
20var_shock beta e unemp z z_idx;
21shock_num = 12;
22ez_trans = [
23 0.5250 0.3500 0.0312 0.0938
24 0.0389 0.8361 0.0021 0.1229
25 0.0938 0.0312 0.2917 0.5833
26 0.0091 0.1159 0.0243 0.8507
27 ];
28beta_trans = [
29 0.9950 0.0050 0.0000
30 0.000625 0.99875 0.000625
31 0.0000 0.0050 0.9950
32 ];
33shock_trans = kron(ez_trans,beta_trans);
34z_idx_grid = [0, 1];
35z_grid = [0.99, 1.01];
36e_grid = [0.00, 0.3271];
37unemp_grid = [0.07, 0.00]; % unemployement transfer
38beta_grid = [0.9858 0.9894 0.9930];
39[beta,e,z] = ndgrid(beta_grid,e_grid,z_grid);
40[~,unemp,z_idx] = ndgrid(beta_grid,unemp_grid,z_idx_grid);
41beta=beta(:); e = e(:); unemp = unemp(:);
42z = z(:); z_idx = z_idx(:);
43
44% State transition functions
45var_interp Evp_interp Ev_interp;
46initial Evp_interp 1./(k+e+unemp-kMin);
47initial Ev_interp log(k+e+unemp-kMin);
48% Update
49Evp_interp = shock_trans * vp;
50Ev_interp = shock_trans * v;
51
52% Tensor
53var_tensor kbar_next lbar Y r w budget;
54% Forecasting rule
55kbar_next = exp(log(kbar).*phi(z_idx*4+1) + phi(z_idx*4+2));
56lbar = exp(log(kbar).*phi(z_idx*4+3) + phi(z_idx*4+4));
57% Interst rate and wage
58Y = z .* kbar.^alpha .* lbar.^(1-alpha)
59r = alpha*Y./kbar - delta;
60w = (1-alpha)*Y./lbar;
61budget = k.*(1+r) + e.*w + unemp;
62
63% Endogenous variables
64var_policy k_next lambda;
65inbound k_next kMin budget;
66inbound lambda 0 1.0;
67% Other variables out of the block
68var_aux c vp v l;
69% Used in simulation
70var_output c k_next l;
71
72model;
73 c = budget - k_next;
74 u_prime = 1/c;
75 [Evp_future,Ev_future] = GDSGE_INTERP_VEC(shock,kbar_next,k_next);
76 Evp_future = Evp_future;
77 euler_residual = -1 + beta*Evp_future/u_prime + lambda;
78 vp = (1+r)*u_prime;
79 v = log(c) + beta*Ev_future;
80 l = e;
81 equations;
82 euler_residual;
83 lambda*(k_next-kMin);
84 end;
85end;
86
87simulate;
88 num_periods = 11000;
89 num_samples = 10000;
90 initial k 1;
91 initial kbar 1;
92 initial shock 1;
93 var_simu c l;
94 k' = k_next;
95 kbar' = k_next; % A place holder
96end;
97post_simulate_iter;
98 SimuRslt.kbar(:,GDSGE_t+1) = mean(SimuRslt.k(:,GDSGE_t+1));
99 SimuRslt.lbar(:,GDSGE_t) = mean(SimuRslt.l(:,GDSGE_t));
100end;
The MATLAB file that calls the toolbox codes and manually update equilibrium objects main.m
1%% Solve the warm-up problems
2options.k = csvread('points.kgd'); options.kMin = options.k(1);
3IterRslt = iter_ks1998(options);
4
5%% Generate random numbers
6% Parameters
7num_periods = 11000;
8num_samples = 10000; % Need around 8GB to run the simulation
9
10% Transition matrix for aggregate shocks
11shock_trans = IterRslt.shock_trans;
12% Reduction along the z dimension (beta,e,z) -> (beta',e',z')
13shock_trans_zp = sum(reshape(shock_trans,[6,2,6,2]),3);
14z_trans = reshape(shock_trans_zp(1,:,:),[2,2]);
15
16% Aggregate shock
17rng(0729);
18z_idx = gen_discrete_markov_rn(z_trans,1,num_periods,1);
19% Idiosyncratic shock conditional on aggregate shock
20% Permute to (beta,e) -> (beta',e') | (z,z')
21shock_trans_conditional_on_z = permute(reshape(shock_trans,[6,2,6,2]),[1,3,2,4]) ./ reshape(z_trans,[1,1,2,2]);
22ind_shock_idx = gen_discrete_markov_rn_with_agg(shock_trans_conditional_on_z, num_samples, num_periods, 6*ones(num_samples,1), z_idx);
23
24simuOptions.num_samples = num_samples;
25simuOptions.GEN_SHOCK_START_PERIOD = num_periods;
26simuOptions.init.shock = ind_shock_idx + (z_idx-1)*6;
27simuOptions.init.k = 11*ones(num_samples,1);
28simuOptions.init.kbar = 11*ones(num_samples,1);
29simuOptions.EnforceSimuStateInbound = 0;
30
31%% Iterate transition coefficients phi
32phi = [
33 0.96053 0.095014 0 -1.2228
34 0.96072 0.099212 0 -1.1583
35 ]';
36metric_phi = inf;
37tol_phi = 1e-4;
38update_speed = 0.5;
39iter = 0;
40while metric_phi>tol_phi
41 options.phi = phi;
42 options.WarmUp = IterRslt;
43 IterRslt = iter_ks1998(options);
44 SimuRslt = simulate_ks1998(IterRslt,simuOptions);
45
46 % Collect samples and run regression
47 burnPeriods = 1000;
48 periodIndex = 1:num_periods;
49 r2_z = zeros(1,2);
50 rmse_z = zeros(1,2);
51 phi_new = phi;
52 for i_z=1:2
53 periodSet = periodIndex(z_idx==i_z & periodIndex>burnPeriods);
54 sample_lbar = SimuRslt.lbar(1,periodSet);
55 sample_kbar = SimuRslt.kbar(1,periodSet);
56 sample_kbar_next = SimuRslt.kbar(1,periodSet+1);
57 mdl = fitlm(log(sample_kbar),log(sample_kbar_next));
58
59 phi_new(1,i_z) = mdl.Coefficients.Estimate(2);
60 phi_new(2,i_z) = mdl.Coefficients.Estimate(1);
61 phi_new(4,i_z) = log(mean(sample_lbar));
62 r2_z(i_z) = mdl.Rsquared.Ordinary;
63 rmse_z(i_z) = mdl.RMSE;
64 end
65
66 iter = iter+1;
67 metric_phi = max(abs(phi(:) - phi_new(:)));
68 fprintf('iter, metric_phi: %d, %g\n',iter, metric_phi);
69 % Update
70 phi = phi_new*update_speed + phi*(1-update_speed);
71end
72
73%% Post analysis
74disp('Converged transition coefficients (phi_k_1 phi_k_0 phi_l_1 phi_l_0):');
75disp(phi')
76% fraction negative wealth
77sum(SimuRslt.k(:,num_periods)<0) / numel(SimuRslt.k(:,num_periods))
78% Gini coefficients
79samplePeriods = num_periods;
80k = SimuRslt.k(:,samplePeriods);
81% ginicoeff(k(k>=0)) % Uncomment this to calculate gini if you have ginicoeff routine
82
83%%%%%%%%%%%%%%% MAIN ENDS HERE %%%%%%%%%%%%%%%%%%
84function shock = gen_discrete_markov_rn_with_agg(trans,numPaths,lenPath,initShock,aggIdx)
85% Generate idiosyncratic shocks conditional on aggregate shocks
86numStates = size(trans,1);
87shock = zeros(numPaths,lenPath);
88shock(:,1) = initShock;
89cumTrans = cumsum(trans, 2);
90for t=1:lenPath-1
91 for j=1:numStates
92 % Find samples that has shock j
93 idxOfShockJ = find(shock(:,t)==j);
94 % Fill in numbers based on distribution weights
95 un = rand(length(idxOfShockJ),1);
96 % Look up trans_j
97 [~,shock(idxOfShockJ,t+1)] = histc(un, [0 cumTrans(j,:,aggIdx(t),aggIdx(t+1))]);
98 end
99end
100end