Changeset 3852 for trunk/psLib/src/math/psMinimize.c
- Timestamp:
- May 5, 2005, 12:10:12 PM (21 years ago)
- File:
-
- 1 edited
-
trunk/psLib/src/math/psMinimize.c (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psLib/src/math/psMinimize.c
r3578 r3852 9 9 * @author GLG, MHPCC 10 10 * 11 * @version $Revision: 1.11 0$ $Name: not supported by cvs2svn $12 * @date $Date: 2005-0 3-31 01:02:15$11 * @version $Revision: 1.111 $ $Name: not supported by cvs2svn $ 12 * @date $Date: 2005-05-05 22:10:12 $ 13 13 * 14 14 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii … … 557 557 } 558 558 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 *****************************************************************************/ 559 psF64 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 563 bool 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 579 bool 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 585 592 psBool psMinimizeLMChi2(psMinimization *min, 586 593 psImage *covar, … … 591 598 const psVector *yErr, 592 599 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 700 psF64 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? 751 psBool 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 814 bool 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 905 fescape: 906 psFree (ipiv); 907 psFree (indxr); 908 psFree (indxc); 909 return (false); 910 } 911 912 /****************************************************************************** 913 psMinimizeLMChi2(): This routine will take an procedure which calculates 914 an arbitrary function and it's derivative and minimize the chi-squared match 915 between that function at the specified coords and the specified value at 916 those coords. 917 918 XXX: 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 924 XXX: This is very different from what is specified in the SDR. Must 925 coordinate with IfA on new SDR. 926 927 XXX: Do vector/image recycles. 928 929 XXX: probably yErr will be part of the SDR. 930 931 XXX: 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 936 XXX: Must update the covar matrix. 937 *****************************************************************************/ 938 psBool 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) 593 946 { 594 947 PS_PTR_CHECK_NULL(min, NULL); … … 636 989 psF32 currChi2 = 0.0; 637 990 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) 640 993 641 994 psTrace(".psLib.dataManip.psMinimize", 6, … … 661 1014 // 662 1015 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 664 1023 for (n=0;n<numData;n++) { 665 1024 currChi2+= (currValueVec->data.F32[n] - y->data.F32[n]) * … … 670 1029 } 671 1030 1031 // XXX EAM: this is just for tracing 672 1032 for (p=0;p<numParams;p++) { 673 1033 psTrace(".psLib.dataManip.psMinimize", 6, … … 679 1039 // 680 1040 // Mask elements of the derivative for each data point. 681 // 1041 // XXX EAM : is this necessary? probably not... 682 1042 for (p=0;p<numParams;p++) { 683 1043 if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) { … … 690 1050 // 691 1051 // Calculate the BETA vector. 692 // 1052 // XXX EAM: I think this is wrong 693 1053 for (p=0;p<numParams;p++) { 1054 if ((paramMask != NULL) && (paramMask->data.U8[p] != 0)) { 1055 continue; 1056 } 694 1057 beta->data.F64[p] = 0.0; 695 1058 for (n=0;n<numData;n++) { … … 707 1070 // 708 1071 // Calculate the ALPHA matrix. 709 // 1072 // XXX EAM: also wrong? (missing yErr) 710 1073 for (k=0;k<numParams;k++) { 711 1074 for (l=0;l<numParams;l++) { … … 773 1136 // 774 1137 newChi2 = 0.0; 775 newValueVec = func(deriv, newParams, x);1138 // newValueVec = func(deriv, newParams, x); 776 1139 for (n=0;n<numData;n++) { 777 1140 newChi2+= (newValueVec->data.F32[n] - y->data.F32[n]) * … … 1098 1461 min->value = 0.0; 1099 1462 min->iter = 0; 1100 min->lastDelta = 0.0;1463 min->lastDelta = tol + 1; 1101 1464 1102 1465 return(min);
Note:
See TracChangeset
for help on using the changeset viewer.
