*! Mata definitions for confa package; 3 March 2009; v.2.0 set matalnum on mata: mata set matastrict on mata clear ////////////////////////////////////////////// // needed by confa.ado real CONFA_NF( string input ) { real scalar nopenpar, nclosepar, ncolon; // opening and closing parentheses nopenpar = length(tokens(input, ")" )) nclosepar = length(tokens(input, "(" )) // n*par will be 2*nf ncolon = length(tokens(input, ":" )) // ncolon will be 2*nf + 1 if ( (nopenpar == nclosepar) & (nopenpar == ncolon-1 ) ) { if (mod(nopenpar,2) == 0) { return( nopenpar/2 ) } } // if everything was OK, should've exited by now // if something's wrong, return zero return(0) } matrix CONFA_StrucToSigma(real vector parms) { real scalar CONFA_loglevel, nobsvar, nfactors, eqno; real matrix Lambda, Phi, Theta, Sigma, CONFA_Struc; // loglevel CONFA_loglevel = strtoreal( st_global("CONFA_loglevel")) CONFA_Struc = st_matrix("CONFA_Struc") if (CONFA_loglevel>4) CONFA_Struc if (CONFA_loglevel>4) { printf("{txt}Current parameter values:\n") parms } // the length should coinicde with the # pars from CONFA_Struc if ( cols(parms) ~= rows(CONFA_Struc) ) { // something's wrong, let's just drop out with an empty matrix if (CONFA_loglevel>4) { printf("{txt}Expected parameters: {res}%3.0f{txt}; received parameters: {res}%3.0f\n", rows(CONFA_Struc),cols(parms)) } return(J(0,0,0)) } // # observed variables: max entry in the number of means nobsvar = colmax( select(CONFA_Struc[,3], !(CONFA_Struc[,1]-J(rows(CONFA_Struc),1,1)) ) ) if (CONFA_loglevel>4) printf("{txt}No. of observed variables: {res}%3.0f\n",nobsvar) // # observed factors: max entry in the phi indices nfactors = colmax( select(CONFA_Struc[,3], !(CONFA_Struc[,1]-J(rows(CONFA_Struc),1,3)) ) ) if (CONFA_loglevel>4) printf("{txt}No. of latent factors: {res}%3.0f\n",nfactors) // set up the matrices Lambda = J(nobsvar,nfactors,0) Phi = J(nfactors,nfactors,0) Theta = J(nobsvar,nobsvar,0) // fill the stuff in for(eqno=nobsvar+1;eqno<=rows(CONFA_Struc);eqno++) { if (CONFA_Struc[eqno,1] == 2) { // a lambda-type entry Lambda[ CONFA_Struc[eqno,3], CONFA_Struc[eqno,4] ] = parms[eqno] } if (CONFA_Struc[eqno,1] == 3) { // a phi-type entry Phi[ CONFA_Struc[eqno,3], CONFA_Struc[eqno,4] ] = parms[eqno] Phi[ CONFA_Struc[eqno,4], CONFA_Struc[eqno,3] ] = parms[eqno] } if (CONFA_Struc[eqno,1] == 4) { // a theta-type entry Theta[ CONFA_Struc[eqno,3], CONFA_Struc[eqno,3] ] = parms[eqno] } if (CONFA_Struc[eqno,1] == 5) { // a theta-type correlated errors entry Theta[ CONFA_Struc[eqno,3], CONFA_Struc[eqno, 4] ] = parms[eqno] Theta[ CONFA_Struc[eqno,4], CONFA_Struc[eqno, 3] ] = parms[eqno] } } if (CONFA_loglevel > 4) { printf("{txt}Loadings:\n") Lambda printf("{txt}Factor covariances:\n") Phi printf("{txt}Residual variances:\n") Theta } Sigma = Lambda*Phi*Lambda' + Theta if (CONFA_loglevel > 4) { printf("{txt}Implied moments:\n") Sigma } if (CONFA_loglevel == -1) { // post matrices to Stata st_matrix("CONFA_Lambda",Lambda) st_matrix("CONFA_Phi",Phi) st_matrix("CONFA_Theta",Theta) st_matrix("CONFA_Sigma",Sigma) } // done with model structure, compute and return implied matrix return( Sigma ) } // vech covariance matrix, for Satorra-Bentler void SBvechZZtoB(string dlist, string blist) { real matrix data, moments, B; real scalar i; // view the deviation variables st_view(data=.,.,tokens(dlist)) // view the moment variables // blist=st_local("blist") st_view(moments=.,.,tokens(blist)) // vectorize! for(i=1; i<=rows(data); i++) { B = data[i,.]'*data[i,.] moments[i,.] = vech(B)' } } // duplication matrix, for Satorra-Bentler void Dupl(scalar p, string Dname) { real scalar pstar, k; real matrix Ipstar, D; pstar = p*(p+1)/2 Ipstar = I(pstar) D = J(p*p,0,.) for(k=1;k<=pstar;k++) { D = (D, vec(invvech(Ipstar[.,k]))) } st_matrix(Dname,D) } // Satorra-Bentler Delta matrix // Delta = \frac \partial{\partial \theta} vech \Sigma(\theta) void SBStrucToDelta(string DeltaName) { real scalar CONFA_loglevel, p, t, varno, facno, i, j, k, fac1, fac2, k1, k2; // log level, # obs vars, # parameters, current var, current factor, cycle indices, temp indices real matrix Lambda, Phi, Theta, Sigma, CONFA_Struc, Delta, DeltaRow; // must be self-explanatory real matrix U, E; // identity matrices of the size #factors and #obs vars // loglevel CONFA_loglevel = strtoreal( st_global("CONFA_loglevel")) // need the CONFA matrices CONFA_Struc = st_matrix("CONFA_Struc") Sigma = st_matrix("CONFA_Sigma") Lambda = st_matrix("CONFA_Lambda") Phi = st_matrix("CONFA_Phi") // Theta = st_matrix("CONFA_Theta") if (CONFA_loglevel>4) CONFA_Struc // # parameters in the model t = rows(CONFA_Struc) // cols(Delta) = t = # pars // rows(Delta) = pstar = p*(p+1)/2 = length( vech( Sigma ) ) // but that should be accumulated one by one... Delta = J(0,t,.) // sources of u and e vectors p = rows( Sigma ) U = I( p ) E = I( rows(Phi) ) for(i=1;i<=p;i++) { for(j=i;j<=p;j++) { if (CONFA_loglevel > 4) printf("{txt}Working with pair ({res}%2.0f{txt},{res}%2.0f{txt})\n",i,j) DeltaRow = J(1,t,0) // parse Struc matrix and see how each parameter affects Cov(X_i,X_j) for(k=1;k<=t;k++) { if (CONFA_Struc[k,1] == 1) { // a mean-type entry // for the moment, assume it does not affect anything } if (CONFA_Struc[k,1] == 2) { // a lambda-type entry // CONFA_Struc[k,.] = (2, equation #, variable #, factor #) varno = CONFA_Struc[k,3] facno = CONFA_Struc[k,4] DeltaRow[1,k] = U[i,.] * U[.,varno] * E[facno,.] * Phi * Lambda' * U[.,j] + U[i,.] * Lambda * Phi * E[.,facno] * U[varno,.] * U[.,j] } if (CONFA_Struc[k,1] == 3) { // a phi-type entry // CONFA_Struc[k,.] = (3, equation #, `factor`kk'', `factor`k'') fac1 = CONFA_Struc[k,3] fac2 = CONFA_Struc[k,4] DeltaRow[1,k] = U[i,.] * Lambda * E[.,fac1] * E[fac2,.] * Lambda' * U[.,j] } if (CONFA_Struc[k,1] == 4) { // a theta-type entry // CONFA_Struc[k,.] = (4, equation #, variable #, 0) varno = CONFA_Struc[k,3] DeltaRow[1,k] = (i==j) & (i==varno) } if (CONFA_Struc[k,1] == 5) { // a theta_{jk}-type entry // CONFA_Struc[k,.] = (5, equation #, variable k1, variable k2) k1 = CONFA_Struc[k,3] k2 = CONFA_Struc[k,4] DeltaRow[1,k] = ((i==k1) & (j==k2) ) | ((i==k2) & (j==k1)) } } Delta = Delta \ DeltaRow } } st_matrix(DeltaName,Delta) } /////////////////////////////////////////// // needed by confa_p.ado void CONFA_P_EB(string Fnames, string ObsVarNames, string ToUseName) { real matrix ff, xx; // views real matrix bb, Sigma, Lambda, Theta, Phi; // substantive matrices real scalar p // view on the newly generated factors st_view(ff=.,.,tokens(Fnames),ToUseName) // view on the observed variables st_view(xx=.,.,tokens(ObsVarNames),ToUseName) // get the estimated matrices bb = st_matrix("e(b)") Sigma = st_matrix("e(Sigma)") Theta = st_matrix("e(Theta)") Lambda = st_matrix("e(Lambda)") Phi = st_matrix("e(Phi)") // # observed vars p = rows(Sigma) // prediction ff[,] = (xx-J(rows(xx),1,1)*bb[1..p]) * invsym(Sigma) * Lambda * Phi } void CONFA_P_MLE(string Fnames, string ObsVarNames, string ToUseName) { real matrix ff, xx; // views real matrix bb, Sigma, Lambda, Theta, Phi, ThetaInv; // substantive matrices real scalar p // view on the newly generated factors st_view(ff=.,.,tokens(Fnames),ToUseName) // view on the observed variables st_view(xx=.,.,tokens(ObsVarNames),ToUseName) // get the estimated matrices bb = st_matrix("e(b)") Sigma = st_matrix("e(Sigma)") Theta = st_matrix("e(Theta)") Lambda = st_matrix("e(Lambda)") Phi = st_matrix("e(Phi)") // # observed vars p = rows(Sigma) // Theta is the vector of diagonal elements, // so the inverse is easy! ThetaInv = diag( 1:/Theta ) // prediction ff[,] = (xx-J(rows(xx),1,1)*bb[1..p]) * ThetaInv * Lambda * invsym(Lambda' * ThetaInv * Lambda) } ////////////////////////////////// // needed by confa_lf.ado void CONFA_NormalLKHDr( string ParsName, string lnfname) { // ParsName are the parameters // lnfname is the name of the likelihood variable // the observed variables are in $CONFA_obsvar real scalar CONFA_loglevel, nobsvar, ldetWS, i; // log level, # obs vars, log determinant, cycle index real matrix Sigma, means, SS, InvWorkSigma; // intermediate computations string scalar obsvar, touse; // list of observed variables real matrix data, lnl, parms; // views CONFA_loglevel = strtoreal( st_global("CONFA_loglevel")) obsvar = st_global("CONFA_obsvar") nobsvar = length(tokens(obsvar)) touse = st_global("CONFA_touse") st_view(data=., ., tokens(obsvar), touse ) st_view(lnl=., ., tokens(lnfname), touse) st_view(parms=., ., tokens(ParsName), touse) // using the set up where the means are the first nobsvar entries of the parameter vector, means = parms[1,1..nobsvar] Sigma = CONFA_StrucToSigma(parms[1,.]) if (CONFA_loglevel > 2) { parms[1,.] means Sigma } // do some equilibration?? SS = cholesky(Sigma) InvWorkSigma = solvelower(SS,I(rows(SS))) InvWorkSigma = solveupper(SS',InvWorkSigma) ldetWS = 2*ln(dettriangular(SS)) for( i=1; i<=rows(data); i++ ) { lnl[i,1] = -.5*(data[i,.]-means)*InvWorkSigma*(data[i,.]-means)' - .5*ldetWS - .5*nobsvar*ln(2*pi()) } if (CONFA_loglevel>2) { sum(lnl) } } // normal likelihood with missing data void CONFA_NormalLKHDrMiss( string ParsName, string lnfname) { // ParsName are the parameters // lnfname is the name of the likelihood variable // the observed variables are in $CONFA_obsvar real scalar CONFA_loglevel, nobsvar, thisldetWS, i, j; // log level, # obs vars, log determinant, cycle index real matrix Sigma, means, thisSigma, thisSS, thisInvSigma, thispattern, parms; // intermediate computations string scalar obsvar, misspat, touse; // list of observed variables; the names of the missing patterns and touse tempvars real matrix data, lnl, parmview, pattern, mdata, mlnl, info; // views CONFA_loglevel = strtoreal( st_global("CONFA_loglevel")) obsvar = st_global("CONFA_obsvar") nobsvar = length(tokens(obsvar)) misspat = st_global("CONFA_miss") touse = st_global("CONFA_touse") st_view(pattern=., ., misspat, touse ) st_view(data=., ., tokens(obsvar), touse ) st_view(lnl=., ., lnfname, touse ) // STILL USING THE FIRST OBSERVATIONS TO GET THE PARAMETERS!!! st_view(parmview=., ., tokens(ParsName), touse ) parms = parmview[1,1..cols(parmview)] if (CONFA_loglevel>2) { obsvar parms } // using the set up where the means are the first nobsvar entries of the parameter vector, means = parms[1..nobsvar] Sigma = CONFA_StrucToSigma(parms) // utilize an existing set up of the missing data patterns // data assumed to be sorted by the patterns of missing data info = panelsetup( pattern, 1 ) for (i=1; i<=rows(info); i++) { panelsubview(mdata=., data, i, info) panelsubview(mlnl=., lnl, i, info) // mdata should contain the portion of the data with the same missing data pattern // mlnl will be conforming to mdata // OK, now need to figure out that pattern thispattern = J(1, cols(data), 1) - colmissing( mdata[1,] ) if (CONFA_loglevel > 2) { printf("{txt}Pattern #{res}%5.0f{txt} :", i) thispattern }; // modify the matrices thisSigma = select( select( Sigma, thispattern), thispattern' ) thisSS = cholesky(thisSigma) thisInvSigma = solvelower(thisSS,I(rows(thisSS))) thisInvSigma = solveupper(thisSS',thisInvSigma) thisldetWS = 2*ln(dettriangular(thisSS)) if (CONFA_loglevel > 3) { thisSigma thisInvSigma }; for( j=1; j<=rows(mdata); j++ ) { // this is actually a single line broken by arithmetic operator signs // that's bad style but it works mlnl[j,1] = -.5*(select(data[j,.],thispattern)-select(means,thispattern)) * thisInvSigma * (select(data[j,.],thispattern)-select(means,thispattern))' - .5*thisldetWS - .5*sum(thispattern)*ln(2*pi()) } if (CONFA_loglevel>3) { mlnl }; } } // Bollen-Stine bootstrap rotation void CONFA_BSrotate( string SigmaName, // the parameter matrix name string varnames // the variable names ) { // declarations real matrix data // views of the data real matrix Sigma, SS, S2, SS2 // the covariance matrices and temp matrices real matrix means // the means -- need modifications for weighted data!!! real scalar n // dimension, no. obs // get the data in st_view(data=., ., tokens(varnames) ) n=rows(data) Sigma = st_matrix(SigmaName) // probability weights!!! means = colsum(data)/n SS = (cross(data,data)-n*means'*means)/(n-1) S2 = cholesky(Sigma) SS2 = cholesky(SS) SS2 = solveupper(SS2',I(rows(SS))) data[,] = data*SS2*S2' } // build a library mata mlib create lconfa, replace mata mlib add lconfa *() mata mlib index end // of mata exit // don't need this: string scalar CONFA_UL( string input ) { string rowvector s; real scalar i,j,n; // tokenize input into a string vector s = tokens( input ) n = cols( s ) for(i=1;i<=n;i++) { // as I go over the elements, compare to the previous ones for(j=1;j