Rev 1131 | Blame | Last modification | View Log | Download | RSS feed
#include "bCrosstab.h"/* TODO:* - Only do a fill-reducing permutation on the first non-nested factor* - Alternatively: change the algorithm for the fill-reducing* permutation to a greedy or a picky algorithm.* - Rewrite this whole section using the lgTMatrix and lgCMatrix classes.* A symbolic manipulation of a sparse matrix is equivalent to* manipulating the sparse matrix with logical entries.* - Create coercion methods for dgCMatrix -> lgCMatrix and vice* versa. Same for dgTMatrix -> lgTMatrix.*//*** Replace the structure of C by the structure of CL^{-1} where L is the* unit lower triangular sparse matrix from an LDL' Cholesky decomposition** @param anc number of columns in A* @param Parent parent array for A* @param C a dgBCMatrix object to be updated*/static voidsymbolic_right_unit_sm(int anc, const int Parent[], SEXP C){SEXP cip = GET_SLOT(C, Matrix_iSym),cpp = GET_SLOT(C, Matrix_pSym);int *Flag,*ci = INTEGER(cip),*cp = INTEGER(cpp),*ncp,cnr, cnz = length(cip),i, j;if ((length(cpp) - 1) != anc) /* A is square so can compare no of cols */error(_("No. of rows in A (%d) does not match no. of cols in C (%d)"),anc, length(cpp) - 1);i = 1; /* check for A being the identity */for (j = 0; j < anc; j++) {if (Parent[j] >= 0) {i = 0;break;}}if (i) return; /* A is the identity */cnr = 0; /* number of rows in C (= max(ci + 1)) */for (i = 0; i < cnz; i++) {int ri = ci[i] + 1;if (cnr < ri) cnr = ri;}Flag = Calloc(cnr, int);ncp = Calloc(anc + 1, int); /* new column pointers */ncp[0] = 0;for (j = 0; j < anc; j++) {int cj2 = cp[j + 1], kk, kc;for (i = 0; i < cnr; i++) Flag[i] = 0;ncp[j+1] = ncp[j] + cj2 - cp[j];/* positions of current column j of C */for (kc = cp[j]; kc < cj2; kc++) Flag[ci[kc]] = 1;/* other positions in column j of product */for (kk = Parent[j]; kk >= 0; kk = Parent[kk]) {int kk2 = cp[kk + 1];for (kc = cp[kk]; kc < kk2; kc++) {if (!Flag[ci[kc]]) {ncp[j+1]++;Flag[ci[kc]] = 1;}}}}if (ncp[anc] > cp[anc]) {int *dims, *nci, nnz = ncp[anc], pos = 0;double *ncx;SET_SLOT(C, Matrix_iSym, allocVector(INTSXP, nnz));nci = INTEGER(GET_SLOT(C, Matrix_iSym));dims = INTEGER(getAttrib(GET_SLOT(C, Matrix_xSym), R_DimSymbol));SET_SLOT(C, Matrix_xSym, alloc3Darray(REALSXP, dims[0], dims[1], nnz));ncx = REAL(GET_SLOT(C, Matrix_xSym));for (i = 0; i < nnz; i++) ncx[i] = 1.;/* As Diana Krall said, "Just do it again." */for (j = 0; j < anc; j++) {int cj2 = cp[j + 1], kc, kk;for (i = 0; i < cnr; i++) Flag[i] = 0;for (kc = cp[j]; kc < cj2; kc++) Flag[ci[kc]] = 1;for (kk = Parent[j]; kk >= 0; kk = Parent[kk]) {int kk2 = cp[kk + 1];for (kc = cp[kk]; kc < kk2; kc++) Flag[ci[kc]] = 1;}for (i = 0; i < cnr; i++) if (Flag[i]) nci[pos++] = i;}Memcpy(cp, ncp, anc + 1);}Free(Flag); Free(ncp);}/*** Replace the structure of C by the structure of CA^{-T}** @param anc number of column blocks in A* @param Parent parent array for column blocks of A* @param C a dgBCMatrix object to be updated*/static voidsymbolic_right_unit_mm_trans(int anc, const int Parent[], SEXP C){SEXP cip = GET_SLOT(C, Matrix_iSym),cpp = GET_SLOT(C, Matrix_pSym);int *ci = INTEGER(cip),*cp = INTEGER(cpp),cnz = length(cip),i, j, nextra = 0;if ((length(cpp) - 1) != anc)error(_("No. of cols in A (%d) does not match no. of cols in C (%d)"),anc, length(cpp) - 1);i = 1; /* check for A being the identity */for (j = 0; j < anc; j++) {if (Parent[j] >= 0) {i = 0;break;}}if (i) return; /* A is the identity */for (j = 0; j < anc; j++) { /* bound the number of extra triplets */int cj2 = cp[j + 1], ka, kc;for (ka = Parent[j]; ka >= 0; ka = Parent[ka]) {for (kc = cp[j]; kc < cj2; kc++) {if (check_csc_index(cp, ci, ci[kc], ka, -1) < 0) nextra++;}}}if (nextra) {int cnr, ntot = cnz + nextra, pos;int *dims = INTEGER(getAttrib(GET_SLOT(C, Matrix_xSym), R_DimSymbol)),*Ti = Memcpy((int *) Calloc(ntot, int), ci, cnz),*Tj = expand_column_pointers(anc, cp, Calloc(ntot, int)),*Ci = Calloc(ntot, int);for (j = 0, pos = cnz; j < anc; j++) {int cj2 = cp[j + 1], ka, kc;for (ka = Parent[j]; ka >= 0; ka = Parent[ka]) {for (kc = cp[j]; kc < cj2; kc++) {if (check_csc_index(cp, ci, ci[kc], ka, -1) < 0) {Tj[pos] = ka;Ti[pos] = ci[kc];pos++;}}}}for (j = 0, cnr = 0; j < cnz; j++) { /* determine number of rows in C */int rr = ci[j] + 1;if (rr > cnr) cnr = rr;}triplet_to_col(cnr, anc, ntot, Ti, Tj, (double *) NULL,INTEGER(cpp), Ci, (double *) NULL);cnz = cp[anc];SET_SLOT(C, Matrix_iSym, allocVector(INTSXP, cnz));SET_SLOT(C, Matrix_xSym, alloc3Darray(REALSXP, dims[0], dims[1], cnz));Free(Ti); Free(Tj); Free(Ci);}}/*** Update a block of L in the blocked crosstabulation** @param L pointer to a unit lower triangular list of logical* compressed sparse column-oriented matrices* @param ZZpO pointer to a list of upper triangular diagonal blocks* stored as compressed sparse column-oriented matrices.* @param j index of updating column block* @param k column index of block to be updated* @param i row index of block to be updated (j < k <= i)*/static voidblock_update(SEXP L, SEXP ZZpO, int j, int k, int i){SEXP tb = (i == k) ? VECTOR_ELT(ZZpO, i) : VECTOR_ELT(L, Lind(i, k)),ib = VECTOR_ELT(L, Lind(i, j)),kb = VECTOR_ELT(L, Lind(k, j));SEXP tpp = GET_SLOT(tb, Matrix_pSym),kpp = GET_SLOT(kb, Matrix_pSym);int *ti = INTEGER(GET_SLOT(tb, Matrix_iSym)),*tp = INTEGER(tpp),*ii = INTEGER(GET_SLOT(ib, Matrix_iSym)),*ip = INTEGER(GET_SLOT(ib, Matrix_pSym)),*ki = INTEGER(GET_SLOT(kb, Matrix_iSym)),*kp = INTEGER(kpp),tnc = length(tpp) - 1,knc = length(kpp) - 1;int jj, extra;if (k > i || j >= k)error(_("i,j,k values of %d,%d,%d do not satisfy j < k <= i"),i, j, k);/* bound the number of extra elements */extra = 0;for (jj = 0; jj < knc; jj++) {int i1, kk, i2 = ip[jj + 1], k2 = kp[jj + 1];for (kk = kp[jj]; kk < k2; kk++) {for (i1 = ip[jj]; i1 < i2; i1++) {if ((check_csc_index(tp, ti, ii[i1], ki[kk], -1) < 0) &&/* only update upper triangle of* diagonal blocks */((k != i) || (ii[i1] <= ki[kk]))) extra++;}}}if (!extra) return;{int pos, nnz = tp[tnc];int ntot = nnz + extra, tnr;int *Ai = Calloc(ntot, int),*Ti = Calloc(ntot, int),*Tj = Calloc(ntot, int),*dims;double *Ax;Memcpy(Ti, ti, nnz); /* make a copy of the row indices */for (pos = 0, jj = 0; jj < tnc; jj++) { /* fill in the column indices */int j2 = tp[jj + 1];for (; pos < j2; pos++) Tj[pos] = jj;}/* add the extra elements */for (jj = 0; jj < knc; jj++) {int i1, kk, i2 = ip[jj + 1], k2 = kp[jj + 1];for (kk = kp[jj]; kk < k2; kk++) {for (i1 = ip[jj]; i1 < i2; i1++) {if ((check_csc_index(tp, ti, ii[i1], ki[kk], -1) < 0) &&((k != i) || (ii[i1] <= ki[kk]))) {Ti[pos] = ii[i1];Tj[pos] = ki[kk];pos++;}}}}/* FIXME: Pass nlev instead - dimensions are nlev[i], nlev[k] *//* Determine maximum row index in T */tnr = -1; for (jj = 0; jj < ntot; jj++) if (Ti[jj] > tnr) tnr = Ti[jj];tnr++; /* increment by 1 to get number of rows */triplet_to_col(tnr, tnc, ntot, Ti, Tj, (double *) NULL,tp, Ai, (double *) NULL);nnz = tp[tnc];SET_SLOT(tb, Matrix_iSym, allocVector(INTSXP, nnz));Memcpy(INTEGER(GET_SLOT(tb, Matrix_iSym)), Ai, nnz);dims = INTEGER(getAttrib(GET_SLOT(tb, Matrix_xSym), R_DimSymbol));SET_SLOT(tb, Matrix_xSym,alloc3Darray(REALSXP, dims[0], dims[1], nnz));Ax = REAL(GET_SLOT(tb, Matrix_xSym));for (j = 0; j < nnz; j++) Ax[j] = 1.;Free(Ai); Free(Ti); Free(Tj);return;}}/*** Permute the levels of one of the grouping factors in a bCrosstab object** @param ctab Pointer to a bCrosstab object* @param nf number of factors in ctab* @param jj index (0-based) of the factor levels to permute* @param nlev number of levels of the grouping factors* @param iperm inverse of the permutation*/static voidbCrosstab_permute(SEXP ctab, int nf, int jj,const int nlev[], const int iperm[]){int j;for (j = 0; j < nf; j++) {int ind = (j < jj ? Lind(jj, j) : Lind(j, jj)),ncol = (j < jj ? nlev[j] : nlev[jj]),nrow = (j < jj ? nlev[jj] : nlev[j]);SEXP cscb = VECTOR_ELT(ctab, ind),cscbi = GET_SLOT(cscb, Matrix_iSym);int *cp = INTEGER(GET_SLOT(cscb, Matrix_pSym)),nnz = length(cscbi);double *cx = REAL(GET_SLOT(cscb, Matrix_xSym));int *mj = expand_column_pointers(ncol, cp, Calloc(nnz, int));int *mi = Memcpy(Calloc(nnz, int), INTEGER(cscbi), nnz);double *mx = Memcpy(Calloc(nnz, double), cx, nnz);if (j <= jj) int_permute(mi, nnz, iperm);if (j >= jj) int_permute(mj, nnz, iperm);if (j == jj) make_upper_triangular(mi, mj, nnz);triplet_to_col(nrow, ncol, nnz, mi, mj, mx, cp, INTEGER(cscbi), cx);Free(mi); Free(mj); Free(mx);}}static voidsymmetric_permute(SEXP A, int nlev, const int iperm[]){SEXP AiP = GET_SLOT(A, Matrix_iSym);int *Ap = INTEGER(GET_SLOT(A, Matrix_pSym)),nnz = length(AiP);double *Ax = REAL(GET_SLOT(A, Matrix_xSym));int *mj = expand_column_pointers(nlev, Ap, Calloc(nnz, int));int *mi = Memcpy(Calloc(nnz, int), INTEGER(AiP), nnz);double *mx = Memcpy(Calloc(nnz, double), Ax, nnz);int_permute(mi, nnz, iperm);int_permute(mj, nnz, iperm);make_upper_triangular(mi, mj, nnz);triplet_to_col(nlev, nlev, nnz, mi, mj, mx, Ap, INTEGER(AiP), Ax);Free(mi); Free(mj); Free(mx);}/*** Apply a permutation vector to the levels of a factor.** The dest pointer is assumed to point to a copy of the src pointer's* contents.** @param dest pointer to the destination factor* @param src pointer to the source factor* @param perm permutation vector (0-based)* @param iperm inverse permutation vector (0-based)*/static voidfactor_levels_permute(SEXP dest, SEXP src, const int perm[],const int iperm[]){SEXP dlev = getAttrib(dest, R_LevelsSymbol),slev = getAttrib(src, R_LevelsSymbol);int nlev = length(dlev), flen = length(dest);int *d = INTEGER(dest), *s = INTEGER(src), i;if (length(slev) != nlev)error(_("number of levels in src and dest must match"));if (length(src) != flen)error(_("length of src and dest must match"));for (i = 0; i < nlev; i++)SET_STRING_ELT(dlev, i, STRING_ELT(slev, perm[i]));for (i = 0; i < flen; i++)d[i] = 1 + iperm[s[i]-1];}/*** Create and populate slots in an lmer object from the blocked crosstabulation.** @param val Pointer to an lmer object*/voidlmer_populate(SEXP val){SEXP D, L, Parent, ZZpO, flist = GET_SLOT(val, Matrix_flistSym),perm, Omega, ZtZ = GET_SLOT(val, Matrix_ZtZSym);SEXP fnms = getAttrib(flist, R_NamesSymbol);int j, k, nf = length(flist);int *nc = INTEGER(GET_SLOT(val, Matrix_ncSym)), *Gp,*nlev = Calloc(nf, int), npairs = (nf * (nf + 1))/2;char *statnms[] = {"factored", "inverted", ""},*devnms[] = {"ML", "REML", ""},*pnms[] = {"index", "block", ""};/* Allocate fixed-sized slots */SET_SLOT(val, Matrix_statusSym, Matrix_make_named(LGLSXP, statnms));SET_SLOT(val, Matrix_devianceSym, Matrix_make_named(REALSXP, devnms));SET_SLOT(val, Matrix_devCompSym, allocVector(REALSXP, 4));/* Allocate slots that are lists of length nf */ZZpO = ALLOC_SLOT(val, Matrix_ZZpOSym, VECSXP, nf);setAttrib(ZZpO, R_NamesSymbol, duplicate(fnms));D = ALLOC_SLOT(val, Matrix_DSym, VECSXP, nf);setAttrib(D, R_NamesSymbol, duplicate(fnms));perm = ALLOC_SLOT(val, Matrix_permSym, VECSXP, nf);setAttrib(perm, R_NamesSymbol, duplicate(fnms));Parent = ALLOC_SLOT(val, Matrix_ParentSym, VECSXP, nf);setAttrib(Parent, R_NamesSymbol, duplicate(fnms));Omega = ALLOC_SLOT(val, Matrix_OmegaSym, VECSXP, nf);setAttrib(Omega, R_NamesSymbol, duplicate(fnms));/* Allocate peculiar length slots */SET_SLOT(val, Matrix_LSym, allocVector(VECSXP, npairs));L = GET_SLOT(val, Matrix_LSym);SET_SLOT(val, Matrix_GpSym, allocVector(INTSXP, nf + 1));Gp = INTEGER(GET_SLOT(val, Matrix_GpSym));Gp[0] = 0;for (j = 0; j < nf; j++) {nlev[j] = length(getAttrib(VECTOR_ELT(flist, j), R_LevelsSymbol));Gp[j + 1] = Gp[j] + nc[j] * nlev[j];SET_VECTOR_ELT(D, j, alloc3Darray(REALSXP, nc[j], nc[j], nlev[j]));SET_VECTOR_ELT(Omega, j, allocMatrix(REALSXP, nc[j], nc[j]));SET_VECTOR_ELT(ZZpO, j, duplicate(VECTOR_ELT(ZtZ, Lind(j, j))));for (k = j; k < nf; k++)SET_VECTOR_ELT(L, Lind(k, j),duplicate(VECTOR_ELT(ZtZ, Lind(k, j))));}SET_SLOT(val, Matrix_XtXSym, allocMatrix(REALSXP, nc[nf], nc[nf]));AZERO(REAL(GET_SLOT(val, Matrix_XtXSym)), nc[nf] * nc[nf]);SET_SLOT(val, Matrix_RXXSym, allocMatrix(REALSXP, nc[nf], nc[nf]));AZERO(REAL(GET_SLOT(val, Matrix_RXXSym)), nc[nf] * nc[nf]);SET_SLOT(val, Matrix_ZtXSym, allocMatrix(REALSXP, Gp[nf], nc[nf]));SET_SLOT(val, Matrix_RZXSym, allocMatrix(REALSXP, Gp[nf], nc[nf]));for (j = 0; j < nf; j++) {int dind = Lind(j, j), i;SEXP ctd = VECTOR_ELT(ZZpO, j); /* diagonal in crosstab */SEXP Ljj = VECTOR_ELT(L, dind),cpp = GET_SLOT(ctd, Matrix_pSym),cip = GET_SLOT(ctd, Matrix_iSym), parent;int *Lp = INTEGER(GET_SLOT(Ljj, Matrix_pSym)), *Perm,*cp = INTEGER(cpp),*ci = INTEGER(cip), *dims,ncj = length(cpp) - 1,nnz = length(cip);SET_VECTOR_ELT(Parent, j, Matrix_make_named(VECSXP, pnms));parent = VECTOR_ELT(Parent, j);SET_VECTOR_ELT(parent, 0, allocVector(INTSXP, ncj));SET_VECTOR_ELT(parent, 1, allocVector(INTSXP, ncj));SET_VECTOR_ELT(perm, j, allocVector(INTSXP, ncj));Perm = INTEGER(VECTOR_ELT(perm, j));dims = INTEGER(getAttrib(GET_SLOT(ctd, Matrix_xSym), R_DimSymbol));if (nnz > ncj) { /* calculate fill-reducing permutation */SEXP fac = VECTOR_ELT(flist, j);SEXP fcp = PROTECT(duplicate(fac));int *iPerm = Calloc(ncj, int);ssc_metis_order(ncj, cp, ci, Perm, iPerm);/* apply to the crosstabulation, L, and ZZpO */bCrosstab_permute(ZtZ, nf, j, nlev, iPerm);bCrosstab_permute(L, nf, j, nlev, iPerm);symmetric_permute(VECTOR_ELT(ZZpO, j), nlev[j], iPerm);/* apply to the factor */factor_levels_permute(fac, fcp, Perm, iPerm);/* symbolic analysis to get Parent */R_ldl_symbolic(ncj, cp, ci, Lp, INTEGER(VECTOR_ELT(parent, 0)),(int *) NULL, (int *) NULL);for (i = 0; i < ncj; i++)INTEGER(VECTOR_ELT(parent, 1))[i] =(INTEGER(VECTOR_ELT(parent, 0))[i] < 0) ? -1 : j;nnz = Lp[ncj];SET_SLOT(Ljj, Matrix_iSym, allocVector(INTSXP, nnz));SET_SLOT(Ljj, Matrix_xSym,alloc3Darray(REALSXP, dims[0], dims[1], nnz));Free(iPerm); UNPROTECT(1);} else {for (i = 0; i < ncj; i++) {Lp[i] = 0;INTEGER(VECTOR_ELT(parent,0))[i] = -1;INTEGER(VECTOR_ELT(parent,1))[i] = -1;Perm[i] = i;}Lp[ncj] = 0;SET_SLOT(Ljj, Matrix_iSym, allocVector(INTSXP, 0));SET_SLOT(Ljj, Matrix_xSym,alloc3Darray(REALSXP, dims[0], dims[1], 0));}for (k = j+1; k < nf; k++) { /* Update other blocks in this column */symbolic_right_unit_mm_trans(ncj, INTEGER(VECTOR_ELT(parent, 0)),VECTOR_ELT(L, Lind(k,j)));}for (k = j+1; k < nf; k++) { /* Update remaining columns */for (i = k; i < nf; i++) block_update(L, ZZpO, j, k, i);}}/* Convert blockwise Parent arrays to extended Parent arrays */for (j = 0; j < (nf - 1); j++) { /* Parent[nf] does not need conversion */SEXP Ljp1j = VECTOR_ELT(L, Lind(j + 1, j)),LpP = GET_SLOT(Ljp1j, Matrix_pSym);int *Li = INTEGER(GET_SLOT(Ljp1j, Matrix_iSym)),*Lp = INTEGER(LpP),*block = INTEGER(VECTOR_ELT(VECTOR_ELT(Parent, j), 1)),*parent = INTEGER(VECTOR_ELT(VECTOR_ELT(Parent, j), 0)),i, nlev = length(LpP) - 1;for (i = 0; i < nlev; i++) {if (block[i] < 0) {block[i] = j + 1;parent[i] = Li[Lp[i]];}}}Free(nlev);}