Changeset 35767 for trunk/psLib/src/math/psMinimizeLMM.c
- Timestamp:
- Jul 3, 2013, 2:30:22 PM (13 years ago)
- File:
-
- 1 edited
-
trunk/psLib/src/math/psMinimizeLMM.c (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psLib/src/math/psMinimizeLMM.c
r29542 r35767 325 325 chisq += PS_SQR(delta) * dy->data.F32[i]; 326 326 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 327 331 if (isnan(dy->data.F32[i])) goto escape; 328 332 if (isnan(delta)) goto escape; … … 360 364 } 361 365 366 /****************************************************************************** 367 psMinimizeLMChi2(): wrapper to call either _Old or _Alt 368 *****************************************************************************/ 369 bool 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 } 362 390 363 391 /****************************************************************************** … … 370 398 XXX Make an F64 version? 371 399 *****************************************************************************/ 372 bool psMinimizeLMChi2 (400 bool psMinimizeLMChi2_Old( 373 401 psMinimization *min, 374 402 psImage *covar, … … 507 535 psF32 rho = (min->value - Chisq) / dLinear; 508 536 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); 512 540 513 541 // dump some useful info if trace is defined … … 580 608 } 581 609 610 /****************************************************************************** 611 psMinimizeLMChi2(): This routine takes a function-pointer (func) which calculates an arbitrary 612 function and it's derivatives and minimizes the chi-squared match between that function at the 613 specified points and the specified value at those points. 614 615 The original version of this function used a convergence criterion based on the change in 616 chisq. 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 618 the chisq -- and the delta-chisq is 10%, then the same change in model fit will yield a 619 delta-chisq of 1% if 1000 pixels are used (all but the 100 measuring the background)). 620 621 This implementation uses changes to the parameters and stops if they are no longer significant. 622 623 This requires F32 input data; all internal calls use F32. 624 *****************************************************************************/ 625 bool 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 582 923 bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask) { 583 924 … … 625 966 min->iter = 0; 626 967 min->lastDelta = NAN; 968 min->rParSigma = NAN; 627 969 min->maxChisqDOF = NAN; 628 970 971 // we default to the old algorithm for convergence 972 min->chisqConvergence = true; 973 min->gainFactorMode = 0; 974 629 975 return(min); 630 976 }
Note:
See TracChangeset
for help on using the changeset viewer.
