IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
May 31, 2013, 6:19:08 AM (13 years ago)
Author:
eugene
Message:

add a new version of psMinimizeLMM using a better convergence criterion -- delta parameters not delta chisq

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/eam_branches/ipp-20130509/psLib/src/math/psMinimizeLMM.c

    r29542 r35615  
    360360}
    361361
    362 
    363362/******************************************************************************
    364363psMinimizeLMChi2():  This routine will take an procedure which calculates an
     
    580579}
    581580
     581/******************************************************************************
     582psMinimizeLMChi2(): This routine takes a function-pointer (func) which calculates an arbitrary
     583function and it's derivatives and minimizes the chi-squared match between that function at the
     584specified points and the specified value at those points.
     585
     586The original version of this function used a convergence criterion based on the change in
     587chisq.  this has problems since it depends on the choice of points used to measure the fit.
     588(consider a gaussian on a background : it 100 pixels are used -- and some or most contribute to
     589the chisq -- and the delta-chisq is 10%, then the same change in model fit will yield a
     590delta-chisq of 1% if 1000 pixels are used (all but the 100 measuring the background)).
     591
     592This implementation uses changes to the parameters and stops if they are no longer significant.
     593
     594This requires F32 input data; all internal calls use F32.
     595  *****************************************************************************/
     596bool psMinimizeLMChi2_Alt(
     597    psMinimization *min,
     598    psImage *covar,
     599    psVector *params,
     600    psMinConstraint *constraint,
     601    const psArray *x,
     602    const psVector *y,
     603    const psVector *yWt,
     604    psMinimizeLMChi2Func func)
     605{
     606    psTrace("psLib.math", 3, "---- begin ----\n");
     607    PS_ASSERT_PTR_NON_NULL(min, false);
     608    PS_ASSERT_VECTOR_NON_NULL(params, false);
     609    PS_ASSERT_VECTOR_NON_EMPTY(params, false);
     610    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
     611    psVector *paramMask = NULL;
     612    if (constraint != NULL) {
     613        paramMask = constraint->paramMask;
     614        if (paramMask != NULL) {
     615            PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_VECTOR_MASK, false);
     616            PS_ASSERT_VECTORS_SIZE_EQUAL(params, paramMask, false);
     617        }
     618    }
     619    PS_ASSERT_PTR_NON_NULL(x, false);
     620    for (psS32 i = 0 ; i < x->n ; i++) {
     621        psVector *coord = (psVector *) (x->data[i]);
     622        PS_ASSERT_VECTOR_NON_NULL(coord, false);
     623        PS_ASSERT_VECTOR_TYPE(coord, PS_TYPE_F32, false);
     624    }
     625    PS_ASSERT_VECTOR_NON_NULL(y, false);
     626    PS_ASSERT_VECTOR_NON_EMPTY(y, false);
     627    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F32, false);
     628    PS_ASSERT_VECTORS_SIZE_EQUAL(x, y, false);
     629    if (yWt != NULL) {
     630        PS_ASSERT_VECTOR_TYPE(yWt, PS_TYPE_F32, false);
     631        PS_ASSERT_VECTORS_SIZE_EQUAL(y, yWt, false);
     632    }
     633    PS_ASSERT_PTR_NON_NULL(func, false);
     634
     635    psMinimizeLMLimitFunc checkLimits = NULL;
     636    if (constraint) {
     637        checkLimits = constraint->checkLimits;
     638    }
     639
     640    // this function has test and current values for several things
     641    // the current best value is in lower case
     642    // the next guess value is in upper case
     643
     644    // allocate internal arrays (current vs Guess)
     645    psImage *Alpha = NULL;
     646    psVector *Beta = NULL;
     647
     648    // Alpha & Beta only contain elements to represent the unmasked parameters
     649    if (!psMinLM_AllocAB (&Alpha, &Beta, params, paramMask)) {
     650        psAbort ("programming error: no unmasked parameters to be fit\n");
     651    }
     652
     653    psImage *alpha   = psImageAlloc(Alpha->numCols, Alpha->numRows, PS_TYPE_F32);
     654    psVector *beta   = psVectorAlloc(Beta->n, PS_TYPE_F32);
     655    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
     656
     657    psVector *dy     = NULL;
     658    psF32 Chisq = 0.0;
     659    psF32 lambda = 0.001;
     660    psF32 dLinear = 0.0;
     661
     662    // the user provides the error or NULL.  we need to convert
     663    // to appropriate weights
     664    if (yWt != NULL) {
     665        dy = (psVector *) yWt;
     666    } else {
     667        dy = psVectorAlloc(y->n, PS_TYPE_F32);
     668        psVectorInit(dy, 1.0);
     669    }
     670
     671    // number of degrees of freedom for this fit
     672    int nDOF = dy->n - params->n;
     673
     674    // calculate initial alpha and beta, set chisq (min->value)
     675    min->value = psMinLM_SetABX(alpha, beta, params, paramMask, x, y, dy, func);
     676    if (isnan(min->value)) {
     677        min->iter = min->maxIter;
     678        psFree(alpha);
     679        psFree(Alpha);
     680        psFree(beta);
     681        psFree(Beta);
     682        psFree(Params);
     683        return(false);
     684    }
     685    // dump some useful info if trace is defined
     686    if (psTraceGetLevel("psLib.math") >= 6) {
     687        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
     688        p_psVectorPrint(psTraceGetDestination(), beta, "beta guess (0)");
     689    }
     690    if (psTraceGetLevel("psLib.math") >= 5) {
     691        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
     692    }
     693
     694    // iterate until: (a) nIter = min->iter or (b) (chisq / ndof) < maxChisq and deltaChisq < minTol (but don't stop unless Chisq is finite)
     695    bool done = (min->iter >= min->maxIter);
     696    while (!done) {
     697        psTrace("psLib.math", 5, "Iteration number %d.  (max iterations is %d).\n", min->iter, min->maxIter);
     698        psTrace("psLib.math", 5, "Last delta is %f.  stop if < %f, accept if < %f\n", min->lastDelta, min->minTol, min->maxTol);
     699
     700        // set a new guess for Alpha, Beta, Params
     701        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
     702            min->iter ++;
     703            if (min->iter >=  min->maxIter) break;
     704            lambda *= 10.0;
     705            continue;
     706        }
     707
     708        // dump some useful info if trace is defined
     709        if (psTraceGetLevel("psLib.math") >= 6) {
     710            p_psImagePrint(psTraceGetDestination(), Alpha, "Alpha guess (1)");
     711            p_psVectorPrint(psTraceGetDestination(), Beta, "Beta guess (1)");
     712            p_psVectorPrint(psTraceGetDestination(), beta, "beta current (1)");
     713        }
     714        if (psTraceGetLevel("psLib.math") >= 5) {
     715            p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
     716        }
     717
     718        // calculate Chisq for new guess, update Alpha & Beta
     719        Chisq = psMinLM_SetABX(Alpha, Beta, Params, paramMask, x, y, dy, func);
     720        if (isnan(Chisq)) {
     721            min->iter ++;
     722            if (min->iter >= min->maxIter) break;
     723            lambda *= 10.0;
     724            continue;
     725        }
     726
     727        // convergence criterion:
     728        // compare the delta (min->value - Chisq) with the
     729        // expected delta from the linear model (dLinear)
     730        // accept new guess if it is an improvement (rho > 0), or else increase lambda
     731        psF32 rho = (min->value - Chisq) / dLinear;
     732
     733        psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %f, nDOF: %d\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda, nDOF);
     734
     735        psTrace("psLib.math.dLinear", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %f\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda);
     736
     737        // dump some useful info if trace is defined
     738        if (psTraceGetLevel("psLib.math") >= 6) {
     739            p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (2)");
     740            p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (2)");
     741        }
     742
     743        // change in chisq/nDOF since last minimum
     744        min->lastDelta = (min->value - Chisq) / nDOF;
     745
     746        /* rho is positive if the new chisq is smaller; allow for some insignificant change (slight negative rho) */
     747        if (rho >= -1e-6) {
     748            min->value = Chisq;
     749            alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
     750            beta   = psVectorCopy(beta, Beta, PS_TYPE_F32);
     751            params = psVectorCopy(params, Params, PS_TYPE_F32);
     752            lambda *= 0.25;
     753        } else {
     754            lambda *= 10.0;
     755        }
     756        min->iter++;
     757
     758        // ending conditions:
     759        // 1) hard limit : too many iterations
     760        done = (min->iter >= min->maxIter);
     761       
     762        // 2) require chisqDOF < maxChisqDOF (if maxChisqDOF is not NAN)
     763        float chisqDOF = Chisq / nDOF;
     764        if (isfinite(min->maxChisqDOF) && (chisqDOF > min->maxChisqDOF)) {
     765            continue;
     766        }
     767
     768        // 3) require deltaChi > 1e-6
     769        if (min->lastDelta < 1e-5) {
     770            continue;
     771        }
     772
     773        // 4) require rParDelta < \eta * rParSigma
     774        //    rParDelta : radius of parameter change;
     775        //    rParSigma : radius of parameter error
     776       
     777        // note that alpha & beta only represent unmasked parameters
     778
     779        // R(Par_sigma) = \sum (alpha[i][i])
     780        float rParSigma = 0.0;
     781        for (int j = 0; j < beta->n; j++) {
     782            rParSigma += Alpha->data.F32[j][j];
     783        }
     784        rParSigma = sqrt(rParSigma);
     785
     786        // note that beta or Beta is the actual parameter change for this pass :
     787        // new param = old param - beta.  I need to be sure I'm using the new
     788        // or the old one and also the one before or after the matrix eqn Ax = B is solved.
     789        float rParDelta = 0.0;
     790        for (int j = 0; j < beta->n; j++) {
     791            rParDelta += PS_SQR(lastBeta->data.F32[j] - Beta->data.F32[j]);
     792        }
     793        rParDelta = sqrt(rParDelta);
     794
     795        psTrace("psLib.math", 5, "rParDelta : %f, rParSigma: %f, Niter: %d\n", rParDelta, rParSigma, min->iter);
     796    }
     797    psTrace("psLib.math", 5, "chisq: %f, last delta: %f, Niter: %d\n", min->value, min->lastDelta, min->iter);
     798
     799    // construct & return the covariance matrix (if requested)
     800    if (covar != NULL) {
     801        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, NULL, 0.0, NULL)) {
     802            psTrace ("psLib.math", 5, "failure to calculate covariance matrix\n");
     803        }
     804        // set covar values which are not masked
     805        psImageInit (covar, 0.0);
     806        for (int j = 0, J = 0; j < params->n; j++) {
     807            if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) {
     808                covar->data.F32[j][j] = 1.0;
     809                continue;
     810            }
     811            for (int k = 0, K = 0; k < params->n; k++) {
     812                if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[k])) continue;
     813                covar->data.F32[j][k] = Alpha->data.F32[J][K];
     814                K++;
     815            }
     816            J++;
     817        }
     818    }
     819
     820    // free the internal temporary data
     821    psFree(alpha);
     822    psFree(Alpha);
     823    psFree(beta);
     824    psFree(Beta);
     825    psFree(Params);
     826    if (yWt == NULL) {
     827        psFree(dy);
     828    }
     829
     830    // if the last improvement was at least as good as maxTol, accept the fit:
     831    if (min->lastDelta <= min->maxTol) {
     832        psTrace("psLib.math", 6, "---- end (true) ----\n");
     833        return(true);
     834    }
     835    psTrace("psLib.math", 6, "---- end (false) ----\n");
     836    return(false);
     837}
     838
    582839bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask) {
    583840
Note: See TracChangeset for help on using the changeset viewer.