The R Project SVN R-packages

Rev

Blame | Last modification | View Log | Download | RSS feed

/*  SCCS @(#)agexact.c  5.2 10/27/98 */
/*
** Anderson-Gill formulation of the cox Model
**   Do an exact calculation of the partial likelihood. (CPU city!)
**
**  the input parameters are
**
**       maxiter      :number of iterations
**       nused        :number of people
**       nvar         :number of covariates
**       start(n)     :each row covers the time interval (start,stop]
**       stop(n)      :
**       event(n)     :was there an event at 'stop':1=dead , 0=censored
**       covar(nv,n)  :covariates for person i.
**                        Note that S sends this in column major order.
**       strata(n)    :marks the strata.  Will be 1 if this person is the
**                       last one in a strata.  If there are no strata, the
**                       vector can be identically zero, since the nth person's
**                       value is always assumed to be = to 1.
**       offset(n)    :linear offset
**       eps          :tolerance for convergence.  Iteration continues until
**                       the percent change in loglikelihood is <= eps.
**       tol_chol     : tolerance for the Cholesky routine
**
**  returned parameters
**       means(nv)    :column means of the X matrix
**       beta(nv)     :the vector of answers (at start contains initial est)
**       u            :the first derivative vector at solution
**       imat(nv,nv)  :the variance matrix at beta=final, also a ragged array
**                      if flag<0, imat is undefined upon return
**       loglik(2)    :loglik at beta=initial values, at beta=final
**       sctest       :the score test at beta=initial
**       flag         :success flag  1000  did not converge
**                                   1 to nvar: rank of the solution
**       maxiter      :actual number of iterations used
**
**  work arrays
**       score(n)              the score exp(beta*z)
**       a(nvar)
**       cmat(nvar,nvar)       ragged array
**       newbeta(nvar)         always contains the "next iteration"
**
**  the 4 arrays score, a, cmat, and newbeta are passed as a single
**    vector of storage, and then broken out.
**
**  calls functions:  cholesky2, chsolve2, chinv2
**
**  the data must be sorted by ascending time within strata, deaths before
**          living within tied times.
*/
#include <math.h>
#include "survS.h"
#include "survproto.h"

void agexact(int *maxiter,  int *nusedx,   int *nvarx,   double *start, 
         double *stop,   int *event,    double *covar2,double *offset, 
         int   *strata, double *means,  double *beta,  double *u, 
         double *imat2,  double loglik[2], int *flag,  double *work, 
         int   *work2,  double *eps,    double *tol_chol, double *sctest)
{
    int i,j,k, l, person;
    int     iter;
    int     n, nvar;

    double **covar, **cmat, **imat;  /*ragged array versions*/
    double *a, *newbeta;
    double *score, *newvar;
    double  denom, zbeta, weight;
    double  time;
    double  temp;
    double  newlk=0; /*-Wall*/
    int     halving;    /*are we doing step halving at the moment? */
    int     nrisk, deaths;
    int *index, *atrisk;

    n = *nusedx;
    nvar  = *nvarx;
    /*
    **  Set up the ragged arrays
    */
    covar= dmatrix(covar2, n, nvar);
    imat = dmatrix(imat2,  nvar, nvar);
    cmat = dmatrix(work,   nvar, nvar);
    a = work + nvar*nvar;
    newbeta = a + nvar;
    score   = newbeta + nvar;
    newvar  = score + n;
    index =  (int *) work2;
    atrisk=  index+n;

    /*
    ** Subtract the mean from each covar, as this makes the regression
    **  much more stable
    */
    for (i=0; i<nvar; i++) {
    temp=0;
    for (person=0; person<n; person++) temp += covar[i][person];
    temp /= n;
    means[i] = temp;
    }

    /*
    ** do the initial iteration step
    */
    loglik[1] =0;
    for (i=0; i<nvar; i++) {
    u[i] =0;
    for (j=0; j<nvar; j++)
        imat[i][j] =0 ;
    }

    for (person=0; person<n; person++) {
    zbeta = 0;      /* form the term beta*z   (vector mult) */
    for (i=0; i<nvar; i++)
        zbeta += beta[i]*covar[i][person];
    score[person] = exp(zbeta + offset[person]);
        }

    for (person=0; person<n;) {
    if (event[person]==0) person++;
    else {
        denom =0;
        for (i=0; i<nvar; i++) {
        a[i] =0;
        for (j=0; j<nvar; j++)  cmat[i][j]=0;
        }

        /*
        ** Compute whom is in the risk group, and #deaths
        */
        nrisk=0;
        deaths=0;
        time = stop[person];
        for (k=person; k<n; k++) {
        if (stop[k]==time) deaths += event[k];
        if (start[k] < time) {
            atrisk[nrisk]=k;
            nrisk++;
            }
        if (strata[k]==1) break;
        }

        /*
        ** compute the mean and covariance over the risk set (a and c)
        **   It's fast if #deaths=1
        */
        if (deaths==1) {
        for (l=0; l<nrisk; l++) {
            k = atrisk[l];
            weight = score[k];
            denom += weight;
            for (i=0; i<nvar; i++) {
            a[i] = a[i] + weight*covar[i][k];
            for (j=0; j<=i; j++)
                cmat[i][j] += weight*covar[i][k]*covar[j][k];
            }
             }
        }
        else {
        /*
        ** for each unique subset of size "deaths" from the risk set,
        **  the "new variable" is the sum of x over that set.  It's
        **  weight is the product of the weights.  I want a to contain
        **  the weighted sum and c the weighted ss of this new var.
        */
        init_doloop(0,nrisk);
        while(doloop(deaths, index) >=0) {
            for (i=0; i<nvar; i++) newvar[i]=0;
            weight =1;
            for (l=0; l<deaths; l++) {
            k = atrisk[index[l]];
            weight *= score[k];
            for (i=0; i<nvar; i++)  newvar[i]+= covar[i][k];
            }
            denom += weight;
            for (i=0; i<nvar; i++) {
            a[i] = a[i] + weight*newvar[i];
            for (j=0; j<=i; j++)
                cmat[i][j] += weight*newvar[i]*newvar[j];
            }
             }
        }
        /*
        ** Add results into u and imat
        */
        loglik[1] -= log(denom);
        for (i=0; i<nvar; i++) {
        u[i] -=  a[i]/denom;
        for (j=0; j<=i; j++)
            imat[j][i] += (cmat[i][j] - a[i]*a[j]/denom)/denom;
        }
        for (k=person; k<n && stop[k]==time; k++) {
        if (event[k]==1) {
            loglik[1] +=  log(score[k]);
            for (i=0; i<nvar; i++) u[i] += covar[i][k];
            }
        person++;
        if (strata[k]==1) break;
        }
        }
    }   /* end  of accumulation loop */

    loglik[0] = loglik[1];   /* save the loglik for iteration zero  */

    /* am I done?
    **   update the betas and test for convergence
    */

    for (i=0; i<nvar; i++) /*use 'a' as a temp to save u0, for the score test*/
    a[i] = u[i];

    *flag= cholesky2(imat, nvar, *tol_chol);
    chsolve2(imat,nvar,a);        /* a replaced by  a *inverse(i) */

    *sctest=0;
    for (i=0; i<nvar; i++)
    *sctest +=  u[i]*a[i];

    /*
    **  Never, never complain about convergence on the first step.  That way,
    **  if someone HAS to they can force one iter at a time.
    */
    for (i=0; i<nvar; i++) {
    newbeta[i] = beta[i] + a[i];
    }
    if (*maxiter==0) {
    chinv2(imat,nvar);
    for (i=1; i<nvar; i++)
        for (j=0; j<i; j++)  imat[i][j] = imat[j][i];
    *flag=0;
    return;   /* and we leave the old beta in peace */
    }

    /*
    ** here is the main loop
    */
    halving =0 ;             /* =1 when in the midst of "step halving" */
    for (iter=1; iter<=*maxiter; iter++) {
    newlk =0;
    for (i=0; i<nvar; i++) {
        u[i] =0;
        for (j=0; j<nvar; j++)
        imat[i][j] =0;
        }

    for (person=0; person<n; person++) {
        zbeta = 0;      /* form the term beta*z   (vector mult) */
        for (i=0; i<nvar; i++)
        zbeta += newbeta[i]*covar[i][person];
        score[person] = exp(zbeta + offset[person]);
        }

    for (person=0; person<n; ) {
        if (event[person]==0) person++;
        else {
        denom =0;
        for (i=0; i<nvar; i++) {
            a[i] =0;
            for (j=0; j<nvar; j++)  cmat[i][j]=0;
            }

        /*
        ** Compute whom is in the risk group, and #deaths
        */
        nrisk=0;
        deaths=0;
        time = stop[person];
        for (k=person; k<n; k++) {
            if (stop[k]==time) deaths += event[k];
            if (start[k] < time) {
            atrisk[nrisk]=k;
            nrisk++;
            }
            if (strata[k]==1) break;
            }

        /*
        ** compute the mean and covariance over the risk set (a and c)
        **   It's fast if #deaths=1
        */
        if (deaths==1) {
            for (l=0; l<nrisk; l++) {
            k = atrisk[l];
            weight = score[k];
            denom += weight;
            for (i=0; i<nvar; i++) {
                a[i] = a[i] + weight*covar[i][k];
                for (j=0; j<=i; j++)
                cmat[i][j] += weight*covar[i][k]*covar[j][k];
                }
             }
            }
        else {
            /*
            ** for each unique subset of size "deaths" from the risk set,
            **  the "new variable" is the sum of x over that set.  It's
            **  weight is the product of the weights.  I want a to contain
            **  the weighted sum and c the weighted ss of this new var.
            */
            init_doloop(0,nrisk);
            while(doloop(deaths, index) >=0) {
            for (i=0; i<nvar; i++) newvar[i]=0;
            weight =1;
            for (l=0; l<deaths; l++) {
                k = atrisk[index[l]];
                weight *= score[k];
                for (i=0; i<nvar; i++)  newvar[i]+= covar[i][k];
                }
            denom += weight;
            for (i=0; i<nvar; i++) {
                a[i] = a[i] + weight*newvar[i];
                for (j=0; j<=i; j++)
                cmat[i][j] += weight*newvar[i]*newvar[j];
                }
             }
            }
        /*
        ** Add results into u and imat
        */
        newlk -= log(denom);
        for (i=0; i<nvar; i++) {
            u[i] -=  a[i]/denom;
            for (j=0; j<=i; j++)
            imat[j][i] += (cmat[i][j] - a[i]*a[j]/denom)/denom;
            }
        for (k=person; k<n && stop[k]==time; k++) {
            if (event[k]==1) {
            newlk +=  log(score[k]);
            for (i=0; i<nvar; i++) u[i] += covar[i][k];
            }
            person++;
            if (strata[k]==1) break;
            }
        }
        }   /* end  of accumulation loop */

    /* am I done?
    **   update the betas and test for convergence
    */
    *flag = cholesky2(imat, nvar, *tol_chol);

    if (fabs(1-(loglik[1]/newlk))<=*eps ) { /* all done */
        loglik[1] = newlk;
        chinv2(imat, nvar);     /* invert the information matrix */
        for (i=1; i<nvar; i++)
        for (j=0; j<i; j++)  imat[i][j] = imat[j][i];
        for (i=0; i<nvar; i++)
        beta[i] = newbeta[i];
        if (halving==1) *flag= 1000; /*didn't converge after all */
        *maxiter = iter;
        return;
        }

    if (iter==*maxiter) break;  /*skip the step halving and etc */

    if (newlk < loglik[1])   {    /*it is not converging ! */
        halving =1;
        for (i=0; i<nvar; i++)
            newbeta[i] = (newbeta[i] + beta[i]) /2; /*half of old increment */
        }
        else {
        halving=0;
        loglik[1] = newlk;
        chsolve2(imat,nvar,u);

        j=0;
        for (i=0; i<nvar; i++) {
            beta[i] = newbeta[i];
            newbeta[i] = newbeta[i] +  u[i];
            }
        }
    }   /* return for another iteration */

    loglik[1] = newlk;
    chinv2(imat, nvar);
    for (i=1; i<nvar; i++)
    for (j=0; j<i; j++)  imat[i][j] = imat[j][i];
    for (i=0; i<nvar; i++)
    beta[i] = newbeta[i];
    *flag= 1000;
    }