|
Examples / WZSAMPLER.RPF |
WZSAMPLER.RPF uses the Waggoner-Zha(2003) Gibbs sampler to analyze the A form model from the CVMODEL.RPF example. (The WZ sampler only applies to A form models).
Their sampler draws each row of A separately. The advantage that this has over importance sampling or Metropolis-Hastings is that it doesn't require experimentation with "tuning" parameters---it's possible to draw the free coefficients in each row directly (conditional on the others).
This does not assume a normalization. This has both good and bad points. The good point is that it can't create a problem by choosing a normalization on a coefficient whose sign isn't determined. The bad point is that interpreting a structural VAR without a normalization can be difficult---does it make sense for a "price'' shock to be of undetermined sign or for a "money demand'' shock to not hit money? The sampler by construction picks a random sign fairly deep in the calculation, so this produces rows with roughly 50-50 signs. How this gets converted to a usable normalization is not clear. Based upon the examples that I've done, it looks like picking a normalization on a row that has the smallest (most negative) kurtosis tends to work well---that's the parameter that is most "bimodal".
This is a rather straightforward example of a structural VAR model without any real identification issues, so the WZ sampler gives basically the same results (once normalized) as Metropolis-Hastings draws.
A large block of the program (between the full lines of ***) is a generic implementation of the sampler and doesn't need to change. As it says in the comments after that,
* WZBDRAWS now has the sign-corrected draws for all the elements. What you do with
* this will obviously depend upon the application.
This does a scatter graph of the MR and MM coefficients (from the money equation) which are the 10th and 11th free parameters in the model. (The SMPL option limits this to only 1 in 10 of the points, as drawing 25000 dots takes a while).
set mrx 1 ndraws = wzbdraws(t)(10)
set mmx 1 ndraws = wzbdraws(t)(11)
*
scatter(footer="Coefficients from Money Demand Equation",$
hlabel="M coefficient",vlabel="R coefficient",smpl=%clock(t,10)==10)
# mmx mrx
The equation gets sign-normalized on the M coefficient, so those are all positive. Note that the money equation (as is the case with all the equations in a typical SVAR "A" model) has all coefficients on the left side, rather than M on the left and R on the right, so if this represents money demand, we would expect the R coefficients to be positive as well. That's is largely (but not exclusively) true.
This does a density graph of the normalized (now to a unit coefficient on M, not just positive sign) of the R coefficient in the M equation.
set mrstd 1 ndraws = mrx/mmx
density(smoothing=1.5,grid=automatic,maxgrid=100) mrstd / mrv mrf
scatter(style=line,vmin=0.0,footer="Posterior Density of Normalized MR coefficient")
# mrv mrf
The final part of the program takes the draws for the SVAR model as given and generates error bands by drawing lag coefficients and doing impulse responses on the combination of the SVAR and VAR and does a standard MC graph matrix for impulse responses.
Full Program
compute nburn =10000
compute ndraws=25000
compute nsteps=24
*
open data oecdsample.rat
calendar(q) 1981
data(format=rats) 1981:1 2006:4 can3mthpcp canexpgdpchs $
canexpgdpds canm1s canusxsr usaexpgdpch
*
set logcangdp = 100.0*log(canexpgdpchs)
set logcandefl = 100.0*log(canexpgdpds)
set logcanm1 = 100.0*log(canm1s)
set logusagdp = 100.0*log(usaexpgdpch)
set logexrate = 100.0*log(canusxsr)
*
system(model=canmodel)
variables logusagdp logcangdp can3mthpcp logexrate logcanm1 logcandefl
lags 1 to 4
det constant
end(system)
*
estimate(noprint)
*
* Set up A matrix with no sign normalizations
*
dec frml[rect] afrml
*
nonlin(parmset=aparms) uu ur cu cc cr rr rx xx mc mr mm mp pc pr pp
*
frml afrml = ||uu ,0.0,ur ,0.0,0.0,0.0|$
cu ,cc ,cr ,0.0,0.0,0.0|$
0.0,0.0,rr ,rx ,0.0,0.0|$
0.0,0.0,0.0,xx ,0.0,0.0|$
0.0,mc ,mr ,0.0,mm ,mp |$
0.0,pc ,pr ,0.0,0.0,pp||
*
* Estimate the model by ML. This uses DMATRIX=IDENTITY. The guess values are the
* reciprocals of the standard deviations down the diagonal, and zeros off of it.
*
compute uu=1.0/sqrt(%sigma(1,1)),cc=1.0/sqrt(%sigma(2,2))
compute rr=1.0/sqrt(%sigma(3,3)),xx=1.0/sqrt(%sigma(4,4))
compute mm=1.0/sqrt(%sigma(5,5)),pp=1.0/sqrt(%sigma(6,6))
compute ur=cu=cr=rx=mc=mr=mp=pc=pr=0.0
*
cvmodel(a=afrml,parmset=aparms,dmatrix=identity,$
method=bfgs,pmethod=simplex,piters=10) %sigma
*
* This is the ML estimate of the A matrix. You have to use ABASE for this, since
* it gets used in setting up the sampler.
*
compute abase=afrml(0)
*************************************************************************************
*
* From here to the next full line of ***'s doesn't require change
*
* Set up mappings that show the free parameters in each row. You have to
* use the name WZSLOTS for this. WZB is the vector of free parameters in each row.
*
compute nvar=%nvar,wztnobs=%nobs
dec vect[vect[int]] wzslots(nvar)
dec vect[vect] wzb(nvar)
*
* Build up the wzslots list by examining the non-zero elements in the estimated A
* matrix. Set the WZB vectors by pulling information out of A.
*
dec vect[int] empty
*
do i=1,nvar
local list[integer] active
compute active=empty
do j=1,nvar
if abase(i,j)<>0.0
compute active=active+j
end do j
compute wzslots(i)=active
dim wzb(i)(%size(active))
ewise wzb(i)(j)=abase(i,active(j))
end do i
*
* Create a parameter set with the subvectors out of each row.
*
nonlin(parmset=allwz) wzb
****
*
* WZAMatrix reconstructs the A matrix given the current values in the
* WZB vectors
*
function WZAMatrix
type rect WZAMatrix
local int i j
*
compute WZAMatrix=%zeros(nvar,nvar)
*
do i=1,nvar
do j=1,%size(WZSlots(i))
compute WZAMatrix(i,WZSlots(i)(j))=WZB(i)(j)
end do j
end do i
end
****
*
* WZPerp returns a vector perpendicular to all rows in the A matrix
* except the focus row.
*
function WZPerp row
type vector WZPerp
type integer row
*
local rect a
local vect arow
local int i j
local vect[rect] qr
*
compute a=WZAMatrix()
*
* Bubble the focus row to the bottom
*
compute arow=%xrow(a,row)
ewise a(i,j)=%if(i<row,a(i,j),%if(i==nvar,arow(j),a(i+1,j)))
*
* Do the QR decomp on the transpose and pick the final row
*
compute qr=%qrdecomp(tr(a))
compute WZPerp=%xcol(qr(1),nvar)
end
****
function WZQForm row
type symmetric WZQForm
type integer row
*
local integer i j
*
dim WZQForm(%size(wzslots(row)),%size(wzslots(row)))
ewise WZQForm(i,j)=%sigma(wzslots(row)(i),wzslots(row)(j))
compute WZQForm=inv(WZQForm)
end
****
dec vect WZUW
dec vect wzdraw
dec vect wzbeta(nvar)
*
infobox(action=define,progress,lower=-nburn,upper=ndraws) "Waggoner-Zha Sampler"
*
dec series[vect] wzbdraws
gset wzbdraws 1 ndraws = %parmspeek(allwz)
set wzlogl 1 ndraws = 0.0
*
do draw=-nburn,ndraws
*
* Do a sweep through the A matrix parameters
*
do row=1,nvar
compute qrow=%size(wzslots(row))
compute WZTMatrix=%decomp(WZQForm(row))
compute w=WZPerp(row)
dim WZUW(qrow)
ewise WZUW(i)=w(wzslots(row)(i))
compute WZTUW=tr(WZTMatrix)*WZUW
compute w1=wztuw/sqrt(%normsqr(wztuw))
*
compute wortho=%perp(w1)
*
dim wzdraw(qrow)
*
compute wzbeta(1)=sqrt(2.0/wztnobs*%rangamma(.5*(wztnobs+1)))*%if(%ranflip(.5),-1.0,1.0)
compute wzdraw=w1*wzbeta(1)
do i=1,qrow-1
compute wzbeta(i+1)=%ran(1.0/sqrt(wztnobs))
compute wzdraw=wzdraw+wzbeta(i+1)*%xcol(wortho,i)
end do i
compute wzb(row)=WZTMatrix*wzdraw
*
end do testrow
*
infobox(current=draw)
if draw<=0
next
compute wzbdraws(draw)=%parmspeek(allwz)
cvmodel(dmatrix=identity,parmset=allwz,method=evaluate,a=WZAMatrix()) %sigma
compute wzlogl(draw)=%logl
end do idraw
infobox(action=remove)
*
* Figure out which element within a row has the smallest kurtosis (which would
* mean the most bimodal in sign). Normalize the sign based upon on it.
*
compute ki=0
do i=1,nvar
compute minkurtosis=%na
do j=1,%size(wzslots(i))
set test 1 ndraws = wzbdraws(t)(ki+j)
stats(noprint) test
if .not.%valid(minkurtosis).or.%kurtosis<minkurtosis
compute minkurtosis=%kurtosis,bestj=j
end do j
?"Equation" i "sign-normalized on" wzslots(i)(bestj)
do t=1,ndraws
compute alla=wzbdraws(t)
compute flip=%sign(alla(ki+bestj))
do j=1,%size(wzslots(i))
compute alla(ki+j)=alla(ki+j)*flip
end do j
compute wzbdraws(t)=alla
end do t
compute ki=ki+%size(wzslots(i))
end do i
**********************************************************************
*
* WZBDRAWS now has the sign-corrected draws for all the elements. What you do with
* this will obviously depend upon the application.
*
* This does a scatter graph of the MR and MM coefficients (from the money equation).
*
set mrx 1 ndraws = wzbdraws(t)(10)
set mmx 1 ndraws = wzbdraws(t)(11)
*
scatter(footer="Coefficients from Money Demand Equation",$
hlabel="M coefficient",vlabel="R coefficient",smpl=%clock(t,10)==10)
# mmx mrx
*
* This does a density graph of the normalized (to a unit coefficient on M) of the
* R coefficient in the M equation.
*
set mrstd 1 ndraws = mrx/mmx
density(smoothing=1.5,grid=automatic,maxgrid=100) mrstd / mrv mrf
scatter(style=line,vmin=0.0,footer="Posterior Density of Normalized MR coefficient")
# mrv mrf
*
* This does MC integration of the impulse responses taking the keeper draws for
* the A matrix coefficients as given. Since the lag coefficients can be drawn
* directly given A, this doesn't need any burn-in.
*
declare vect[rect] %%responses(ndraws)
*
* Re-estimate the model so we can pull out the information required for drawing
* the coefficients.
*
estimate(noprint)
*
compute fxx =%decomp(%xx)
compute betaols=%modelgetcoeffs(canmodel)
*
infobox(action=define,progress,upper=ndraws) "Monte Carlo Integration"
*
do draw=1,ndraws
*
* Poke the draw back into the parmset and compute the approximate factor (which
* is the inverse of the A matrix).
*
compute %parmspoke(allwz,wzbdraws(draw))
compute factor=inv(WZAMatrix())
*
* Draw the VAR coefficients and put the draw back into the model.
*
compute betadraw=betaols+%ranmvkron(factor,fxx)
compute %modelsetcoeffs(canmodel,betadraw)
*
impulse(model=canmodel,factor=factor,flatten=%%responses(draw),steps=nsteps,noprint)
infobox(current=draw)
end do draws
infobox(action=remove)
*
@mcgraphirf(model=canmodel,footer="IRF Error Bands using WZ Sampler",$
shocklabels=||"Real 1","Real 2","Fin 1","Fin 2","Nom 1","Nom 2"||)
Output
Covariance Model-Likelihood - Estimation by BFGS
Convergence in 24 Iterations. Final criterion was 0.0000032 <= 0.0000100
Observations 100
Log Likelihood -581.0251
Log Likelihood Unrestricted -578.6729
Chi-Squared(6) 4.7044
Significance Level 0.5822397
Variable Coeff Std Error T-Stat Signif
************************************************************************************
1. UU 2.498729939 0.176726758 14.13895 0.00000000
2. UR -0.248354254 0.161798078 -1.53496 0.12479263
3. CU -1.272425088 0.265922843 -4.78494 0.00000171
4. CC 2.519950035 0.183451004 13.73637 0.00000000
5. CR 0.369561196 0.168431453 2.19413 0.02822577
6. RR 1.662514287 0.117613062 14.13546 0.00000000
7. RX 0.160339770 0.061437998 2.60978 0.00906000
8. XX 0.602263595 0.042976564 14.01377 0.00000000
9. MC -0.350913490 0.229761667 -1.52729 0.12668806
10. MR 0.271351530 0.164502448 1.64953 0.09903934
11. MM 1.048247458 0.074180565 14.13103 0.00000000
12. MP -0.637429494 0.230929212 -2.76028 0.00577517
13. PC 0.400736908 0.229054274 1.74953 0.08019976
14. PR 0.175737590 0.164250832 1.06993 0.28464891
15. PP 2.259102755 0.163082068 13.85255 0.00000000
Equation 1 sign-normalized on 1
Equation 2 sign-normalized on 2
Equation 3 sign-normalized on 3
Equation 4 sign-normalized on 4
Equation 5 sign-normalized on 5
Equation 6 sign-normalized on 6
Graphs
.png)
.png)
.png)
Copyright © 2026 Thomas A. Doan