IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
May 5, 2005, 12:10:12 PM (21 years ago)
Author:
gusciora
Message:

Eugene's mods to LM minimization.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psLib/src/math/psMinimize.c

    r3578 r3852  
    99 *  @author GLG, MHPCC
    1010 *
    11  *  @version $Revision: 1.110 $ $Name: not supported by cvs2svn $
    12  *  @date $Date: 2005-03-31 01:02:15 $
     11 *  @version $Revision: 1.111 $ $Name: not supported by cvs2svn $
     12 *  @date $Date: 2005-05-05 22:10:12 $
    1313 *
    1414 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
     
    557557}
    558558
    559 /******************************************************************************
    560 psMinimizeLMChi2():  This routine will take an procedure which calculates
    561 an arbitrary function and it's derivative and minimize the chi-squared match
    562 between that function at the specified coords and the specified value at
    563 those coords.
    564  
    565 XXX: Do this:
    566  After checking that all entries in the paramMask are 1 or 0, when
    567  forming the A matrix from alpha, try this:
    568  
    569      A[i][i] = (1 + lambda*paramask[i]) * alpha[i][i];
    570  
    571 XXX: This is very different from what is specified in the SDR.  Must
    572 coordinate with IfA on new SDR.
    573  
    574 XXX: Do vector/image recycles.
    575  
    576 XXX: probably yErr will be part of the SDR.
    577  
    578 XXX: This must work for both F32 and F64.  F32 is currently implemented.
    579      Note: since the LUD routines are only implemented in F64, then we
    580      will have to convert all F32 input vectors to F64 regardless.  So,
    581      the F64 port might be.
    582  
    583 XXX: Must update the covar matrix.
    584  *****************************************************************************/
     559psF64 p_psImageGetElementF64(psImage *a, int i, int j);
     560
     561// XXX EAM these two functions are useful for testing
     562// XXX EAM this should move to psImage.c
     563bool p_psImagePrint (FILE *f, psImage *a, char *name)
     564{
     565
     566    fprintf (f, "matrix: %s\n", name);
     567
     568    for (int j = 0; j < a[0].numRows; j++) {
     569        for (int i = 0; i < a[0].numCols; i++) {
     570            fprintf (f, "%f  ", p_psImageGetElementF64(a, i, j));
     571        }
     572        fprintf (f, "\n");
     573    }
     574    fprintf (f, "\n");
     575    return (true);
     576}
     577
     578// XXX EAM this should move to psVector.c
     579bool p_psVectorPrint (FILE *f, psVector *a, char *name)
     580{
     581
     582    fprintf (f, "vector: %s\n", name);
     583
     584    for (int i = 0; i < a[0].n; i++) {
     585        fprintf (f, "%f\n", p_psVectorGetElementF64(a, i));
     586    }
     587    fprintf (f, "\n");
     588    return (true);
     589}
     590
     591// XXX EAM this is my re-implementation of MinLM
    585592psBool psMinimizeLMChi2(psMinimization *min,
    586593                        psImage *covar,
     
    591598                        const psVector *yErr,
    592599                        psMinimizeLMChi2Func func)
     600{
     601    PS_PTR_CHECK_NULL(min, NULL);
     602    PS_VECTOR_CHECK_NULL(params, NULL);
     603    PS_VECTOR_CHECK_EMPTY(params, NULL);
     604    PS_PTR_CHECK_NULL(x, NULL);
     605    PS_VECTOR_CHECK_NULL(y, NULL);
     606    PS_VECTOR_CHECK_EMPTY(y, NULL);
     607    PS_VECTOR_CHECK_SIZE_EQUAL(x, y, NULL);
     608    PS_PTR_CHECK_NULL(func, NULL);
     609
     610    // this function has test and current values for several things
     611    // the current best value is in lower case
     612    // the next guess value is in upper case
     613
     614    // allocate internal arrays (current vs Guess)
     615    psImage *alpha   = psImageAlloc (params->n, params->n, PS_TYPE_F64);
     616    psImage *Alpha   = psImageAlloc (params->n, params->n, PS_TYPE_F64);
     617    psVector *beta   = psVectorAlloc (params->n, PS_TYPE_F64);
     618    psVector *Beta   = psVectorAlloc (params->n, PS_TYPE_F64);
     619    psVector *Params = psVectorAlloc (params->n, PS_TYPE_F64);
     620    psVector *dy     = NULL;
     621    psF64 chisq = 0.0;
     622    psF64 Chisq = 0.0;
     623    psF64 lambda = 0.001;
     624
     625    // the initial guess on params is provided by the user
     626    Params = psVectorCopy (Params, params, PS_TYPE_F32);
     627
     628    // the user provides the error or NULL.  we need to convert
     629    // to appropriate weights
     630    dy = psVectorAlloc (y->n, PS_TYPE_F32);
     631    if (yErr != NULL) {
     632        for (int i = 0; i < dy->n; i++) {
     633            dy->data.F32[i] = 1.0 / PS_SQR (yErr->data.F32[i]);
     634        }
     635    } else {
     636        for (int i = 0; i < dy->n; i++) {
     637            dy->data.F32[i] = 1.0;
     638        }
     639    }
     640
     641    // calculate initial alpha and beta, set chisq (min->value)
     642    min->value = p_psMinLM_SetABX (alpha, beta, params, x, y, dy, func);
     643    # ifndef PS_NO_TRACE
     644    // dump some useful info if trace is defined
     645    if (psTraceGetLevel (".psLib.dataManip.psMinimizeLMChi2") > 4) {
     646        p_psImagePrint  (psTraceGetDestination(), alpha, "alpha guess");
     647        p_psVectorPrint (psTraceGetDestination(), beta, "beta guess");
     648        p_psVectorPrint (psTraceGetDestination(), params, "params guess");
     649    }
     650    # endif /* PS_NO_TRACE */
     651
     652
     653    // iterate until the tolerance is reached, or give up
     654    while ((min->lastDelta > min->tol) && (min->iter < min->maxIter)) {
     655
     656        // set a new guess for Alpha, Beta, Params
     657        p_psMinLM_GuessABP (Alpha, Beta, Params, alpha, beta, params, lambda);
     658
     659        # ifndef PS_NO_TRACE
     660        // dump some useful info if trace is defined
     661        if (psTraceGetLevel (".psLib.dataManip.psMinimizeLMChi2") > 4) {
     662            p_psImagePrint  (psTraceGetDestination(), Alpha, "alpha guess");
     663            p_psVectorPrint (psTraceGetDestination(), Beta, "beta guess");
     664            p_psVectorPrint (psTraceGetDestination(), Params, "params guess");
     665        }
     666        # endif /* PS_NO_TRACE */
     667
     668        // calculate Chisq for new guess, update Alpha & Beta
     669        Chisq = p_psMinLM_SetABX (Alpha, Beta, Params, x, y, dy, func);
     670        psTrace (".psLib.dataManip.psMinimizeLMChi2", 3, "chisq: %f, Chisq %f, delta: %f\n", chisq, Chisq, min->lastDelta);
     671
     672        // accept new guess (if improvement), or increase lambda
     673        if (Chisq < min->value) {
     674            min->lastDelta = (min->value - Chisq) / (dy->n - params->n);
     675            min->value = Chisq;
     676            alpha  = psImageCopy (alpha, Alpha, PS_TYPE_F64);
     677            beta   = psVectorCopy (beta, Beta, PS_TYPE_F64);
     678            params = psVectorCopy (params, Params, PS_TYPE_F32);
     679            lambda *= 0.1;
     680        } else {
     681            lambda *= 10.0;
     682        }
     683        min->iter ++;
     684    }
     685    psTrace (".psLib.dataManip.psMinimizeLMChi2", 3, "chisq: %f, Chisq %f, delta: %f\n", chisq, Chisq, min->lastDelta);
     686
     687    // free the internal temporary data
     688    psFree (alpha);
     689    psFree (Alpha);
     690    psFree (beta);
     691    psFree (Beta);
     692    psFree (Params);
     693    psFree (dy);
     694    return (true);
     695}
     696
     697// XXX EAM: this needs to respect the mask on params
     698// XXX EAM: check not NULL on alpha, beta, params
     699// alpha, beta, params are already allocated
     700psF64 p_psMinLM_SetABX (psImage  *alpha,
     701                        psVector *beta,
     702                        psVector *params,
     703                        const psArray  *x,
     704                        const psVector *y,
     705                        const psVector *dy,
     706                        psMinimizeLMChi2Func func)
     707{
     708
     709    psF64 chisq;
     710    psF64 delta;
     711    psF64 weight;
     712    psF64 ymodel;
     713    psVector *deriv = psVectorAlloc (params->n, PS_TYPE_F32);
     714
     715    // zero alpha and beta for summing below
     716    for (int j = 0; j < params->n; j++) {
     717        for (int k = 0; k < params->n; k++) {
     718            alpha->data.F64[j][k] = 0;
     719        }
     720        beta->data.F64[j] = 0;
     721    }
     722    chisq = 0.0;
     723
     724    // calculate chisq, alpha, beta
     725    for (int i = 0; i < y->n; i++) {
     726        ymodel = func (deriv, params, (psVector *) x->data[i]);
     727
     728        delta = ymodel - y->data.F32[i];
     729        chisq += PS_SQR (delta) * dy->data.F32[i];
     730
     731        for (int j = 0; j < params->n; j++) {
     732            weight = deriv->data.F32[j] * dy->data.F32[i];
     733            for (int k = 0; k <= j; k++) {
     734                alpha->data.F64[j][k] += weight * deriv->data.F32[k];
     735            }
     736            beta->data.F64[j] += weight * delta;
     737        }
     738    }
     739
     740    // calculate lower-left half of alpha
     741    for (int j = 1; j < params->n; j++) {
     742        for (int k = 0; k < j; k++) {
     743            alpha->data.F64[k][j] = alpha->data.F64[j][k];
     744        }
     745    }
     746    psFree (deriv);
     747    return (chisq);
     748}
     749
     750// XXX EAM : can we use static copies of LUv, LUm, A?
     751psBool p_psMinLM_GuessABP (psImage  *Alpha,
     752                           psVector *Beta,
     753                           psVector *Params,
     754                           psImage  *alpha,
     755                           psVector *beta,
     756                           psVector *params,
     757                           psF64 lambda)
     758{
     759
     760    # define USE_LU_DECOMP 1
     761    # if (USE_LU_DECOMP)
     762        psVector *LUv = NULL;
     763    psImage  *LUm = NULL;
     764    psImage  *A   = NULL;
     765    psF32    det;
     766
     767    // LU decomposition version
     768    psTrace (".pslib.dataManip.psMinLM_GuessABP", 3, "using LUD version");
     769
     770    // set new guess values (creates matrix A)
     771    A = psImageCopy (NULL, alpha, PS_TYPE_F64);
     772    for (int j = 0; j < params->n; j++) {
     773        A->data.F64[j][j] = alpha->data.F64[j][j] * (1.0 + lambda);
     774    }
     775
     776    // solve A*beta = Beta (Alpha = 1/A)
     777    // these operations do not modify the input values (creates LUm, LUv)
     778    LUm   = psMatrixLUD (NULL, &LUv, A);
     779    Beta  = psMatrixLUSolve (Beta, LUm, beta, LUv);
     780    Alpha = psMatrixInvert (Alpha, A, &det);
     781
     782    # else
     783        // gauss-jordan version
     784        psTrace (".pslib.dataManip.psMinLM_GuessABP", 3, "using Gauss-J version");
     785
     786    // set new guess values (creates matrix A)
     787    Beta = psVectorCopy (Beta, beta, PS_TYPE_F64);
     788    Alpha = psImageCopy (Alpha, alpha, PS_TYPE_F64);
     789    for (int j = 0; j < params->n; j++) {
     790        Alpha->data.F64[j][j] = alpha->data.F64[j][j] * (1.0 + lambda);
     791    }
     792
     793    psGaussJordan (Alpha, Beta);
     794    # endif
     795
     796    // apply beta to get new params values
     797    for (int j = 0; j < params->n; j++) {
     798        Params->data.F32[j] = params->data.F32[j] - Beta->data.F64[j];
     799    }
     800
     801    # if (USE_LU_DECOMP)
     802        psFree (A);
     803    psFree (LUm);
     804    psFree (LUv);
     805    # endif
     806
     807    return true;
     808}
     809
     810# define SWAP(X,Y) {double tmp=(X); (X) = (Y); (Y) = tmp;}
     811
     812// XXX EAM : temporary gauss-jordan solver based on gene's
     813// version based on the Numerical Recipes version
     814bool psGaussJordan (psImage *a, psVector *b)
     815{
     816
     817    int *indxc,*indxr,*ipiv;
     818    int Nx, icol, irow;
     819    int i, j, k, l, ll;
     820    float big, dum, pivinv;
     821    psF64 *vector;
     822    psF64 **matrix;
     823
     824    Nx = a->numCols;
     825    matrix = a->data.F64;
     826    vector = b->data.F64;
     827
     828    indxc = psAlloc (Nx*sizeof(int));
     829    indxr = psAlloc (Nx*sizeof(int));
     830    ipiv  = psAlloc (Nx*sizeof(int));
     831    for (j = 0; j < Nx; j++)
     832        ipiv[j] = 0;
     833
     834    irow = icol = 0;
     835    big = fabs(matrix[0][0]);
     836
     837    for (i = 0; i < Nx; i++) {
     838        big = 0.0;
     839        for (j = 0; j < Nx; j++) {
     840            if (!finite(matrix[i][j])) {
     841                // XXX EAM: this should use the psError stack
     842                fprintf (stderr, "GAUSSJ: NaN\n");
     843                goto fescape;
     844            }
     845            if (ipiv[j] != 1) {
     846                for (k = 0; k < Nx; k++) {
     847                    if (ipiv[k] == 0) {
     848                        if (fabs (matrix[j][k]) >= big) {
     849                            big  = fabs (matrix[j][k]);
     850                            irow = j;
     851                            icol = k;
     852                        }
     853                    } else {
     854                        if (ipiv[k] > 1) {
     855                            // XXX EAM: this should use the psError stack
     856                            fprintf (stderr, "GAUSSJ: Singular Matrix! (1)\n");
     857                            goto fescape;
     858                        }
     859                    }
     860                }
     861            }
     862        }
     863        ipiv[icol]++;
     864        if (irow != icol) {
     865            for (l = 0; l < Nx; l++) {
     866                SWAP (matrix[irow][l], matrix[icol][l]);
     867            }
     868            SWAP (vector[irow], vector[icol]);
     869        }
     870        indxr[i] = irow;
     871        indxc[i] = icol;
     872        if (matrix[icol][icol] == 0.0) {
     873            // XXX EAM: this should use the psError stack
     874            fprintf (stderr, "GAUSSJ: Singular Matrix! (2)\n");
     875            goto fescape;
     876        }
     877        pivinv = 1.0 / matrix[icol][icol];
     878        matrix[icol][icol] = 1.0;
     879        for (l = 0; l < Nx; l++) {
     880            matrix[icol][l] *= pivinv;
     881        }
     882        vector[icol] *= pivinv;
     883
     884        for (ll = 0; ll < Nx; ll++) {
     885            if (ll != icol) {
     886                dum = matrix[ll][icol];
     887                matrix[ll][icol] = 0.0;
     888                for (l = 0; l < Nx; l++)
     889                    matrix[ll][l] -= matrix[icol][l]*dum;
     890                vector[ll] -= vector[icol]*dum;
     891            }
     892        }
     893    }
     894
     895    for (l = Nx - 1; l >= 0; l--) {
     896        if (indxr[l] != indxc[l])
     897            for (k = 0; k < Nx; k++)
     898                SWAP (matrix[k][indxr[l]], matrix[k][indxc[l]]);
     899    }
     900    psFree (ipiv);
     901    psFree (indxr);
     902    psFree (indxc);
     903    return (true);
     904
     905fescape:
     906    psFree (ipiv);
     907    psFree (indxr);
     908    psFree (indxc);
     909    return (false);
     910}
     911
     912/******************************************************************************
     913psMinimizeLMChi2():  This routine will take an procedure which calculates
     914an arbitrary function and it's derivative and minimize the chi-squared match
     915between that function at the specified coords and the specified value at
     916those coords.
     917 
     918XXX: Do this:
     919 After checking that all entries in the paramMask are 1 or 0, when
     920 forming the A matrix from alpha, try this:
     921 
     922     A[i][i] = (1 + lambda*paramask[i]) * alpha[i][i];
     923 
     924XXX: This is very different from what is specified in the SDR.  Must
     925coordinate with IfA on new SDR.
     926 
     927XXX: Do vector/image recycles.
     928 
     929XXX: probably yErr will be part of the SDR.
     930 
     931XXX: This must work for both F32 and F64.  F32 is currently implemented.
     932     Note: since the LUD routines are only implemented in F64, then we
     933     will have to convert all F32 input vectors to F64 regardless.  So,
     934     the F64 port might be.
     935 
     936XXX: Must update the covar matrix.
     937 *****************************************************************************/
     938psBool psMinimizeLMChi2Old(psMinimization *min,
     939                           psImage *covar,
     940                           psVector *params,
     941                           const psVector *paramMask,
     942                           const psArray *x,
     943                           const psVector *y,
     944                           const psVector *yErr,
     945                           psMinimizeLMChi2Func func)
    593946{
    594947    PS_PTR_CHECK_NULL(min, NULL);
     
    636989    psF32 currChi2 = 0.0;
    637990    psF32 newChi2 = 0.0;
    638     psF32 lamda = 0.00005;
    639     lamda = 0.05;
     991    psF32 lamda = 0.00005;  // XXX EAM : this starting value is VERY small (lamda is mis-spelt)
     992    lamda = 0.05;  // XXX EAM : this starting value is quite large (lamda is mis-spelt)
    640993
    641994    psTrace(".psLib.dataManip.psMinimize", 6,
     
    6611014        //
    6621015        currChi2 = 0.0;
    663         currValueVec = func(deriv, params, x);
     1016        // currValueVec = func(deriv, params, x);
     1017
     1018        // XXX EAM: use BinaryOp ?
     1019        // t1 = BinaryOp (NULL, currValueVec, "-", y);
     1020        // t1 = BinaryOp (t1, t1, "*", t1);
     1021
     1022        // XXX EAM: this ignores yErr
    6641023        for (n=0;n<numData;n++) {
    6651024            currChi2+= (currValueVec->data.F32[n] - y->data.F32[n]) *
     
    6701029        }
    6711030
     1031        // XXX EAM: this is just for tracing
    6721032        for (p=0;p<numParams;p++) {
    6731033            psTrace(".psLib.dataManip.psMinimize", 6,
     
    6791039        //
    6801040        // Mask elements of the derivative for each data point.
    681         //
     1041        // XXX EAM : is this necessary?  probably not...
    6821042        for (p=0;p<numParams;p++) {
    6831043            if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) {
     
    6901050        //
    6911051        // Calculate the BETA vector.
    692         //
     1052        // XXX EAM: I think this is wrong
    6931053        for (p=0;p<numParams;p++) {
     1054            if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) {
     1055                continue;
     1056            }
    6941057            beta->data.F64[p] = 0.0;
    6951058            for (n=0;n<numData;n++) {
     
    7071070        //
    7081071        // Calculate the ALPHA matrix.
    709         //
     1072        // XXX EAM: also wrong? (missing yErr)
    7101073        for (k=0;k<numParams;k++) {
    7111074            for (l=0;l<numParams;l++) {
     
    7731136        //
    7741137        newChi2 = 0.0;
    775         newValueVec = func(deriv, newParams, x);
     1138        // newValueVec = func(deriv, newParams, x);
    7761139        for (n=0;n<numData;n++) {
    7771140            newChi2+= (newValueVec->data.F32[n] - y->data.F32[n]) *
     
    10981461    min->value = 0.0;
    10991462    min->iter = 0;
    1100     min->lastDelta = 0.0;
     1463    min->lastDelta = tol + 1;
    11011464
    11021465    return(min);
Note: See TracChangeset for help on using the changeset viewer.