/* Algorithm AS 51 Appl. Statist. (1972), vol. 21, p. 218 original (C) Royal Statistical Society 1972 Performs an iterative proportional fit of the marginal totals of a contingency table. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #undef max #undef min #undef abs #define max(a, b) ((a) < (b) ? (b) : (a)) #define min(a, b) ((a) > (b) ? (b) : (a)) #define abs(x) ((x) >= 0 ? (x) : -(x)) static void collap(int *nvar, double *x, double *y, int *locy, int *nx, int *ny, int *dim, int *config); static void adjust(int *nvar, double *x, double *y, double *z, int *locz, int *nx, int *ny, int *nz, int *dim, int *config, double *d); static int *lvector(int n); /* Table of constant values */ static int c__1 = 1; void loglin(int *nvar, int *dim, int *ncon, int *config, int *ntab, double *table, double *fit, int *locmar, int *nmar, double *marg, int *nu, double *u, double *maxdev, int *maxit, double *dev, int *nlast, int *ifault) { int i, j, k, n, point, size, *icon, *check; double x, y, xmax; check = lvector(*nvar); icon = lvector(*nvar); /* Parameter adjustments */ --dim; --locmar; config -= *nvar + 1; --fit; --table; --marg; --u; --dev; /* Function body */ *ifault = 0; *nlast = 0; /* Check validity of NVAR, the number of variables, and of maxit, the maximum number of iterations */ if (*nvar > 0 && *maxit > 0) { goto L10; } L5: *ifault = 4; return; /* Look at table and fit constants */ L10: size = 1; for (j = 1; j <= *nvar; ++j) { if (dim[j] <= 0) { goto L5; } size *= dim[j]; } if (size <= *ntab) { goto L40; } L35: *ifault = 2; return; L40: x = 0.; y = 0.; for (i = 1; i <= size; ++i) { if (table[i] < 0. || fit[i] < 0.) { goto L5; } x += table[i]; y += fit[i]; } /* Make a preliminary adjustment to obtain the fit to an empty configuration list */ if (y == 0.) { goto L5; } x /= y; for (i = 1; i <= size; ++i) { fit[i] = x * fit[i]; } if (*ncon <= 0 || config[*nvar + 1] == 0) { return; } /* Allocate marginal tables */ point = 1; for (i = 1; i <= *ncon; ++i) { /* A zero beginning a configuration indicates that the list is completed */ if (config[i * *nvar + 1] == 0) { goto L160; } /* Get marginal table size. While doing this task, see if the configuration list contains duplications or elements out of range. */ size = 1; for (j = 0; j < *nvar; ++j) { check[j] = 0; } for (j = 1; j <= *nvar; ++j) { k = config[j + i * *nvar]; /* A zero indicates the end of the string. */ if (k == 0) { goto L130; } /* See if element is valid. */ if (k >= 0 && k <= *nvar) { goto L100; } L95: *ifault = 1; return; /* Check for duplication */ L100: if (check[k - 1]) { goto L95; } check[k - 1] = (1); /* Get size */ size *= dim[k]; } /* Since U is used to store fitted marginals, size must not exceed NU */ L130: if (size > *nu) { goto L35; } /* LOCMAR points to marginal tables to be placed in MARG */ locmar[i] = point; point += size; } /* Get N, number of valid configurations */ i = *ncon + 1; L160: n = i - 1; /* See if MARG can hold all marginal tables */ if (point > *nmar + 1) { goto L35; } /* Obtain marginal tables */ for (i = 1; i <= n; ++i) { for (j = 1; j <= *nvar; ++j) { icon[j - 1] = config[j + i * *nvar]; } collap(nvar, &table[1], &marg[1], &locmar[i], ntab, nmar, &dim[1], icon); } /* Perform iterations */ for (k = 1; k <= *maxit; ++k) { /* XMAX is maximum deviation observed between fitted and true marginal during a cycle */ xmax = 0.; for (i = 1; i <= n; ++i) { for (j = 1; j <= *nvar; ++j) { icon[j - 1] = config[j + i * *nvar]; } collap(nvar, &fit[1], &u[1], &c__1, ntab, nu, &dim[1], icon); adjust(nvar, &fit[1], &u[1], &marg[1], &locmar[i], ntab, nu, nmar, &dim[1], icon, &xmax); } /* Test convergence */ dev[k] = xmax; if (xmax < *maxdev) { goto L240; } } if (*maxit > 1) { goto L230; } *nlast = 1; return; /* No convergence */ L230: *ifault = 3; *nlast = *maxit; return; /* Normal termination */ L240: *nlast = k; return; } /* Algorithm AS 51.1 Appl. Statist. (1972), vol. 21, p. 218 Computes a marginal table from a complete table. All parameters are assumed valid without test. The larger table is X and the smaller one is Y. */ void collap(int *nvar, double *x, double *y, int *locy, int *nx, int *ny, int *dim, int *config) { int i, j, k, l, n, locu, *coord, *size; size = lvector(*nvar + 1); coord = lvector(*nvar); /* Parameter adjustments */ --config; --dim; --x; --y; /* Initialize arrays */ size[0] = 1; for (k = 1; k <= *nvar; ++k) { l = config[k]; if (l == 0) { goto L20; } size[k] = size[k - 1] * dim[l]; } /* Find number of variables in configuration */ k = *nvar + 1; L20: n = k - 1; /* Initialize Y. First cell of marginal table is at Y(LOCY) and table has SIZE(K) elements */ locu = *locy + size[k - 1] - 1; for (j = *locy; j <= locu; ++j) { y[j] = 0.; } /* Initialize coordinates */ for (k = 0; k < *nvar; ++k) { coord[k] = 0; } /* Find locations in tables */ i = 1; L60: j = *locy; for (k = 1; k <= n; ++k) { l = config[k]; j += coord[l - 1] * size[k - 1]; } y[j] += x[i]; /* Update coordinates */ ++i; for (k = 1; k <= *nvar; ++k) { ++coord[k - 1]; if (coord[k - 1] < dim[k]) { goto L60; } coord[k - 1] = 0; } return; } /* Algorithm AS 51.2 Appl. Statist. (1972), vol. 21, p. 218 Makes proportional adjustment corresponding to CONFIG. All parameters are assumed valid without test. */ void adjust(int *nvar, double *x, double *y, double *z, int *locz, int *nx, int *ny, int *nz, int *dim, int *config, double *d) { int i, j, k, l, n, *coord, *size; double e; size = lvector(*nvar + 1); coord = lvector(*nvar); /* Parameter adjustments */ --config; --dim; --x; --y; --z; /* Set size array */ size[0] = 1; for (k = 1; k <= *nvar; ++k) { l = config[k]; if (l == 0) { goto L20; } size[k] = size[k - 1] * dim[l]; } /* Find number of variables in configuration */ k = *nvar + 1; L20: n = k - 1; /* Test size of deviation */ l = size[k - 1]; j = 1; k = *locz; for (i = 1; i <= l; ++i) { e = abs(z[k] - y[j]); if (e > *d) { *d = e; } ++j; ++k; } /* Initialize coordinates */ for (k = 0; k < *nvar; ++k) { coord[k] = 0; } i = 1; /* Perform adjustment */ L50: j = 0; for (k = 1; k <= n; ++k) { l = config[k]; j += coord[l - 1] * size[k - 1]; } k = j + *locz; ++j; /* Note that Y(J) should be non-negative */ if (y[j] <= 0.) { x[i] = 0.; } if (y[j] > 0.) { x[i] = x[i] * z[k] / y[j]; } /* Update coordinates */ ++i; for (k = 1; k <= *nvar; ++k) { ++coord[k - 1]; if (coord[k - 1] < dim[k]) { goto L50; } coord[k - 1] = 0; } return; } /* Auxiliary routine to get rid of limitations on the number of factors in the model. Changed to use R_alloc to avoid memory leak if routine was interrupted. */ static int *lvector(int n) { return (int *) R_alloc(n, sizeof(int)); }