IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Jul 3, 2013, 2:30:22 PM (13 years ago)
Author:
eugene
Message:

add new psMinimizeLMChi2 function with new convergence criterion; add threaded version of psImageSmoothNoMask

File:
1 edited

Legend:

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

    r29542 r35767  
    325325        chisq += PS_SQR(delta) * dy->data.F32[i];
    326326
     327        // XXX remove this later:
     328        // psVector *tmp = x->data[i];
     329        // fprintf (stderr, "%f %f  %f %f  %f\n", tmp->data.F32[0], tmp->data.F32[1], y->data.F32[i], dy->data.F32[i], ymodel);
     330
    327331        if (isnan(dy->data.F32[i])) goto escape;
    328332        if (isnan(delta)) goto escape;
     
    360364}
    361365
     366/******************************************************************************
     367psMinimizeLMChi2():  wrapper to call either _Old or _Alt
     368  *****************************************************************************/
     369bool psMinimizeLMChi2(
     370    psMinimization *min,
     371    psImage *covar,
     372    psVector *params,
     373    psMinConstraint *constraint,
     374    const psArray *x,
     375    const psVector *y,
     376    const psVector *yWt,
     377    psMinimizeLMChi2Func func)
     378{
     379    bool status = psMinimizeLMChi2_Alt(
     380        min,
     381        covar,
     382        params,
     383        constraint,
     384        x,
     385        y,
     386        yWt,
     387        func);
     388    return status;
     389}
    362390
    363391/******************************************************************************
     
    370398XXX Make an F64 version?
    371399  *****************************************************************************/
    372 bool psMinimizeLMChi2(
     400bool psMinimizeLMChi2_Old(
    373401    psMinimization *min,
    374402    psImage *covar,
     
    507535        psF32 rho = (min->value - Chisq) / dLinear;
    508536
    509         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);
    510 
    511         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);
     537        psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %g, nDOF: %d\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda, nDOF);
     538
     539        psTrace("psLib.math.dLinear", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %g\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda);
    512540
    513541        // dump some useful info if trace is defined
     
    580608}
    581609
     610/******************************************************************************
     611psMinimizeLMChi2(): This routine takes a function-pointer (func) which calculates an arbitrary
     612function and it's derivatives and minimizes the chi-squared match between that function at the
     613specified points and the specified value at those points.
     614
     615The original version of this function used a convergence criterion based on the change in
     616chisq.  this has problems since it depends on the choice of points used to measure the fit.
     617(consider a gaussian on a background : it 100 pixels are used -- and some or most contribute to
     618the chisq -- and the delta-chisq is 10%, then the same change in model fit will yield a
     619delta-chisq of 1% if 1000 pixels are used (all but the 100 measuring the background)).
     620
     621This implementation uses changes to the parameters and stops if they are no longer significant.
     622
     623This requires F32 input data; all internal calls use F32.
     624  *****************************************************************************/
     625bool psMinimizeLMChi2_Alt(
     626    psMinimization *min,
     627    psImage *covar,
     628    psVector *params,
     629    psMinConstraint *constraint,
     630    const psArray *x,
     631    const psVector *y,
     632    const psVector *yWt,
     633    psMinimizeLMChi2Func func)
     634{
     635    psTrace("psLib.math", 3, "---- begin ----\n");
     636    PS_ASSERT_PTR_NON_NULL(min, false);
     637    PS_ASSERT_VECTOR_NON_NULL(params, false);
     638    PS_ASSERT_VECTOR_NON_EMPTY(params, false);
     639    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
     640    psVector *paramMask = NULL;
     641    if (constraint != NULL) {
     642        paramMask = constraint->paramMask;
     643        if (paramMask != NULL) {
     644            PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_VECTOR_MASK, false);
     645            PS_ASSERT_VECTORS_SIZE_EQUAL(params, paramMask, false);
     646        }
     647    }
     648    PS_ASSERT_PTR_NON_NULL(x, false);
     649    for (psS32 i = 0 ; i < x->n ; i++) {
     650        psVector *coord = (psVector *) (x->data[i]);
     651        PS_ASSERT_VECTOR_NON_NULL(coord, false);
     652        PS_ASSERT_VECTOR_TYPE(coord, PS_TYPE_F32, false);
     653    }
     654    PS_ASSERT_VECTOR_NON_NULL(y, false);
     655    PS_ASSERT_VECTOR_NON_EMPTY(y, false);
     656    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F32, false);
     657    PS_ASSERT_VECTORS_SIZE_EQUAL(x, y, false);
     658    if (yWt != NULL) {
     659        PS_ASSERT_VECTOR_TYPE(yWt, PS_TYPE_F32, false);
     660        PS_ASSERT_VECTORS_SIZE_EQUAL(y, yWt, false);
     661    }
     662    PS_ASSERT_PTR_NON_NULL(func, false);
     663
     664    psMinimizeLMLimitFunc checkLimits = NULL;
     665    if (constraint) {
     666        checkLimits = constraint->checkLimits;
     667    }
     668
     669    // this function has test and current values for several things
     670    // the current best value is in lower case
     671    // the next guess value is in upper case
     672
     673    // allocate internal arrays (current vs Guess)
     674    psImage *Alpha = NULL;
     675    psVector *Beta = NULL;
     676
     677    // Alpha & Beta only contain elements to represent the unmasked parameters
     678    if (!psMinLM_AllocAB (&Alpha, &Beta, params, paramMask)) {
     679        psAbort ("programming error: no unmasked parameters to be fit\n");
     680    }
     681
     682    int nFitParams = Beta->n;
     683    psImage *alpha   = psImageAlloc(nFitParams, nFitParams, PS_TYPE_F32);
     684    psVector *beta   = psVectorAlloc(nFitParams, PS_TYPE_F32);
     685    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
     686
     687    psVector *dy     = NULL;
     688    psF32 Chisq = 0.0;
     689    psF32 lambda = 0.001;
     690    psF32 dLinear = 0.0;
     691    psF32 nu = 2.0;
     692
     693    // the user provides the error or NULL.  we need to convert
     694    // to appropriate weights
     695    if (yWt != NULL) {
     696        dy = (psVector *) yWt;
     697    } else {
     698        dy = psVectorAlloc(y->n, PS_TYPE_F32);
     699        psVectorInit(dy, 1.0);
     700    }
     701
     702    // number of degrees of freedom for this fit
     703    int nDOF = dy->n - nFitParams;
     704
     705    // calculate initial alpha and beta, set chisq (min->value)
     706    min->value = psMinLM_SetABX(alpha, beta, params, paramMask, x, y, dy, func);
     707    if (isnan(min->value)) {
     708        min->iter = min->maxIter;
     709        psFree(alpha);
     710        psFree(Alpha);
     711        psFree(beta);
     712        psFree(Beta);
     713        psFree(Params);
     714        return(false);
     715    }
     716    // dump some useful info if trace is defined
     717    if (psTraceGetLevel("psLib.math") >= 6) {
     718        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
     719        p_psVectorPrint(psTraceGetDestination(), beta, "beta guess (0)");
     720    }
     721    if (psTraceGetLevel("psLib.math") >= 5) {
     722        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
     723    }
     724
     725    bool done = (min->iter >= min->maxIter);
     726    while (!done) {
     727        psTrace("psLib.math", 5, "Iteration number %d.  (max iterations is %d).\n", min->iter, min->maxIter);
     728
     729        if (min->chisqConvergence) {
     730          psTrace("psLib.math", 5, "Last delta is %f.  stop if < %f, accept if < %f\n", min->lastDelta, min->minTol, min->maxTol);
     731        } else {
     732          psTrace("psLib.math", 5, "Last delta is %f.  stop if < %f, accept if < %f\n", min->rParSigma, min->minTol*nFitParams, min->maxTol*nFitParams);
     733        }
     734
     735        // set a new guess for Alpha, Beta, Params
     736        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
     737            min->iter ++;
     738            if (min->iter >=  min->maxIter) break;
     739            lambda *= 10.0;
     740            // ALT? lambda *= 2.0;
     741            continue;
     742        }
     743
     744        // dump some useful info if trace is defined
     745        if (psTraceGetLevel("psLib.math") >= 6) {
     746            p_psImagePrint(psTraceGetDestination(), Alpha, "Alpha guess (1)");
     747            p_psVectorPrint(psTraceGetDestination(), Beta, "Beta guess (1)");
     748            p_psVectorPrint(psTraceGetDestination(), beta, "beta current (1)");
     749        }
     750        if (psTraceGetLevel("psLib.math") >= 5) {
     751            p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
     752        }
     753
     754        // calculate the parameter change (rParDelta) and error radius (rParSigma)
     755        //    rParDelta : radius of parameter change;
     756        //    rParSigma : radius of parameter error
     757       
     758        // note that (before SetABX) Alpha[i][i] is the covariance matrix and
     759        // Beta is the actual parameter change for this pass
     760
     761        // note that Alpha & Beta only represent unmasked parameters, while params and Params have all
     762
     763        // dParSigma = Alpha[i][i] : error (squared) on parameter i
     764        // dParDelta = Params->data.F32[i] - params->data.F32[i]     : change on parameter i
     765        float rParSigma = 0.0;
     766        for (int j = 0, J = 0; j < Params->n; j++) {
     767            if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) {
     768                continue;
     769            }
     770            rParSigma += PS_SQR(Params->data.F32[j] - params->data.F32[j]) / Alpha->data.F32[J][J];
     771            J++;
     772        }
     773        rParSigma = sqrt(rParSigma);
     774        psTrace("psLib.math", 5, "rParSigma: %f, Niter: %d\n", rParSigma, min->iter);
     775        // fprintf (stderr, "rParSigma: %f, Niter: %d\n", rParSigma, min->iter);
     776
     777        // calculate Chisq for new guess, update Alpha & Beta
     778        Chisq = psMinLM_SetABX(Alpha, Beta, Params, paramMask, x, y, dy, func);
     779        if (isnan(Chisq)) {
     780            min->iter ++;
     781            if (min->iter >= min->maxIter) break;
     782            lambda *= 10.0;
     783            // ALT lambda *= 2.0;
     784            continue;
     785        }
     786
     787        // convergence criterion:
     788        // compare the delta (min->value - Chisq) with the
     789        // expected delta from the linear model (dLinear)
     790        // accept new guess if it is an improvement (rho > 0), or else increase lambda
     791        psF32 rho = (min->value - Chisq) / dLinear;
     792
     793        psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %g, nDOF: %d\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda, nDOF);
     794
     795        psTrace("psLib.math.dLinear", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %g\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda);
     796
     797        // dump some useful info if trace is defined
     798        if (psTraceGetLevel("psLib.math") >= 6) {
     799            p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (2)");
     800            p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (2)");
     801        }
     802
     803        // change in chisq/nDOF since last minimum
     804        min->lastDelta = (min->value - Chisq) / nDOF;
     805
     806        // rho is positive if the new chisq is smaller; allow for some insignificant change (slight negative rho)
     807
     808        // XXX the old version of lambda changes:
     809        // XXX : Madsen gives suggestion for better use of rho
     810        // rho is positive if the new chisq is smaller
     811        if (rho >= -1e-6) {
     812            min->value = Chisq;
     813            alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
     814            beta   = psVectorCopy(beta, Beta, PS_TYPE_F32);
     815            params = psVectorCopy(params, Params, PS_TYPE_F32);
     816        }
     817        switch (min->gainFactorMode) {
     818          case 0:
     819            if (rho >= -1e-6) {
     820              lambda *= 0.25;
     821            } else {
     822              lambda *= 10.0;
     823            }
     824            break;
     825
     826          case 1:
     827            // adjust the gain ratio (lambda) based on rho
     828            if (rho < 0.25) {
     829              lambda *= 2.0;
     830            }
     831            if (rho > 0.75) {
     832              lambda *= 0.333;
     833            }
     834            break;
     835
     836          case 2:
     837            if (rho > 0.0) {
     838              lambda *= PS_MAX(0.33, (1.0 - pow(2.0*rho - 1.0, 3.0)));
     839              nu = 2.0;
     840            } else {
     841              lambda *= nu;
     842              nu *= 2.0;
     843            }
     844            break;
     845        }
     846        min->iter++;
     847
     848        // ending conditions:
     849        // 1) hard limit : too many iterations
     850        done = (min->iter >= min->maxIter);
     851       
     852        // 2) require deltaChi > 1e-6 (ie, chisq is decreasing, but accept an insignificant change)
     853        if (min->lastDelta < -1e-6) {
     854            continue;
     855        }
     856
     857        // save this value in case we stop iterating
     858        min->rParSigma = rParSigma;
     859
     860        // 2) require chisqDOF < maxChisqDOF (if maxChisqDOF is not NAN)
     861        // keep iterating regardless of rParSigma in this case
     862        float chisqDOF = Chisq / nDOF;
     863        if (isfinite(min->maxChisqDOF) && (chisqDOF > min->maxChisqDOF)) {
     864            continue;
     865        }
     866
     867        // delta-chisq or rParSigma ?
     868        if (min->chisqConvergence) {
     869          done |= (min->lastDelta < min->minTol);
     870        } else {
     871          done |= (rParSigma < min->minTol*nFitParams);
     872        }
     873    }
     874    psTrace("psLib.math", 5, "chisq: %f, last delta: %f, rParSigma: %f, Niter: %d\n", min->value, min->lastDelta, min->rParSigma, min->iter);
     875
     876    // construct & return the covariance matrix (if requested)
     877    if (covar != NULL) {
     878        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, NULL, 0.0, NULL)) {
     879            psTrace ("psLib.math", 5, "failure to calculate covariance matrix\n");
     880        }
     881        // set covar values which are not masked
     882        psImageInit (covar, 0.0);
     883        for (int j = 0, J = 0; j < params->n; j++) {
     884            if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) {
     885                covar->data.F32[j][j] = 1.0;
     886                continue;
     887            }
     888            for (int k = 0, K = 0; k < params->n; k++) {
     889                if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[k])) continue;
     890                covar->data.F32[j][k] = Alpha->data.F32[J][K];
     891                K++;
     892            }
     893            J++;
     894        }
     895    }
     896
     897    // free the internal temporary data
     898    psFree(alpha);
     899    psFree(Alpha);
     900    psFree(beta);
     901    psFree(Beta);
     902    psFree(Params);
     903    if (yWt == NULL) {
     904        psFree(dy);
     905    }
     906
     907    // if the last improvement was at least as good as maxTol, accept the fit:
     908    if (min->chisqConvergence) {
     909      if (min->lastDelta <= min->maxTol) {
     910        psTrace("psLib.math", 6, "---- end (true) ----\n");
     911        return(true);
     912      }
     913    } else {
     914      if (min->rParSigma <= min->maxTol*nFitParams) {
     915        psTrace("psLib.math", 6, "---- end (true) ----\n");
     916        return(true);
     917      }
     918    }
     919    psTrace("psLib.math", 6, "---- end (false) ----\n");
     920    return(false);
     921}
     922
    582923bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask) {
    583924
     
    625966    min->iter = 0;
    626967    min->lastDelta = NAN;
     968    min->rParSigma = NAN;
    627969    min->maxChisqDOF = NAN;
    628970
     971    // we default to the old algorithm for convergence
     972    min->chisqConvergence = true;
     973    min->gainFactorMode = 0;
     974   
    629975    return(min);
    630976}
Note: See TracChangeset for help on using the changeset viewer.